mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 12:41:20 -06:00 
			
		
		
		
	
		
			
				
	
	
		
			639 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			639 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "Print.hpp"
 | |
| #include "ToolOrdering.hpp"
 | |
| 
 | |
| // #define SLIC3R_DEBUG
 | |
| 
 | |
| // Make assert active if SLIC3R_DEBUG
 | |
| #ifdef SLIC3R_DEBUG
 | |
|     #define DEBUG
 | |
|     #define _DEBUG
 | |
|     #undef NDEBUG
 | |
| #endif
 | |
| 
 | |
| #include <cassert>
 | |
| #include <limits>
 | |
| 
 | |
| namespace Slic3r {
 | |
| 
 | |
| 
 | |
| // Returns true in case that extruder a comes before b (b does not have to be present). False otherwise.
 | |
| bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const
 | |
| {
 | |
|     if (a==b)
 | |
|         return false;
 | |
| 
 | |
|     for (auto extruder : extruders) {
 | |
|         if (extruder == a)
 | |
|             return true;
 | |
|         if (extruder == b)
 | |
|             return false;
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| // For the use case when each object is printed separately
 | |
| // (print.config().complete_objects is true).
 | |
| ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material)
 | |
| {
 | |
|     if (object.layers().empty())
 | |
|         return;
 | |
| 
 | |
|     // Initialize the print layers for just a single object.
 | |
|     {
 | |
|         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);
 | |
|         this->initialize_layers(zs);
 | |
|     }
 | |
| 
 | |
|     // Collect extruders reuqired to print the layers.
 | |
|     this->collect_extruders(object);
 | |
| 
 | |
|     // Reorder the extruders to minimize tool switches.
 | |
|     this->reorder_extruders(first_extruder);
 | |
| 
 | |
|     this->fill_wipe_tower_partitions(object.print()->config(), object.layers().front()->print_z - object.layers().front()->height);
 | |
| 
 | |
|     this->collect_extruder_statistics(prime_multi_material);
 | |
| }
 | |
| 
 | |
| // For the use case when all objects are printed at once.
 | |
| // (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.;
 | |
|     {
 | |
|         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);
 | |
| 
 | |
|             // Find first object layer that is not empty and save its print_z
 | |
|             for (const Layer* layer : object->layers())
 | |
|                 if (layer->has_extrusions()) {
 | |
|                     object_bottom_z = layer->print_z - layer->height;
 | |
|                     break;
 | |
|                 }
 | |
|         }
 | |
|         this->initialize_layers(zs);
 | |
|     }
 | |
| 
 | |
|     // Collect extruders reuqired to print the layers.
 | |
|     for (auto object : print.objects())
 | |
|         this->collect_extruders(*object);
 | |
| 
 | |
|     // Reorder the extruders to minimize tool switches.
 | |
|     this->reorder_extruders(first_extruder);
 | |
| 
 | |
|     this->fill_wipe_tower_partitions(print.config(), object_bottom_z);
 | |
| 
 | |
|     this->collect_extruder_statistics(prime_multi_material);
 | |
| }
 | |
| 
 | |
| void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs)
 | |
| {
 | |
|     sort_remove_duplicates(zs);
 | |
|     // Merge numerically very close Z values.
 | |
|     for (size_t i = 0; i < zs.size();) {
 | |
|         // Find the last layer with roughly the same print_z.
 | |
|         size_t j = i + 1;
 | |
|         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_print_config_ptr));
 | |
|         i = j;
 | |
|     }
 | |
| }
 | |
| 
 | |
| // Collect extruders reuqired to print layers.
 | |
| void ToolOrdering::collect_extruders(const PrintObject &object)
 | |
| {
 | |
|     // Collect the support extruders.
 | |
|     for (auto support_layer : object.support_layers()) {
 | |
|         LayerTools   &layer_tools = this->tools_for_layer(support_layer->print_z);
 | |
|         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)
 | |
|             layer_tools.extruders.push_back(extruder_support);
 | |
|         if (has_interface)
 | |
|             layer_tools.extruders.push_back(extruder_interface);
 | |
|         if (has_support || has_interface)
 | |
|             layer_tools.has_support = true;
 | |
|     }
 | |
