mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	'Dontcare' extrusions now don't force a toolchange + code reorganization
This commit is contained in:
		
							parent
							
								
									4dae6cbf60
								
							
						
					
					
						commit
						f8388abe17
					
				
					 6 changed files with 259 additions and 180 deletions
				
			
		|  | @ -764,7 +764,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) | |||
|         } | ||||
|         // Extrude the layers.
 | ||||
|         for (auto &layer : layers_to_print) { | ||||
|             const ToolOrdering::LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first); | ||||
|             const LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first); | ||||
|             if (m_wipe_tower && layer_tools.has_wipe_tower) | ||||
|                 m_wipe_tower->next_layer(); | ||||
|             this->process_layer(file, print, layer.second, layer_tools, size_t(-1)); | ||||
|  | @ -1009,7 +1009,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, | ||||
|     const 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) | ||||
|  | @ -1239,18 +1239,20 @@ void GCode::process_layer( | |||
|                             continue; | ||||
| 
 | ||||
|                         // This extrusion is part of certain Region, which tells us which extruder should be used for it:
 | ||||
|                         int correct_extruder_id = get_extruder(fill, region); entity_type=="infills" ? std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) : | ||||
|                         int correct_extruder_id = get_extruder(*fill, region); entity_type=="infills" ? std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) : | ||||
|                                                                            std::max<int>(region.config.perimeter_extruder.value - 1, 0); | ||||
| 
 | ||||
|                         // Let's recover vector of extruder overrides:
 | ||||
|                         const ExtruderPerCopy* entity_overrides = const_cast<ToolOrdering::LayerTools&>(layer_tools).wiping_extrusions.get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->_shifted_copies.size()); | ||||
|                         const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions.get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->_shifted_copies.size()); | ||||
| 
 | ||||
|                         // Now we must add this extrusion into the by_extruder map, once for each extruder that will print it:
 | ||||
