diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 40ca7b074..1e7fae3b5 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2026,58 +2026,69 @@ void GCode::process_layer( layer.lslices[i].contour.contains(point); }; - for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) { - const LayerRegion *layerm = (region_id < layer.regions().size()) ? layer.regions()[region_id] : nullptr; + for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id) { + const LayerRegion *layerm = layer.regions()[region_id]; if (layerm == nullptr) continue; const PrintRegion ®ion = *print.regions()[region_id]; - // Now we must process perimeters and infills and create islands of extrusions in by_region std::map. // It is also necessary to save which extrusions are part of MM wiping and which are not. // The process is almost the same for perimeters and infills - we will do it in a cycle that repeats twice: - for (std::string entity_type("infills") ; entity_type != "done" ; entity_type = entity_type=="infills" ? "perimeters" : "done") { - - const ExtrusionEntitiesPtr& source_entities = entity_type=="infills" ? layerm->fills.entities : layerm->perimeters.entities; - - for (const ExtrusionEntity *ee : source_entities) { - // fill represents infill extrusions of a single island. - const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee); - if (fill->entities.empty()) // This shouldn't happen but first_point() would fail. + std::vector<unsigned int> printing_extruders; + for (const ObjectByExtruder::Island::Region::Type entity_type : { ObjectByExtruder::Island::Region::INFILL, ObjectByExtruder::Island::Region::PERIMETERS }) { + for (const ExtrusionEntity *ee : (entity_type == ObjectByExtruder::Island::Region::INFILL) ? layerm->fills.entities : layerm->perimeters.entities) { + // extrusions represents infill or perimeter extrusions of a single island. + assert(dynamic_cast<const ExtrusionEntityCollection*>(ee) != nullptr); + const auto *extrusions = static_cast<const ExtrusionEntityCollection*>(ee); + if (extrusions->entities.empty()) // This shouldn't happen but first_point() would fail. continue; // This extrusion is part of certain Region, which tells us which extruder should be used for it: - int correct_extruder_id = Print::get_extruder(*fill, region); + int correct_extruder_id = Print::get_extruder(*extrusions, region); // Let's recover vector of extruder overrides: - const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->copies().size()); + const WipingExtrusions::ExtruderPerCopy *entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(extrusions, correct_extruder_id, layer_to_print.object()->copies().size()); + printing_extruders.clear(); + if (entity_overrides == nullptr) { + printing_extruders.emplace_back(correct_extruder_id); + } else { + printing_extruders.reserve(entity_overrides->size() + 1); + for (int extruder : *entity_overrides) + printing_extruders.emplace_back(extruder >= 0 ? + // at least one copy is overridden to use this extruder + extruder : + // at least one copy would normally be printed with this extruder (see get_extruder_overrides function for explanation) + static_cast<unsigned int>(- extruder - 1)); + } + if (! layer_tools.has_extruder(correct_extruder_id)) { + // 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) + printing_extruders.emplace_back(layer_tools.extruders.back()); + } + Slic3r::sort_remove_duplicates(printing_extruders); + if (printing_extruders.size() == 1 && printing_extruders.front() == correct_extruder_id) + entity_overrides = nullptr; // 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) + for (unsigned int extruder : printing_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) - (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, - extruder, - &layer_to_print - layers.data(), - layers.size(), n_slices+1); - for (size_t i = 0; i <= n_slices; ++ i) { - bool last = i == n_slices; - size_t island_idx = last ? n_slices : slices_test_order[i]; - if (// fill->first_point does not fit inside any slice - last || - // fill->first_point fits inside ith slice - point_inside_surface(island_idx, fill->first_point())) { - if (islands[island_idx].by_region.empty()) - islands[island_idx].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region()); - islands[island_idx].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->copies().size()); - break; - } + std::vector<ObjectByExtruder::Island> &islands = object_islands_by_extruder( + by_extruder, + extruder, + &layer_to_print - layers.data(), + layers.size(), n_slices+1); + for (size_t i = 0; i <= n_slices; ++ i) { + bool last = i == n_slices; + size_t island_idx = last ? n_slices : slices_test_order[i]; + if (// extrusions->first_point does not fit inside any slice + last || + // extrusions->first_point fits inside ith slice + point_inside_surface(island_idx, extrusions->first_point())) { + if (islands[island_idx].by_region.empty()) + islands[island_idx].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region()); + islands[island_idx].by_region[region_id].append(entity_type, extrusions, entity_overrides); + break; } } } @@ -2147,6 +2158,7 @@ void GCode::process_layer( // We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature): bool is_anything_overridden = const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden(); + std::vector<ObjectByExtruder::Island::Region> by_region_per_copy_cache; for (int print_wipe_extrusions = is_anything_overridden; print_wipe_extrusions>=0; --print_wipe_extrusions) { if (is_anything_overridden && print_wipe_extrusions == 0) gcode+="; PURGING FINISHED\n"; @@ -2174,8 +2186,8 @@ void GCode::process_layer( m_layer = layers[instance_to_print.layer_id].layer(); } for (ObjectByExtruder::Island &island : instance_to_print.object_by_extruder.islands) { - const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(instance_to_print.instance_id, extruder_id, print_wipe_extrusions) : island.by_region; - + const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(by_region_per_copy_cache, instance_to_print.instance_id, extruder_id, print_wipe_extrusions) : island.by_region; + //FIXME the following code prints regions in the order they are defined, the path is not optimized in any way. if (print.config().infill_first) { gcode += this->extrude_infill(print, by_region_specific); gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[instance_to_print.layer_id]); @@ -3260,59 +3272,97 @@ Point GCode::gcode_to_point(const Vec2d &point) const // Goes through by_region std::vector and returns reference to a subvector of entities, that are to be printed // during infill/perimeter wiping, or normally (depends on wiping_entities parameter) -// Returns a reference to member to avoid copying. -const std::vector<GCode::ObjectByExtruder::Island::Region>& GCode::ObjectByExtruder::Island::by_region_per_copy(unsigned int copy, int extruder, bool wiping_entities) +// Fills in by_region_per_copy_cache and returns its reference. +const std::vector<GCode::ObjectByExtruder::Island::Region>& GCode::ObjectByExtruder::Island::by_region_per_copy(std::vector<Region> &by_region_per_copy_cache, unsigned int copy, int extruder, bool wiping_entities) const { + bool has_overrides = false; + for (const auto& reg : by_region) + if (! reg.infills_overrides.empty() || ! reg.perimeters_overrides.empty()) { + has_overrides = true; + break; + } + if (! has_overrides) + // Simple case. No need to copy the regions. + return this->by_region; + + // Complex case. Some of the extrusions of some object instances are to be printed first - those are the wiping extrusions. + // Some of the extrusions of some object instances are printed later - those are the clean print extrusions. + // Filter out the extrusions based on the infill_overrides / perimeter_overrides: + + // Data is cleared, but the memory is not. by_region_per_copy_cache.clear(); for (const auto& reg : by_region) { - by_region_per_copy_cache.push_back(ObjectByExtruder::Island::Region()); // creates a region in the newly created Island + by_region_per_copy_cache.emplace_back(); // creates a region in the newly created Island // Now we are going to iterate through perimeters and infills and pick ones that are supposed to be printed // References are used so that we don't have to repeat the same code for (int iter = 0; iter < 2; ++iter) { - const ExtrusionEntitiesPtr& entities = (iter ? reg.infills.entities : reg.perimeters.entities); - ExtrusionEntityCollection& target_eec = (iter ? by_region_per_copy_cache.back().infills : by_region_per_copy_cache.back().perimeters); - const std::vector<const ExtruderPerCopy*>& overrides = (iter ? reg.infills_overrides : reg.perimeters_overrides); + const ExtrusionEntitiesPtr& entities = (iter ? reg.infills.entities : reg.perimeters.entities); + ExtrusionEntityCollection& target_eec = (iter ? by_region_per_copy_cache.back().infills : by_region_per_copy_cache.back().perimeters); + const std::vector<const WipingExtrusions::ExtruderPerCopy*>& overrides = (iter ? reg.infills_overrides : reg.perimeters_overrides); // Now the most important thing - which extrusion should we print. // See function ToolOrdering::get_extruder_overrides for details about the negative numbers hack. - int this_extruder_mark = wiping_entities ? extruder : -extruder-1; - - for (unsigned int i=0;i<entities.size();++i) - if (overrides[i]->at(copy) == this_extruder_mark) // this copy should be printed with this extruder - target_eec.append((*entities[i])); + if (wiping_entities) { + // Apply overrides for this region. + for (unsigned int i = 0; i < overrides.size(); ++ i) { + const WipingExtrusions::ExtruderPerCopy *this_override = overrides[i]; + // This copy (aka object instance) should be printed with this extruder, which overrides the default one. + if (this_override != nullptr && (*this_override)[copy] == extruder) + // Clone the ExtrusionEntity. This is quite expensive. + target_eec.append((*entities[i])); + } + } else { + // Apply normal extrusions (non-overrides) for this region. + unsigned int i = 0; + for (; i < overrides.size(); ++ i) { + const WipingExtrusions::ExtruderPerCopy *this_override = overrides[i]; + // This copy (aka object instance) should be printed with this extruder, which shall be equal to the default one. + if (this_override == nullptr || (*this_override)[copy] == -extruder-1) + // Clone the ExtrusionEntity. This is quite expensive. + target_eec.append((*entities[i])); + } + for (; i < overrides.size(); ++ i) + // Clone the ExtrusionEntity. This is quite expensive. + target_eec.append(*entities[i]); + } } } return by_region_per_copy_cache; } - - // This function takes the eec and appends its entities to either perimeters or infills of this Region (depending on the first parameter) // It also saves pointer to ExtruderPerCopy struct (for each entity), that holds information about which extruders should be used for which copy. -void GCode::ObjectByExtruder::Island::Region::append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copies_extruder, size_t object_copies_num) +void GCode::ObjectByExtruder::Island::Region::append(const Type type, const ExtrusionEntityCollection* eec, const WipingExtrusions::ExtruderPerCopy* copies_extruder) { // We are going to manipulate either perimeters or infills, exactly in the same way. Let's create pointers to the proper structure to not repeat ourselves: - ExtrusionEntityCollection* perimeters_or_infills = &infills; - std::vector<const ExtruderPerCopy*>* perimeters_or_infills_overrides = &infills_overrides; + ExtrusionEntityCollection* perimeters_or_infills; + std::vector<const WipingExtrusions::ExtruderPerCopy*>* perimeters_or_infills_overrides; - if (type == "perimeters") { - perimeters_or_infills = &perimeters; - perimeters_or_infills_overrides = &perimeters_overrides; + switch (type) { + case PERIMETERS: + perimeters_or_infills = &perimeters; + perimeters_or_infills_overrides = &perimeters_overrides; + break; + case INFILL: + perimeters_or_infills = &infills; + perimeters_or_infills_overrides = &infills_overrides; + break; + default: + throw std::invalid_argument("Unknown parameter!"); } - else - if (type != "infills") { - throw std::invalid_argument("Unknown parameter!"); - return; - } - // First we append the entities, there are eec->entities.size() of them: + size_t old_size = perimeters_or_infills->entities.size(); perimeters_or_infills->append(eec->entities); - for (unsigned int i=0;i<eec->entities.size();++i) - perimeters_or_infills_overrides->push_back(copies_extruder); + if (copies_extruder != nullptr) { + perimeters_or_infills_overrides->reserve(old_size + eec->entities.size()); + perimeters_or_infills_overrides->resize(old_size, nullptr); + for (unsigned int i = 0; i < eec->entities.size(); ++ i) + perimeters_or_infills_overrides->emplace_back(copies_extruder); + } } } // namespace Slic3r diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 497dc8c71..934dcaece 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -239,7 +239,6 @@ protected: std::string extrude_multi_path(ExtrusionMultiPath multipath, std::string description = "", double speed = -1.); std::string extrude_path(ExtrusionPath path, std::string description = "", double speed = -1.); - typedef std::vector<int> ExtruderPerCopy; // Extruding multiple objects with soluble / non-soluble / combined supports // on a multi-material printer, trying to minimize tool switches. // Following structures sort extrusions by the extruder ID, by an order of objects and object islands. @@ -256,18 +255,23 @@ protected: ExtrusionEntityCollection perimeters; ExtrusionEntityCollection infills; - std::vector<const ExtruderPerCopy*> infills_overrides; - std::vector<const ExtruderPerCopy*> perimeters_overrides; + std::vector<const WipingExtrusions::ExtruderPerCopy*> infills_overrides; + std::vector<const WipingExtrusions::ExtruderPerCopy*> perimeters_overrides; + + enum Type { + PERIMETERS, + INFILL, + }; // Appends perimeter/infill entities and writes don't indices of those that are not to be extruder as part of perimeter/infill wiping - void append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copy_extruders, size_t object_copies_num); + void append(const Type type, const ExtrusionEntityCollection* eec, const WipingExtrusions::ExtruderPerCopy* copy_extruders); }; - std::vector<Region> by_region; // all extrusions for this island, grouped by regions - const std::vector<Region>& by_region_per_copy(unsigned int copy, int extruder, bool wiping_entities = false); // returns reference to subvector of by_region - private: - std::vector<Region> by_region_per_copy_cache; // caches vector generated by function above to avoid copying and recalculating + std::vector<Region> by_region; // all extrusions for this island, grouped by regions + + // Fills in by_region_per_copy_cache and returns its reference. + const std::vector<Region>& by_region_per_copy(std::vector<Region> &by_region_per_copy_cache, unsigned int copy, int extruder, bool wiping_entities = false) const; }; std::vector<Island> islands; }; @@ -277,7 +281,9 @@ protected: InstanceToPrint(ObjectByExtruder &object_by_extruder, size_t layer_id, const PrintObject &print_object, size_t instance_id) : object_by_extruder(object_by_extruder), layer_id(layer_id), print_object(print_object), instance_id(instance_id) {} - ObjectByExtruder &object_by_extruder; + // Repository + ObjectByExtruder &object_by_extruder; + // Index into std::vector<LayerToPrint>, which contains Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances. const size_t layer_id; const PrintObject &print_object; // Instance idx of the copy of a print object. @@ -285,7 +291,8 @@ protected: }; std::vector<InstanceToPrint> sort_print_object_instances( - std::vector<ObjectByExtruder> &objects_by_extruder, + std::vector<ObjectByExtruder> &objects_by_extruder, + // Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances. const std::vector<LayerToPrint> &layers, // Ordering must be defined for normal (non-sequential print). const std::vector<std::pair<size_t, size_t>> *ordering, diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index bcb65554d..123656e43 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -19,7 +19,7 @@ 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) + if (a == b) return false; for (auto extruder : extruders) { @@ -415,10 +415,9 @@ void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsi { 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); + auto entity_map_it = (entity_map.emplace(entity, ExtruderPerCopy())).first; // (add and) return iterator + ExtruderPerCopy& copies_vector = entity_map_it->second; + 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. @@ -426,7 +425,6 @@ void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsi 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 { @@ -622,31 +620,23 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) } } - - - - - - -// 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) +// Following function is called from GCode::process_layer and returns pointer to vector with information about which extruders should be used for given copy of this entity. +// If this extrusion does not have any override, nullptr is returned. +// Otherwise it 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 therefore keeps track of which extrusions are the ones that were overridden and which were not. If the extruder used is overridden, +// its number is saved as is (zero-based index). Regular extrusions are saved as -number-1 (unfortunately there is no negative zero). +const WipingExtrusions::ExtruderPerCopy* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies) { + ExtruderPerCopy *overrides = nullptr; 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); + if (entity_map_it != entity_map.end()) { + overrides = &entity_map_it->second; + overrides->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(overrides->begin(), overrides->end(), -1, -correct_extruder_id-1); + } + return overrides; } diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index c37c90823..dcb93c021 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -7,6 +7,8 @@ #include <utility> +#include <boost/container/small_vector.hpp> + namespace Slic3r { class Print; @@ -25,8 +27,19 @@ public: return something_overridden; } + // When allocating extruder overrides of an object's ExtrusionEntity, overrides for maximum 3 copies are allocated in place. + typedef boost::container::small_vector<int32_t, 3> ExtruderPerCopy; + + class ExtruderOverrides + { + public: + ExtruderOverrides(const ExtruderPerCopy *overrides, const int correct_extruder_id) : m_overrides(overrides) {} + private: + const ExtruderPerCopy *m_overrides; + }; + // 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, size_t num_of_copies); + const ExtruderPerCopy* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t 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: @@ -50,7 +63,7 @@ private: return (entity_map.find(entity) == entity_map.end() ? false : entity_map.at(entity).at(copy_id) != -1); } - std::map<const ExtrusionEntity*, std::vector<int>> entity_map; // to keep track of who prints what + std::map<const ExtrusionEntity*, ExtruderPerCopy> entity_map; // to keep track of who prints what bool something_overridden = false; const LayerTools* m_layer_tools; // so we know which LayerTools object this belongs to }; @@ -74,6 +87,7 @@ public: bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; } bool is_extruder_order(unsigned int a, unsigned int b) const; + bool has_extruder(unsigned int extruder) const { return std::find(this->extruders.begin(), this->extruders.end(), extruder) != this->extruders.end(); } coordf_t print_z; bool has_object; diff --git a/src/libslic3r/pchheader.hpp b/src/libslic3r/pchheader.hpp index 67b3d3a21..1339368bd 100644 --- a/src/libslic3r/pchheader.hpp +++ b/src/libslic3r/pchheader.hpp @@ -63,6 +63,7 @@ #include <boost/bind.hpp> #include <boost/config.hpp> #include <boost/config/warning_disable.hpp> +#include <boost/container/small_vector.hpp> #include <boost/date_time/local_time/local_time.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/filesystem.hpp>