|     // Collect the object extruders.
 | |
|     for (auto layer : object.layers()) {
 | |
|         LayerTools &layer_tools = this->tools_for_layer(layer->print_z);
 | |
|         // What extruders are required to print this object layer?
 | |
|         for (size_t region_id = 0; region_id < object.region_volumes.size(); ++ region_id) {
 | |
|             const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr;
 | |
|             if (layerm == nullptr)
 | |
|                 continue;
 | |
|             const PrintRegion ®ion = *object.print()->regions()[region_id];
 | |
| 
 | |
|             if (! layerm->perimeters.entities.empty()) {
 | |
|                 bool something_nonoverriddable = true;
 | |
| 
 | |
|                 if (m_print_config_ptr) { // in this case complete_objects is false (see ToolOrdering constructors)
 | |
|                     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);
 | |
|                 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 (m_print_config_ptr) {
 | |
|                     if (!something_nonoverriddable && !layer_tools.wiping_extrusions().is_overriddable(*fill, *m_print_config_ptr, object, region))
 | |
|                         something_nonoverriddable = true;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if (something_nonoverriddable || !m_print_config_ptr)
 | |
|             {
 | |
|                 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;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     for (auto& layer : m_layer_tools) {
 | |
|         // Sort and remove duplicates
 | |
|         sort_remove_duplicates(layer.extruders);
 | |
| 
 | |
|         // make sure that there are some tools for each object layer (e.g. tall wiping object will result in empty extruders vector)
 | |
|         if (layer.extruders.empty() && layer.has_object)
 | |
|             layer.extruders.push_back(0); // 0="dontcare" extruder - it will be taken care of in reorder_extruders
 | |
|     }
 | |
| }
 | |
| 
 | |
| // Reorder extruders to minimize layer changes.
 | |
| void ToolOrdering::reorder_extruders(unsigned int last_extruder_id)
 | |
| {
 | |
|     if (m_layer_tools.empty())
 | |
|         return;
 | |
| 
 | |
|     if (last_extruder_id == (unsigned int)-1) {
 | |
|         // The initial print extruder has not been decided yet.
 | |
|         // Initialize the last_extruder_id with the first non-zero extruder id used for the print.
 | |
|         last_extruder_id = 0;
 | |
|         for (size_t i = 0; i < m_layer_tools.size() && last_extruder_id == 0; ++ i) {
 | |
|             const LayerTools < = m_layer_tools[i];
 | |
|             for (unsigned int extruder_id : lt.extruders)
 | |
|                 if (extruder_id > 0) {
 | |
|                     last_extruder_id = extruder_id;
 | |
|                     break;
 | |
|                 }
 | |
|         }
 | |
|         if (last_extruder_id == 0)
 | |
|             // Nothing to extrude.
 | |
|             return;
 | |
|     } else
 | |
|         // 1 based index
 | |
|         ++ last_extruder_id;
 | |
| 
 | |
|     for (LayerTools < : m_layer_tools) {
 | |
|         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 < : m_layer_tools)
 | |
|         for (unsigned int &extruder_id : lt.extruders) {
 | |
|             assert(extruder_id > 0);
 | |
|             -- extruder_id;
 | |
|         }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z)
 | |
| {
 | |
|     if (m_layer_tools.empty())
 | |
|         return;
 | |
| 
 | |
|     // Count the minimum number of tool changes per layer.
 | |
|     size_t last_extruder = size_t(-1);
 | |
|     for (LayerTools < : m_layer_tools) {
 | |
|         lt.wipe_tower_partitions = lt.extruders.size();
 | |
|         if (! lt.extruders.empty()) {
 | |
|             if (last_extruder == size_t(-1) || last_extruder == lt.extruders.front())
 | |
|                 // The first extruder on this layer is equal to the current one, no need to do an initial tool change.
 | |
|                 -- lt.wipe_tower_partitions;
 | |
|             last_extruder = lt.extruders.back();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Propagate the wipe tower partitions down to support the upper partitions by the lower partitions.
 | |
|     for (int i = int(m_layer_tools.size()) - 2; i >= 0; -- i)
 | |
|         m_layer_tools[i].wipe_tower_partitions = std::max(m_layer_tools[i + 1].wipe_tower_partitions, m_layer_tools[i].wipe_tower_partitions);
 | |
| 
 | |
|     //FIXME this is a hack to get the ball rolling.
 | |
|     for (LayerTools < : m_layer_tools)
 | |
|         lt.has_wipe_tower = (lt.has_object && lt.wipe_tower_partitions > 0) || lt.print_z < object_bottom_z + EPSILON;
 | |
| 
 | |
|     // Test for a raft, insert additional wipe tower layer to fill in the raft separation gap.
 | |
|     double max_layer_height = std::numeric_limits<double>::max();
 | |
|     for (size_t i = 0; i < config.nozzle_diameter.values.size(); ++ i) {
 | |
|         double mlh = config.max_layer_height.values[i];
 | |
|         if (mlh == 0.)
 | |
|             mlh = 0.75 * config.nozzle_diameter.values[i];
 | |
|         max_layer_height = std::min(max_layer_height, mlh);
 | |
|     }
 | |
|     for (size_t i = 0; i + 1 < m_layer_tools.size(); ++ i) {
 | |
|         const LayerTools <      = m_layer_tools[i];
 | |
|         const LayerTools <_next = m_layer_tools[i + 1];
 | |
|         if (lt.print_z < object_bottom_z + EPSILON && lt_next.print_z >= object_bottom_z + EPSILON) {
 | |
|             // lt is the last raft layer. Find the 1st object layer.
 | |
|             size_t j = i + 1;
 | |
|             for (; j < m_layer_tools.size() && ! m_layer_tools[j].has_wipe_tower; ++ j);
 | |
|             if (j < m_layer_tools.size()) {
 | |
|                 const LayerTools <_object = m_layer_tools[j];
 | |
|                 coordf_t gap = lt_object.print_z - lt.print_z;
 | |
|                 assert(gap > 0.f);
 | |
|                 if (gap > max_layer_height + EPSILON) {
 | |
|                     // Insert one additional wipe tower layer between lh.print_z and lt_object.print_z.
 | |
|                     LayerTools lt_new(0.5f * (lt.print_z + lt_object.print_z));
 | |
|                     // Find the 1st layer above lt_new.
 | |
|                     for (j = i + 1; j < m_layer_tools.size() && m_layer_tools[j].print_z < lt_new.print_z - EPSILON; ++ j);
 | |
|                     if (std::abs(m_layer_tools[j].print_z - lt_new.print_z) < EPSILON) {
 | |
| 						m_layer_tools[j].has_wipe_tower = true;
 | |
| 					} else {
 | |
| 						LayerTools <_extra = *m_layer_tools.insert(m_layer_tools.begin() + j, lt_new);
 | |
|                         //LayerTools <_prev  = m_layer_tools[j];
 | |
|                         LayerTools <_next  = m_layer_tools[j + 1];
 | |
|                         assert(! m_layer_tools[j - 1].extruders.empty() && ! lt_next.extruders.empty());
 | |
|                         // FIXME: Following assert tripped when running combine_infill.t. I decided to comment it out for now.
 | |
|                         // If it is a bug, it's likely not critical, because this code is unchanged for a long time. It might
 | |
|                         // still be worth looking into it more and decide if it is a bug or an obsolete assert.
 | |
|                         //assert(lt_prev.extruders.back() == lt_next.extruders.front());
 | |
|                         lt_extra.has_wipe_tower = true;
 | |
|                         lt_extra.extruders.push_back(lt_next.extruders.front());
 | |
|                         lt_extra.wipe_tower_partitions = lt_next.wipe_tower_partitions;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // If the model contains empty layers (such as https://github.com/prusa3d/Slic3r/issues/1266), there might be layers
 | |
|     // that were not marked as has_wipe_tower, even when they should have been. This produces a crash with soluble supports
 | |
|     // and maybe other problems. We will therefore go through layer_tools and detect and fix this.
 | |
|     // So, if there is a non-object layer starting with different extruder than the last one ended with (or containing more than one extruder),
 | |
|     // we'll mark it with has_wipe tower.
 | |
|     for (unsigned int i=0; i+1<m_layer_tools.size(); ++i) {
 | |
|         LayerTools& lt = m_layer_tools[i];
 | |
|         LayerTools& lt_next = m_layer_tools[i+1];
 | |
|         if (lt.extruders.empty() || lt_next.extruders.empty())
 | |
|             break;
 | |
|         if (!lt_next.has_wipe_tower && (lt_next.extruders.front() != lt.extruders.back() || lt_next.extruders.size() > 1))
 | |
|             lt_next.has_wipe_tower = true;
 | |
|         // We should also check that the next wipe tower layer is no further than max_layer_height:
 | |
|         unsigned int j = i+1;
 | |
|         double last_wipe_tower_print_z = lt_next.print_z;
 | |
|         while (++j < m_layer_tools.size()-1 && !m_layer_tools[j].has_wipe_tower)
 | |
|             if (m_layer_tools[j+1].print_z - last_wipe_tower_print_z > max_layer_height) {
 | |
|                 m_layer_tools[j].has_wipe_tower = true;
 | |
|                 last_wipe_tower_print_z = m_layer_tools[j].print_z;
 | |
|             }
 | |
|     }
 | |
| 
 | |
|     // Calculate the wipe_tower_layer_height values.
 | |
|     coordf_t wipe_tower_print_z_last = 0.;
 | |
|     for (LayerTools < : m_layer_tools)
 | |
|         if (lt.has_wipe_tower) {
 | |
|             lt.wipe_tower_layer_height = lt.print_z - wipe_tower_print_z_last;
 | |
|             wipe_tower_print_z_last = lt.print_z;
 | |
|         }
 | |
| }
 | |
| 
 | |
| void ToolOrdering::collect_extruder_statistics(bool prime_multi_material)
 | |
| {
 | |
|     m_first_printing_extruder = (unsigned int)-1;
 | |
|     for (const auto < : m_layer_tools)
 | |
|         if (! lt.extruders.empty()) {
 | |
|             m_first_printing_extruder = lt.extruders.front();
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|     m_last_printing_extruder = (unsigned int)-1;
 | |
|     for (auto lt_it = m_layer_tools.rbegin(); lt_it != m_layer_tools.rend(); ++ lt_it)
 | |
|         if (! lt_it->extruders.empty()) {
 | |
|             m_last_printing_extruder = lt_it->extruders.back();
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|     m_all_printing_extruders.clear();
 | |
|     for (const auto < : m_layer_tools) {
 | |
|         append(m_all_printing_extruders, lt.extruders);
 | |
|         sort_remove_duplicates(m_all_printing_extruders);
 | |
|     }
 | |
| 
 | |
|     if (prime_multi_material && ! m_all_printing_extruders.empty()) {
 | |
|         // Reorder m_all_printing_extruders in the sequence they will be primed, the last one will be m_first_printing_extruder.
 | |
|         // Then set m_first_printing_extruder to the 1st extruder primed.
 | |
|         m_all_printing_extruders.erase(
 | |
|             std::remove_if(m_all_printing_extruders.begin(), m_all_printing_extruders.end(),
 | |
|                 [ this ](const unsigned int eid) { return eid == m_first_printing_extruder; }),
 | |
|             m_all_printing_extruders.end());
 | |
|         m_all_printing_extruders.emplace_back(m_first_printing_extruder);
 | |
|         m_first_printing_extruder = m_all_printing_extruders.front();
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| // This function is called from Print::mark_wiping_extrusions and sets extruder this entity should be printed with (-1 .. as usual)
 | |
| void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies)
 | |
| {
 | |
|     something_overridden = true;
 | |
| 
 | |
|     auto entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first; // (add and) return iterator
 | |
|     auto& copies_vector = entity_map_it->second;
 | |
|     if (copies_vector.size() < num_of_copies)
 | |
|         copies_vector.resize(num_of_copies, -1);
 | |
| 
 | |
|     if (copies_vector[copy_id] != -1)
 | |
|         std::cout << "ERROR: Entity extruder overriden multiple times!!!\n";    // A debugging message - this must never happen.
 | |
| 
 | |
|     copies_vector[copy_id] = extruder;
 | |
| }
 | |
| 
 | |
| 
 | |
| // Finds first non-soluble extruder on the layer
 | |
| int WipingExtrusions::first_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const
 | |
| {
 | |
|     const LayerTools& lt = *m_layer_tools;
 | |
|     for (auto extruders_it = lt.extruders.begin(); extruders_it != lt.extruders.end(); ++extruders_it)
 | |
|         if (!print_config.filament_soluble.get_at(*extruders_it))
 | |
|             return (*extruders_it);
 | |
| 
 | |
|     return (-1);
 | |
| }
 | |
| 
 | |
| // Finds last non-soluble extruder on the layer
 | |
| int WipingExtrusions::last_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const
 | |
| {
 | |
|     const LayerTools& lt = *m_layer_tools;
 | |
|     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);
 | |
| 
 | |
|     return (-1);
 | |
| }
 | |
| 
 | |
| 
 | |
| // 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 (print_config.filament_soluble.get_at(Print::get_extruder(eec, region)))
 | |
|         return false;
 | |
| 
 | |
|     if (object.config().wipe_into_objects)
 | |
|         return true;
 | |
| 
 | |
|     if (!region.config().wipe_into_infill || eec.role() != erInternalInfill)
 | |
|         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, unsigned int old_extruder, unsigned int new_extruder, float volume_to_wipe)
 | |
| {
 | |
|     const LayerTools& lt = *m_layer_tools;
 | |
|     const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
 | |
| 
 | |
|     if (print.config().filament_soluble.get_at(old_extruder) || print.config().filament_soluble.get_at(new_extruder))
 | |
|         return volume_to_wipe;      // Soluble filament cannot be wiped in a random infill, neither the filament after it
 | |
| 
 | |
|     // 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() + (perimeters_done ? 0 : 1); ++i) {
 | |
|         if (!perimeters_done && (i==(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;
 | |
|         }
 | |
| 
 | |
|         const PrintObject* object = object_list[i];
 | |
| 
 | |
|         // Finds this layer:
 | |
|         auto this_layer_it = std::find_if(object->layers().begin(), object->layers().end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
 | |
|         if (this_layer_it == object->layers().end())
 | |
|             continue;
 | |
|         const Layer* this_layer = *this_layer_it;
 | |
|         size_t num_of_copies = object->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->region_volumes.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)) {
 | |
|                     for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) {                      // iterate through all infill Collections
 | |
|                         auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
 | |
| 
 | |
|                         if (!is_overriddable(*fill, print.config(), *object, region))
 | |
|                             continue;
 | |
| 
 | |
|                         if (volume_to_wipe<=0)
 | |
|                             continue;
 | |
| 
 | |
|                         if (!object->config().wipe_into_objects && !print.config().infill_first && region.config().wipe_into_infill)
 | |
|                             // 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 (!lt.is_extruder_order(region.config().perimeter_extruder - 1, new_extruder))
 | |
|                                 continue;
 | |
| 
 | |
|                         if ((!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 -= float(fill->total_volume());
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 // Now the same for perimeters - see comments above for explanation:
 | |
|                 if (object->config().wipe_into_objects && (print.config().infill_first ? perimeters_done : !perimeters_done))
 | |
|                 {
 | |
|                     for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) {
 | |
|                         auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
 | |
|                         if (!is_overriddable(*fill, print.config(), *object, region))
 | |
|                             continue;
 | |
| 
 | |
|                         if (volume_to_wipe<=0)
 | |
|                             continue;
 | |
| 
 | |
|                         if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) {
 | |
|                             set_extruder_override(fill, copy, new_extruder, num_of_copies);
 | |
|                             volume_to_wipe -= float(fill->total_volume());
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return std::max(0.f, volume_to_wipe);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| // Called after all toolchanges on a layer were mark_infill_overridden. There might still be overridable entities,
 | |
| // that were not actually overridden. If they are part of a dedicated object, printing them with the extruder
 | |
| // they were initially assigned to might mean violating the perimeter-infill order. We will therefore go through
 | |
| // them again and make sure we override it.
 | |
| void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
 | |
| {
 | |
|     const LayerTools& lt = *m_layer_tools;
 | |
|     unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config());
 | |
|     unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config());
 | |
| 
 | |
|     for (const PrintObject* object : print.objects()) {
 | |
|         // Finds this layer:
 | |
|         auto this_layer_it = std::find_if(object->layers().begin(), object->layers().end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
 | |
|         if (this_layer_it == object->layers().end())
 | |
|             continue;
 | |
|         const Layer* this_layer = *this_layer_it;
 | |
|         size_t num_of_copies = object->copies().size();
 | |
| 
 | |
|         for (size_t 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->region_volumes.size(); ++ region_id) {
 | |
|                 const auto& region = *object->print()->regions()[region_id];
 | |
| 
 | |
|                 if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
 | |
|                     continue;
 | |
| 
 | |
|                 for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) {                      // iterate through all infill Collections
 | |
|                     auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
 | |
| 
 | |
|                     if (!is_overriddable(*fill, print.config(), *object, region)
 | |
|                      || is_entity_overridden(fill, copy) )
 | |
|                         continue;
 | |
| 
 | |
|                     // This infill could have been overridden but was not - unless we do something, it could be
 | |
|                     // printed before its perimeter, or not be printed at all (in case its original extruder has
 | |
|                     // not been added to LayerTools
 | |
|                     // Either way, we will now force-override it with something suitable:
 | |
|                     if (print.config().infill_first
 | |
|                     || object->config().wipe_into_objects  // in this case the perimeter is overridden, so we can override by the last one safely
 | |
|                     || lt.is_extruder_order(region.config().perimeter_extruder - 1, last_nonsoluble_extruder    // !infill_first, but perimeter is already printed when last extruder prints
 | |
|                     || std::find(lt.extruders.begin(), lt.extruders.end(), region.config().infill_extruder - 1) == lt.extruders.end()) // we have to force override - this could violate infill_first (FIXME)
 | |
|                       )
 | |
|                         set_extruder_override(fill, copy, (print.config().infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies);
 | |
|                     else {
 | |
|                         // In this case we can (and should) leave it to be printed normally.
 | |
|                         // Force overriding would mean it gets printed before its perimeter.
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 // Now the same for perimeters - see comments above for explanation:
 | |
|                 for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) {                      // iterate through all perimeter Collections
 | |
|                     auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
 | |
|                     if (!is_overriddable(*fill, print.config(), *object, region)
 | |
|                      || is_entity_overridden(fill, copy) )
 | |
|                         continue;
 | |
| 
 | |
|                     set_extruder_override(fill, copy, (print.config().infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| // 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
 | |
| // It also modifies the vector in place and changes all -1 to correct_extruder_id (at the time the overrides were created, correct extruders were not known,
 | |
| // so -1 was used as "print as usual".
 | |
| // The resulting vector has to keep track of which extrusions are the ones that were overridden and which were not. In the extruder is used as overridden,
 | |
| // its number is saved as it is (zero-based index). Usual extrusions are saved as -number-1 (unfortunately there is no negative zero).
 | |
| const std::vector<int>* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies)
 | |
| {
 | |
|     auto entity_map_it = entity_map.find(entity);
 | |
|     if (entity_map_it == entity_map.end())
 | |
|         entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first;
 | |
| 
 | |
|     // Now the entity_map_it should be valid, let's make sure the vector is long enough:
 | |
|     entity_map_it->second.resize(num_of_copies, -1);
 | |
| 
 | |
|     // Each -1 now means "print as usual" - we will replace it with actual extruder id (shifted it so we don't lose that information):
 | |
|     std::replace(entity_map_it->second.begin(), entity_map_it->second.end(), -1, -correct_extruder_id-1);
 | |
| 
 | |
|     return &(entity_map_it->second);
 | |
| }
 | |
| 
 | |
| 
 | |
| } // namespace Slic3r
 | 