|                         for (unsigned int extruder : layer_tools.extruders) | ||||
|                         { | ||||
|                             // Init by_extruder item only if we actually use the extruder:
 | ||||
|                             if (std::find(entity_overrides->begin(), entity_overrides->end(), extruder) != entity_overrides->end() ||   // at least one copy is overridden to use this extruder
 | ||||
|                                 std::find(entity_overrides->begin(), entity_overrides->end(), -extruder-1) != entity_overrides->end())  // at least one copy would normally be printed with this extruder (see get_extruder_overrides function for explanation)
 | ||||
|                             if (std::find(entity_overrides->begin(), entity_overrides->end(), extruder) != entity_overrides->end() ||      // at least one copy is overridden to use this extruder
 | ||||
|                                 std::find(entity_overrides->begin(), entity_overrides->end(), -extruder-1) != entity_overrides->end() ||   // at least one copy would normally be printed with this extruder (see get_extruder_overrides function for explanation)
 | ||||
|                                 (std::find(layer_tools.extruders.begin(), layer_tools.extruders.end(), correct_extruder_id) == layer_tools.extruders.end() && extruder == layer_tools.extruders.back())) // this entity is not overridden, but its extruder is not in layer_tools - we'll print it
 | ||||
|                                                                                                                                             //by last extruder on this layer (could happen e.g. when a wiping object is taller than others - dontcare extruders are eradicated from layer_tools)
 | ||||
|                             { | ||||
|                                 std::vector<ObjectByExtruder::Island> &islands = object_islands_by_extruder( | ||||
|                                     by_extruder, | ||||
|  |  | |||
|  | @ -185,7 +185,7 @@ protected: | |||
|         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, | ||||
|         const 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)); | ||||
|  |  | |||
|  | @ -48,6 +48,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude | |||
| // (print.config.complete_objects is false).
 | ||||
| ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material) | ||||
| { | ||||
|     m_print_config_ptr = &print.config; | ||||
|     // Initialize the print layers for all objects and all layers.
 | ||||
|     coordf_t object_bottom_z = 0.; | ||||
|     { | ||||
|  | @ -77,9 +78,9 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| ToolOrdering::LayerTools&  ToolOrdering::tools_for_layer(coordf_t print_z) | ||||
| LayerTools& ToolOrdering::tools_for_layer(coordf_t print_z) | ||||
| { | ||||
|     auto it_layer_tools = std::lower_bound(m_layer_tools.begin(), m_layer_tools.end(), ToolOrdering::LayerTools(print_z - EPSILON)); | ||||
|     auto it_layer_tools = std::lower_bound(m_layer_tools.begin(), m_layer_tools.end(), LayerTools(print_z - EPSILON)); | ||||
|     assert(it_layer_tools != m_layer_tools.end()); | ||||
|     coordf_t dist_min = std::abs(it_layer_tools->print_z - print_z); | ||||
| 	for (++ it_layer_tools; it_layer_tools != m_layer_tools.end(); ++it_layer_tools) { | ||||
|  | @ -103,7 +104,7 @@ void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs) | |||
|         coordf_t zmax = zs[i] + EPSILON; | ||||
|         for (; j < zs.size() && zs[j] <= zmax; ++ j) ; | ||||
|         // Assign an average print_z to the set of layers with nearly equal print_z.
 | ||||
|         m_layer_tools.emplace_back(LayerTools(0.5 * (zs[i] + zs[j-1]))); | ||||
|         m_layer_tools.emplace_back(LayerTools(0.5 * (zs[i] + zs[j-1]), m_print_config_ptr)); | ||||
|         i = j; | ||||
|     } | ||||
| } | ||||
|  | @ -135,12 +136,25 @@ void ToolOrdering::collect_extruders(const PrintObject &object) | |||
|             if (layerm == nullptr) | ||||
|                 continue; | ||||
|             const PrintRegion ®ion = *object.print()->regions[region_id]; | ||||
| 
 | ||||
|             if (! layerm->perimeters.entities.empty()) { | ||||
|                 layer_tools.extruders.push_back(region.config.perimeter_extruder.value); | ||||
|                 bool something_nonoverriddable = false; | ||||
|                 for (const auto& eec : layerm->perimeters.entities) // let's check if there are nonoverriddable entities
 | ||||
|                     if (!layer_tools.wiping_extrusions.is_overriddable(dynamic_cast<const ExtrusionEntityCollection&>(*eec), *m_print_config_ptr, object, region)) { | ||||
|                         something_nonoverriddable = true; | ||||
|                         break; | ||||
|                     } | ||||
| 
 | ||||
|                 if (something_nonoverriddable) | ||||
|                     layer_tools.extruders.push_back(region.config.perimeter_extruder.value); | ||||
| 
 | ||||
|                 layer_tools.has_object = true; | ||||
|             } | ||||
| 
 | ||||
| 
 | ||||
|             bool has_infill       = false; | ||||
|             bool has_solid_infill = false; | ||||
|             bool something_nonoverriddable = false; | ||||
|             for (const ExtrusionEntity *ee : layerm->fills.entities) { | ||||
|                 // fill represents infill extrusions of a single island.
 | ||||
|                 const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee); | ||||
|  | @ -149,19 +163,32 @@ void ToolOrdering::collect_extruders(const PrintObject &object) | |||
|                     has_solid_infill = true; | ||||
|                 else if (role != erNone) | ||||
|                     has_infill = true; | ||||
| 
 | ||||
|                 if (!something_nonoverriddable && !layer_tools.wiping_extrusions.is_overriddable(*fill, *m_print_config_ptr, object, region)) | ||||
|                     something_nonoverriddable = true; | ||||
|             } | ||||
|             if (something_nonoverriddable) | ||||
|             { | ||||
|                 if (has_solid_infill) | ||||
|                     layer_tools.extruders.push_back(region.config.solid_infill_extruder); | ||||
|                 if (has_infill) | ||||
|                     layer_tools.extruders.push_back(region.config.infill_extruder); | ||||
|             } | ||||
|             if (has_solid_infill) | ||||
|                 layer_tools.extruders.push_back(region.config.solid_infill_extruder); | ||||
|             if (has_infill) | ||||
|                 layer_tools.extruders.push_back(region.config.infill_extruder); | ||||
|             if (has_solid_infill || has_infill) | ||||
|                 layer_tools.has_object = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Sort and remove duplicates
 | ||||
|     for (LayerTools < : m_layer_tools) | ||||
|         sort_remove_duplicates(lt.extruders); | ||||
|     // Sort and remove duplicates, make sure that there are some tools for each object layer (e.g. tall wiping object will result in empty extruders vector)
 | ||||
|     for (auto lt_it=m_layer_tools.begin(); lt_it != m_layer_tools.end(); ++lt_it) { | ||||
|         sort_remove_duplicates(lt_it->extruders); | ||||
| 
 | ||||
|         if (lt_it->extruders.empty() && lt_it->has_object) | ||||
|             if (lt_it != m_layer_tools.begin()) | ||||
|                 lt_it->extruders.push_back(std::prev(lt_it)->extruders.back()); | ||||
|             else | ||||
|                 lt_it->extruders.push_back(1); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Reorder extruders to minimize layer changes.
 | ||||
|  | @ -348,6 +375,151 @@ void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsi | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Finds last non-soluble extruder on the layer
 | ||||
| bool WipingExtrusions::is_last_nonsoluble_on_layer(const PrintConfig& print_config, const LayerTools& lt, unsigned int extruder) const { | ||||
|     for (auto extruders_it = lt.extruders.rbegin(); extruders_it != lt.extruders.rend(); ++extruders_it) | ||||
|         if (!print_config.filament_soluble.get_at(*extruders_it)) | ||||
|             return (*extruders_it == extruder); | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Decides whether this entity could be overridden
 | ||||
| bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const { | ||||
|     if ((!is_infill(eec.role()) && !object.config.wipe_into_objects) || | ||||
|         ((eec.role() == erTopSolidInfill || eec.role() == erGapFill) && !object.config.wipe_into_objects) || | ||||
|         (is_infill(eec.role()) && !region.config.wipe_into_infill) || | ||||
|         (print_config.filament_soluble.get_at(get_extruder(eec, region))) ) | ||||
|             return false; | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Following function iterates through all extrusions on the layer, remembers those that could be used for wiping after toolchange
 | ||||
| // and returns volume that is left to be wiped on the wipe tower.
 | ||||
| float WipingExtrusions::mark_wiping_extrusions(const Print& print, const LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe) | ||||
| { | ||||
|     const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
 | ||||
| 
 | ||||
|     if (print.config.filament_soluble.get_at(new_extruder)) | ||||
|         return volume_to_wipe;      // Soluble filament cannot be wiped in a random infill
 | ||||
| 
 | ||||
|     bool last_nonsoluble = is_last_nonsoluble_on_layer(print.config, layer_tools, new_extruder); | ||||
| 
 | ||||
|     // we will sort objects so that dedicated for wiping are at the beginning:
 | ||||
|     PrintObjectPtrs object_list = print.objects; | ||||
|     std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config.wipe_into_objects; }); | ||||
| 
 | ||||
| 
 | ||||
|     // We will now iterate through
 | ||||
|     //  - first the dedicated objects to mark perimeters or infills (depending on infill_first)
 | ||||
|     //  - second through the dedicated ones again to mark infills or perimeters (depending on infill_first)
 | ||||
|     //  - then all the others to mark infills (in case that !infill_first, we must also check that the perimeter is finished already
 | ||||
|     // this is controlled by the following variable:
 | ||||
|     bool perimeters_done = false; | ||||
| 
 | ||||
|     for (int i=0 ; i<(int)object_list.size() ; ++i) { | ||||
|         const auto& object = object_list[i]; | ||||
| 
 | ||||
|         if (!perimeters_done && (i+1==(int)object_list.size() || !object_list[i]->config.wipe_into_objects)) { // we passed the last dedicated object in list
 | ||||
|             perimeters_done = true; | ||||
|             i=-1;   // let's go from the start again
 | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         // Finds this layer:
 | ||||
|         auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [&layer_tools](const Layer* lay) { return std::abs(layer_tools.print_z - lay->print_z)<EPSILON; }); | ||||
|         if (this_layer_it == object->layers.end()) | ||||
|             continue; | ||||
|         const Layer* this_layer = *this_layer_it; | ||||
|         unsigned int num_of_copies = object->_shifted_copies.size(); | ||||
| 
 | ||||
|         for (unsigned int copy = 0; copy < num_of_copies; ++copy) {    // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
 | ||||
| 
 | ||||
|             for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) { | ||||
|                 const auto& region = *object->print()->regions[region_id]; | ||||
| 
 | ||||
|                 if (!region.config.wipe_into_infill && !object->config.wipe_into_objects) | ||||
|                     continue; | ||||
| 
 | ||||
| 
 | ||||
|                 if (((!print.config.infill_first ? perimeters_done : !perimeters_done) || !object->config.wipe_into_objects) && region.config.wipe_into_infill) { | ||||
|                     const ExtrusionEntityCollection& eec = this_layer->regions[region_id]->fills; | ||||
|                     for (const ExtrusionEntity* ee : eec.entities) {                      // iterate through all infill Collections
 | ||||
|                         auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee); | ||||
| 
 | ||||
|                         if (fill->role() == erTopSolidInfill || fill->role() == erGapFill)  // these cannot be changed - such infill is / may be visible
 | ||||
|                             continue; | ||||
| 
 | ||||
|                         // What extruder would this normally be printed with?
 | ||||
|                         unsigned int correct_extruder = get_extruder(*fill, region); | ||||
| 
 | ||||
|                         bool force_override = false; | ||||
|                         // If the extruder is not in layer tools - we MUST override it. This happens whenever all extrusions, that would normally
 | ||||
|                         // be printed with this extruder on this layer are "dont care" (part of infill/perimeter wiping):
 | ||||
|                         if (last_nonsoluble && std::find(layer_tools.extruders.begin(), layer_tools.extruders.end(), correct_extruder) == layer_tools.extruders.end()) | ||||
|                             force_override = true; | ||||
|                         if (!force_override && volume_to_wipe<=0) | ||||
|                             continue; | ||||
| 
 | ||||
|                         if (!is_overriddable(*fill, print.config, *object, region)) | ||||
|                             continue; | ||||
| 
 | ||||
|                         if (!object->config.wipe_into_objects && !print.config.infill_first && !force_override) { | ||||
|                             // In this case we must check that the original extruder is used on this layer before the one we are overridding
 | ||||
|                             // (and the perimeters will be finished before the infill is printed):
 | ||||
|                             if ((!print.config.infill_first && region.config.wipe_into_infill)) { | ||||
|                                 bool unused_yet = false; | ||||
|                                 for (unsigned i = 0; i < layer_tools.extruders.size(); ++i) { | ||||
|                                     if (layer_tools.extruders[i] == new_extruder) | ||||
|                                         unused_yet = true; | ||||
|                                     if (layer_tools.extruders[i] == correct_extruder) | ||||
|                                         break; | ||||
|                                 } | ||||
|                                 if (unused_yet) | ||||
|                                     continue; | ||||
|                             } | ||||
|                         } | ||||
| 
 | ||||
|                         if (force_override || (!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) {     // this infill will be used to wipe this extruder
 | ||||
|                             set_extruder_override(fill, copy, new_extruder, num_of_copies); | ||||
|                             volume_to_wipe -= fill->total_volume(); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
| 
 | ||||
|                 if (object->config.wipe_into_objects && (print.config.infill_first ? perimeters_done : !perimeters_done)) | ||||
|                 { | ||||
|                     const ExtrusionEntityCollection& eec = this_layer->regions[region_id]->perimeters; | ||||
|                     for (const ExtrusionEntity* ee : eec.entities) {                      // iterate through all perimeter Collections
 | ||||
|                         auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee); | ||||
|                         // What extruder would this normally be printed with?
 | ||||
|                         unsigned int correct_extruder = get_extruder(*fill, region); | ||||
|                         bool force_override = false; | ||||
|                         if (last_nonsoluble && std::find(layer_tools.extruders.begin(), layer_tools.extruders.end(), correct_extruder) == layer_tools.extruders.end()) | ||||
|                             force_override = true; | ||||
|                         if (!force_override && volume_to_wipe<=0) | ||||
|                             continue; | ||||
| 
 | ||||
|                         if (!is_overriddable(*fill, print.config, *object, region)) | ||||
|                             continue; | ||||
| 
 | ||||
|                         if (force_override || (!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { | ||||
|                             set_extruder_override(fill, copy, new_extruder, num_of_copies); | ||||
|                             volume_to_wipe -= fill->total_volume(); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return std::max(0.f, volume_to_wipe); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| // Following function is called from process_layer and returns pointer to vector with information about which extruders should be used for given copy of this entity.
 | ||||
| // It first makes sure the pointer is valid (creates the vector if it does not exist) and contains a record for each copy
 | ||||
|  |  | |||
|  | @ -9,6 +9,8 @@ namespace Slic3r { | |||
| 
 | ||||
| class Print; | ||||
| class PrintObject; | ||||
| class LayerTools; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| // Object of this class holds information about whether an extrusion is printed immediately
 | ||||
|  | @ -16,65 +18,74 @@ class PrintObject; | |||
| // of several copies - this has to be taken into account.
 | ||||
| class WipingExtrusions | ||||
| { | ||||
|     public: | ||||
| public: | ||||
|     bool is_anything_overridden() const {   // if there are no overrides, all the agenda can be skipped - this function can tell us if that's the case
 | ||||
|         return something_overridden; | ||||
|     } | ||||
| 
 | ||||
|     // This is called from GCode::process_layer - see implementation for further comments:
 | ||||
|     const std::vector<int>* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies); | ||||
| 
 | ||||
|     // This function goes through all infill entities, decides which ones will be used for wiping and
 | ||||
|     // marks them by the extruder id. Returns volume that remains to be wiped on the wipe tower:
 | ||||
|     float mark_wiping_extrusions(const Print& print, const LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe); | ||||
| 
 | ||||
|     bool is_overriddable(const ExtrusionEntityCollection& ee, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const; | ||||
| 
 | ||||
| private: | ||||
|     bool is_last_nonsoluble_on_layer(const PrintConfig& print_config, const LayerTools& lt, unsigned int extruder) const; | ||||
| 
 | ||||
|     // This function is called from mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual)
 | ||||
|     void set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies); | ||||
| 
 | ||||
|     // Returns true in case that entity is not printed with its usual extruder for a given copy:
 | ||||
|     bool is_entity_overridden(const ExtrusionEntity* entity, int copy_id) const { | ||||
|         return (entity_map.find(entity) == entity_map.end() ? false : entity_map.at(entity).at(copy_id) != -1); | ||||
|     } | ||||
| 
 | ||||
|     // This function is called from Print::mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual)
 | ||||
|     void set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies); | ||||
| 
 | ||||
|     // This is called from GCode::process_layer - see implementation for further comments:
 | ||||
|     const std::vector<int>* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies); | ||||
| 
 | ||||
| private: | ||||
|     std::map<const ExtrusionEntity*, std::vector<int>> entity_map;  // to keep track of who prints what
 | ||||
|     bool something_overridden = false; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class ToolOrdering  | ||||
| class LayerTools | ||||
| { | ||||
| public: | ||||
| 	struct LayerTools | ||||
| 	{ | ||||
| 	    LayerTools(const coordf_t z) : | ||||
| 	    	print_z(z),  | ||||
| 	    	has_object(false), | ||||
| 			has_support(false), | ||||
| 			has_wipe_tower(false), | ||||
| 	    	wipe_tower_partitions(0), | ||||
| 	    	wipe_tower_layer_height(0.) {} | ||||
|     LayerTools(const coordf_t z, const PrintConfig* print_config_ptr = nullptr) : | ||||
|         print_z(z), | ||||
|         has_object(false), | ||||
|         has_support(false), | ||||
|         has_wipe_tower(false), | ||||
|         wipe_tower_partitions(0), | ||||
|         wipe_tower_layer_height(0.) {} | ||||
| 
 | ||||
| 	    bool operator< (const LayerTools &rhs) const { return print_z <  rhs.print_z; } | ||||
| 	    bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; } | ||||
|     bool operator< (const LayerTools &rhs) const { return print_z - EPSILON <  rhs.print_z; } | ||||
|     bool operator==(const LayerTools &rhs) const { return std::abs(print_z - rhs.print_z) < EPSILON; } | ||||
| 
 | ||||
| 		coordf_t 					print_z; | ||||
| 		bool 						has_object; | ||||
| 		bool						has_support; | ||||
| 		// Zero based extruder IDs, ordered to minimize tool switches.
 | ||||
| 		std::vector<unsigned int> 	extruders; | ||||
| 		// Will there be anything extruded on this layer for the wipe tower?
 | ||||
| 		// Due to the support layers possibly interleaving the object layers,
 | ||||
| 		// wipe tower will be disabled for some support only layers.
 | ||||
| 		bool 						has_wipe_tower; | ||||
| 		// Number of wipe tower partitions to support the required number of tool switches
 | ||||
| 		// and to support the wipe tower partitions above this one.
 | ||||
| 	    size_t                      wipe_tower_partitions; | ||||
| 	    coordf_t 					wipe_tower_layer_height; | ||||
|     coordf_t 					print_z; | ||||
|     bool 						has_object; | ||||
|     bool						has_support; | ||||
|     // Zero based extruder IDs, ordered to minimize tool switches.
 | ||||
|     std::vector<unsigned int> 	extruders; | ||||
|     // Will there be anything extruded on this layer for the wipe tower?
 | ||||
|     // Due to the support layers possibly interleaving the object layers,
 | ||||
|     // wipe tower will be disabled for some support only layers.
 | ||||
|     bool 						has_wipe_tower; | ||||
|     // Number of wipe tower partitions to support the required number of tool switches
 | ||||
|     // and to support the wipe tower partitions above this one.
 | ||||
|     size_t                      wipe_tower_partitions; | ||||
|     coordf_t 					wipe_tower_layer_height; | ||||
| 
 | ||||
|     // This object holds list of extrusion that will be used for extruder wiping
 | ||||
|     WipingExtrusions wiping_extrusions; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|         // This holds list of extrusion that will be used for extruder wiping
 | ||||
|         WipingExtrusions wiping_extrusions; | ||||
| 
 | ||||
| 	}; | ||||
| 
 | ||||
| class ToolOrdering | ||||
| { | ||||
| public: | ||||
| 	ToolOrdering() {} | ||||
| 
 | ||||
| 	// For the use case when each object is printed separately
 | ||||
|  | @ -114,17 +125,22 @@ private: | |||
| 	void 				collect_extruders(const PrintObject &object); | ||||
| 	void				reorder_extruders(unsigned int last_extruder_id); | ||||
| 	void 				fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z); | ||||
| 	void 				collect_extruder_statistics(bool prime_multi_material); | ||||
|     void 				collect_extruder_statistics(bool prime_multi_material); | ||||
| 
 | ||||
| 	std::vector<LayerTools> 	m_layer_tools; | ||||
| 	// First printing extruder, including the multi-material priming sequence.
 | ||||
| 	unsigned int 				m_first_printing_extruder = (unsigned int)-1; | ||||
| 	// Final printing extruder.
 | ||||
| 	unsigned int 				m_last_printing_extruder  = (unsigned int)-1; | ||||
| 	// All extruders, which extrude some material over m_layer_tools.
 | ||||
|     std::vector<unsigned int> 	m_all_printing_extruders; | ||||
|     std::vector<LayerTools>    m_layer_tools; | ||||
|     // First printing extruder, including the multi-material priming sequence.
 | ||||
|     unsigned int               m_first_printing_extruder = (unsigned int)-1; | ||||
|     // Final printing extruder.
 | ||||
|     unsigned int               m_last_printing_extruder  = (unsigned int)-1; | ||||
|     // All extruders, which extrude some material over m_layer_tools.
 | ||||
|     std::vector<unsigned int>  m_all_printing_extruders; | ||||
| 
 | ||||
| 
 | ||||
|     const PrintConfig*         m_print_config_ptr = nullptr; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| } // namespace SLic3r
 | ||||
| 
 | ||||
| #endif /* slic3r_ToolOrdering_hpp_ */ | ||||
|  |  | |||
|  | @ -1064,7 +1064,7 @@ void Print::_make_wipe_tower() | |||
|         size_t idx_end   = m_tool_ordering.layer_tools().size(); | ||||
|         // Find the first wipe tower layer, which does not have a counterpart in an object or a support layer.
 | ||||
|         for (size_t i = 0; i < idx_end; ++ i) { | ||||
|             const ToolOrdering::LayerTools < = m_tool_ordering.layer_tools()[i]; | ||||
|             const LayerTools < = m_tool_ordering.layer_tools()[i]; | ||||
|             if (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support) { | ||||
|                 idx_begin = i; | ||||
|                 break; | ||||
|  | @ -1078,7 +1078,7 @@ void Print::_make_wipe_tower() | |||
|             for (; it_layer != it_end && (*it_layer)->print_z - EPSILON < wipe_tower_new_layer_print_z_first; ++ it_layer); | ||||
|             // Find the stopper of the sequence of wipe tower layers, which do not have a counterpart in an object or a support layer.
 | ||||
|             for (size_t i = idx_begin; i < idx_end; ++ i) { | ||||
|                 ToolOrdering::LayerTools < = const_cast<ToolOrdering::LayerTools&>(m_tool_ordering.layer_tools()[i]); | ||||
|                 LayerTools < = const_cast<LayerTools&>(m_tool_ordering.layer_tools()[i]); | ||||
|                 if (! (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support)) | ||||
|                     break; | ||||
|                 lt.has_support = true; | ||||
|  | @ -1137,7 +1137,7 @@ void Print::_make_wipe_tower() | |||
|                     float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id];    // total volume to wipe after this toolchange
 | ||||
| 
 | ||||
|                     if (!first_layer)   // unless we're on the first layer, try to assign some infills/objects for the wiping:
 | ||||
|                         volume_to_wipe = mark_wiping_extrusions(layer_tools, extruder_id, wipe_volumes[current_extruder_id][extruder_id]); | ||||
|                         volume_to_wipe = layer_tools.wiping_extrusions.mark_wiping_extrusions(*this, layer_tools, extruder_id, wipe_volumes[current_extruder_id][extruder_id]); | ||||
| 
 | ||||
|                     wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, first_layer && extruder_id == m_tool_ordering.all_extruders().back(), volume_to_wipe); | ||||
|                     current_extruder_id = extruder_id; | ||||
|  | @ -1173,114 +1173,7 @@ void Print::_make_wipe_tower() | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Following function iterates through all extrusions on the layer, remembers those that could be used for wiping after toolchange
 | ||||
| // and returns volume that is left to be wiped on the wipe tower.
 | ||||
| float Print::mark_wiping_extrusions(ToolOrdering::LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe) | ||||
| { | ||||
|     const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
 | ||||
| 
 | ||||
|     if (config.filament_soluble.get_at(new_extruder)) | ||||
|         return volume_to_wipe;      // Soluble filament cannot be wiped in a random infill
 | ||||
| 
 | ||||
|     PrintObjectPtrs object_list = objects; | ||||
| 
 | ||||
|     // sort objects so that dedicated for wiping are at the beginning:
 | ||||
|     std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config.wipe_into_objects; });  | ||||
| 
 | ||||
| 
 | ||||
|     // We will now iterate through objects
 | ||||
|     //  - first through the dedicated ones to mark perimeters or infills (depending on infill_first)
 | ||||
|     //  - second through the dedicated ones again to mark infills or perimeters (depending on infill_first)
 | ||||
|     //  - then for the others to mark infills
 | ||||
|     // this is controlled by the following variable:
 | ||||
|     bool perimeters_done = false; | ||||
| 
 | ||||
|     for (int i=0 ; i<(int)object_list.size() ; ++i) {                              // Let's iterate through all objects...
 | ||||
|         const auto& object = object_list[i]; | ||||
| 
 | ||||
|         if (!perimeters_done && (i+1==object_list.size() || !object_list[i]->config.wipe_into_objects)) { // we passed the last dedicated object in list
 | ||||
|             perimeters_done = true; | ||||
|             i=-1;   // let's go from the start again
 | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         // Finds this layer:
 | ||||
|         auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [&layer_tools](const Layer* lay) { return std::abs(layer_tools.print_z - lay->print_z)<EPSILON; }); | ||||
|         if (this_layer_it == object->layers.end()) | ||||
|             continue; | ||||
|         const Layer* this_layer = *this_layer_it; | ||||
|         unsigned int num_of_copies = object->_shifted_copies.size(); | ||||
| 
 | ||||
|         for (unsigned int copy = 0; copy < num_of_copies; ++copy) {    // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
 | ||||
| 
 | ||||
|             for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) { | ||||
|                 const auto& region = *object->print()->regions[region_id]; | ||||
| 
 | ||||
|                 if (!region.config.wipe_into_infill && !object->config.wipe_into_objects) | ||||
|                     continue; | ||||
| 
 | ||||
| 
 | ||||
|                 if (((!config.infill_first ? perimeters_done : !perimeters_done) || !object->config.wipe_into_objects) && region.config.wipe_into_infill) { | ||||
|                     ExtrusionEntityCollection& eec = this_layer->regions[region_id]->fills; | ||||
|                     for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all infill Collections
 | ||||
|                         if (volume_to_wipe <= 0.f) | ||||
|                                 return 0.f; | ||||
|                         auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee); | ||||
|                         if (fill->role() == erTopSolidInfill || fill->role() == erGapFill)  // these cannot be changed - such infill is / may be visible
 | ||||
|                             continue; | ||||
| 
 | ||||
|                         // What extruder would this normally be printed with?
 | ||||
|                         unsigned int correct_extruder = get_extruder(fill, region); | ||||
|                         if (config.filament_soluble.get_at(correct_extruder)) // if this entity is meant to be soluble, keep it that way
 | ||||
|                             continue; | ||||
| 
 | ||||
|                         if (!object->config.wipe_into_objects && !config.infill_first)  { | ||||
|                             // In this case we must check that the original extruder is used on this layer before the one we are overridding
 | ||||
|                             // (and the perimeters will be finished before the infill is printed):
 | ||||
|                             if (!config.infill_first && region.config.wipe_into_infill) { | ||||
|                                 bool unused_yet = false; | ||||
|                                 for (unsigned i = 0; i < layer_tools.extruders.size(); ++i) { | ||||
|                                     if (layer_tools.extruders[i] == new_extruder) | ||||
|                                         unused_yet = true; | ||||
|                                     if (layer_tools.extruders[i] == correct_extruder) | ||||
|                                         break; | ||||
|                                 } | ||||
|                                 if (unused_yet) | ||||
|                                     continue; | ||||
|                             } | ||||
|                         } | ||||
| 
 | ||||
|                         if (!layer_tools.wiping_extrusions.is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) {     // this infill will be used to wipe this extruder
 | ||||
|                             layer_tools.wiping_extrusions.set_extruder_override(fill, copy, new_extruder, num_of_copies); | ||||
|                             volume_to_wipe -= fill->total_volume(); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
| 
 | ||||
|                 if (object->config.wipe_into_objects && (config.infill_first ? perimeters_done : !perimeters_done)) | ||||
|                 { | ||||
|                     ExtrusionEntityCollection& eec = this_layer->regions[region_id]->perimeters; | ||||
|                     for (ExtrusionEntity* ee : eec.entities) {                      // iterate through all perimeter Collections
 | ||||
|                         if (volume_to_wipe <= 0.f) | ||||
|                             return 0.f; | ||||
|                         auto* fill = dynamic_cast<ExtrusionEntityCollection*>(ee); | ||||
|                         // What extruder would this normally be printed with?
 | ||||
|                         unsigned int correct_extruder = get_extruder(fill, region); | ||||
|                         if (config.filament_soluble.get_at(correct_extruder)) // if this entity is meant to be soluble, keep it that way
 | ||||
|                             continue; | ||||
| 
 | ||||
|                         if (!layer_tools.wiping_extrusions.is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) { | ||||
|                             layer_tools.wiping_extrusions.set_extruder_override(fill, copy, new_extruder, num_of_copies); | ||||
|                             volume_to_wipe -= fill->total_volume(); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return std::max(0.f, volume_to_wipe); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| std::string Print::output_filename() | ||||
|  |  | |||
|  | @ -318,19 +318,15 @@ private: | |||
|     bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys); | ||||
|     PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume); | ||||
| 
 | ||||
|     // This function goes through all infill entities, decides which ones will be used for wiping and
 | ||||
|     // marks them by the extruder id. Returns volume that remains to be wiped on the wipe tower:
 | ||||
|     float mark_wiping_extrusions(ToolOrdering::LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe); | ||||
| 
 | ||||
|     // Has the calculation been canceled?
 | ||||
|     tbb::atomic<bool>   m_canceled; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| // Returns extruder this eec should be printed with, according to PrintRegion config
 | ||||
| static int get_extruder(const ExtrusionEntityCollection* fill, const PrintRegion ®ion) { | ||||
|     return is_infill(fill->role()) ? std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) : | ||||
|                                      std::max<int>(region.config.perimeter_extruder.value - 1, 0); | ||||
| static int get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion ®ion) { | ||||
|     return is_infill(fill.role()) ? std::max<int>(0, (is_solid_infill(fill.entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) : | ||||
|                                     std::max<int>(region.config.perimeter_extruder.value - 1, 0); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lukas Matena
						Lukas Matena