diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp index c0f681de5b..15363e8eda 100644 --- a/xs/src/libslic3r/ExtrusionEntity.hpp +++ b/xs/src/libslic3r/ExtrusionEntity.hpp @@ -93,19 +93,6 @@ public: virtual Polyline as_polyline() const = 0; virtual double length() const = 0; virtual double total_volume() const = 0; - - void set_entity_extruder_override(unsigned int copy, int extruder) { - if (copy+1 > extruder_override.size()) - extruder_override.resize(copy+1, -1); // copy is zero-based index - extruder_override[copy] = extruder; - } - virtual int get_extruder_override(unsigned int copy) const { try { return extruder_override.at(copy); } catch (...) { return -1; } } - virtual bool is_extruder_overridden(unsigned int copy) const { try { return extruder_override.at(copy) != -1; } catch (...) { return false; } } - -private: - // Set this variable to explicitly state you want to use specific extruder for thie EE (used for MM infill wiping) - // Each member of the vector corresponds to the respective copy of the object - std::vector extruder_override; }; typedef std::vector ExtrusionEntitiesPtr; diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp index ee4b75f383..382455fe33 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp @@ -90,13 +90,6 @@ public: CONFESS("Calling length() on a ExtrusionEntityCollection"); return 0.; } - - void set_extruder_override(unsigned int copy, int extruder) { - for (ExtrusionEntity* member : entities) - member->set_entity_extruder_override(copy, extruder); - } - virtual int get_extruder_override(unsigned int copy) const { return entities.front()->get_extruder_override(copy); } - virtual bool is_extruder_overridden(unsigned int copy) const { return entities.front()->is_extruder_overridden(copy); } }; } diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 809745da21..cd27e3edde 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -1147,7 +1147,6 @@ void GCode::process_layer( // Group extrusions by an extruder, then by an object, an island and a region. std::map> by_extruder; - for (const LayerToPrint &layer_to_print : layers) { if (layer_to_print.support_layer != nullptr) { const SupportLayer &support_layer = *layer_to_print.support_layer; @@ -1225,92 +1224,63 @@ void GCode::process_layer( continue; const PrintRegion ®ion = *print.regions[region_id]; - // process perimeters - for (const ExtrusionEntity *ee : layerm->perimeters.entities) { - // perimeter_coll represents perimeter extrusions of a single island. - const auto *perimeter_coll = dynamic_cast(ee); - if (perimeter_coll->entities.empty()) - // This shouldn't happen but first_point() would fail. - continue; - // Init by_extruder item only if we actually use the extruder. - std::vector &islands = object_islands_by_extruder( - by_extruder, - std::max(region.config.perimeter_extruder.value - 1, 0), - &layer_to_print - layers.data(), - layers.size(), n_slices+1); - for (size_t i = 0; i <= n_slices; ++ i) - if (// perimeter_coll->first_point does not fit inside any slice - i == n_slices || - // perimeter_coll->first_point fits inside ith slice - point_inside_surface(i, perimeter_coll->first_point())) { - if (islands[i].by_region.empty()) - islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region()); - islands[i].by_region[region_id].perimeters.append(perimeter_coll->entities); - // We just added perimeter_coll->entities.size() entities, if they are not to be printed before the main object (during infill wiping), - // we will note their indices (for each copy separately): - unsigned int first_added_entity_index = islands[i].by_region[region_id].perimeters.entities.size() - perimeter_coll->entities.size(); - for (unsigned copy_id = 0; copy_id < layer_to_print.object()->_shifted_copies.size(); ++copy_id) { - if (islands[i].by_region[region_id].perimeters_per_copy_ids.size() < copy_id + 1) // if this copy isn't in the list yet - islands[i].by_region[region_id].perimeters_per_copy_ids.push_back(std::vector()); - if (!perimeter_coll->is_extruder_overridden(copy_id)) - for (int j=first_added_entity_index; jfills.entities : layerm->perimeters.entities; + + for (const ExtrusionEntity *ee : source_entities) { + // fill represents infill extrusions of a single island. + const auto *fill = dynamic_cast(ee); + if (fill->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 = entity_type=="infills" ? std::max(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) : + std::max(region.config.perimeter_extruder.value - 1, 0); + + // Let's recover vector of extruder overrides: + const ExtruderPerCopy* entity_overrides = const_cast(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) + { + std::vector &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) + if (// fill->first_point does not fit inside any slice + i == n_slices || + // fill->first_point fits inside ith slice + point_inside_surface(i, fill->first_point())) { + if (islands[i].by_region.empty()) + islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region()); + islands[i].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->_shifted_copies.size()); + break; + } } - break; - } - } - - // process infill - // layerm->fills is a collection of Slic3r::ExtrusionPath::Collection objects (C++ class ExtrusionEntityCollection), - // each one containing the ExtrusionPath objects of a certain infill "group" (also called "surface" - // throughout the code). We can redefine the order of such Collections but we have to - // do each one completely at once. - for (const ExtrusionEntity *ee : layerm->fills.entities) { - // fill represents infill extrusions of a single island. - const auto *fill = dynamic_cast(ee); - if (fill->entities.empty()) - // This shouldn't happen but first_point() would fail. - continue; - - // init by_extruder item only if we actually use the extruder - int extruder_id = std::max(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1); - // Init by_extruder item only if we actually use the extruder. - std::vector &islands = object_islands_by_extruder( - by_extruder, - extruder_id, - &layer_to_print - layers.data(), - layers.size(), n_slices+1); - for (size_t i = 0; i <= n_slices; ++i) - if (// fill->first_point does not fit inside any slice - i == n_slices || - // fill->first_point fits inside ith slice - point_inside_surface(i, fill->first_point())) { - if (islands[i].by_region.empty()) - islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region()); - islands[i].by_region[region_id].infills.append(fill->entities); - - // We just added fill->entities.size() entities, if they are not to be printed before the main object (during infill wiping), - // we will note their indices (for each copy separately): - unsigned int first_added_entity_index = islands[i].by_region[region_id].infills.entities.size() - fill->entities.size(); - for (unsigned copy_id = 0; copy_id < layer_to_print.object()->_shifted_copies.size(); ++copy_id) { - if (islands[i].by_region[region_id].infills_per_copy_ids.size() < copy_id + 1) // if this copy isn't in the list yet - islands[i].by_region[region_id].infills_per_copy_ids.push_back(std::vector()); - if (!fill->is_extruder_overridden(copy_id)) - for (int j=first_added_entity_index; j> lower_layer_edge_grids(layers.size()); for (unsigned int extruder_id : layer_tools.extruders) - { + { gcode += (layer_tools.has_wipe_tower && m_wipe_tower) ? m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()) : this->set_extruder(extruder_id); @@ -1335,7 +1305,7 @@ void GCode::process_layer( for (ExtrusionPath &path : loop.paths) { path.height = (float)layer.height; path.mm3_per_mm = mm3_per_mm; - } + } gcode += this->extrude_loop(loop, "skirt", m_config.support_material_speed.value); } m_avoid_crossing_perimeters.use_external_mp = false; @@ -1344,7 +1314,7 @@ void GCode::process_layer( m_avoid_crossing_perimeters.disable_once = true; } } - + // Extrude brim with the extruder of the 1st region. if (! m_brim_done) { this->set_origin(0., 0.); @@ -1357,100 +1327,59 @@ void GCode::process_layer( m_avoid_crossing_perimeters.disable_once = true; } - if (layer_tools.has_wipe_tower) // the infill/perimeter wiping to save the material on the wipe tower - { - gcode += "; INFILL WIPING STARTS\n"; - if (extruder_id != layer_tools.extruders.front()) { // if this is the first extruder on this layer, there was no toolchange - for (const auto& layer_to_print : layers) { // iterate through all objects - if (layer_to_print.object_layer == nullptr) - continue; - - m_config.apply((layer_to_print.object_layer)->object()->config, true); - - for (unsigned copy_id = 0; copy_id < layer_to_print.object()->copies().size(); ++copy_id) { - std::vector overridden; - for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) { - ObjectByExtruder::Island::Region new_region; - overridden.push_back(new_region); - for (ExtrusionEntity *ee : (*layer_to_print.object_layer).regions[region_id]->fills.entities) { - auto *fill = dynamic_cast(ee); - if (fill->get_extruder_override(copy_id) == (int)extruder_id) - overridden.back().infills.append(*fill); - } - for (ExtrusionEntity *ee : (*layer_to_print.object_layer).regions[region_id]->perimeters.entities) { - auto *fill = dynamic_cast(ee); - if (fill->get_extruder_override(copy_id) == (int)extruder_id) - overridden.back().perimeters.append((*fill).entities); - } - } - - Point copy = (layer_to_print.object_layer)->object()->_shifted_copies[copy_id]; - this->set_origin(unscale(copy.x), unscale(copy.y)); - - - std::unique_ptr u; - if (print.config.infill_first) { - gcode += this->extrude_infill(print, overridden); - gcode += this->extrude_perimeters(print, overridden, u); - } - else { - gcode += this->extrude_perimeters(print, overridden, u); - gcode += this->extrude_infill(print, overridden); - } - } - } - } - gcode += "; WIPING FINISHED\n"; - } - - auto objects_by_extruder_it = by_extruder.find(extruder_id); if (objects_by_extruder_it == by_extruder.end()) continue; - for (ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) { - const size_t layer_id = &object_by_extruder - objects_by_extruder_it->second.data(); - const PrintObject *print_object = layers[layer_id].object(); - if (print_object == nullptr) - // This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z. - continue; - m_config.apply(print_object->config, true); - m_layer = layers[layer_id].layer(); - if (m_config.avoid_crossing_perimeters) - m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true)); - Points copies; - if (single_object_idx == size_t(-1)) - copies = print_object->_shifted_copies; - else - copies.push_back(print_object->_shifted_copies[single_object_idx]); - // Sort the copies by the closest point starting with the current print position. + // We are almost ready to print. However, we must go through all the object twice and only print the overridden extrusions first (infill/primeter wiping feature): + for (int print_wipe_extrusions=layer_tools.wiping_extrusions.is_anything_overridden(); print_wipe_extrusions>=0; --print_wipe_extrusions) { + for (ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) { + const size_t layer_id = &object_by_extruder - objects_by_extruder_it->second.data(); + const PrintObject *print_object = layers[layer_id].object(); + if (print_object == nullptr) + // This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z. + continue; - unsigned int copy_id = 0; - for (const Point © : copies) { - // When starting a new object, use the external motion planner for the first travel move. - std::pair this_object_copy(print_object, copy); - if (m_last_obj_copy != this_object_copy) - m_avoid_crossing_perimeters.use_external_mp_once = true; - m_last_obj_copy = this_object_copy; - this->set_origin(unscale(copy.x), unscale(copy.y)); - if (object_by_extruder.support != nullptr) { - m_layer = layers[layer_id].support_layer; - gcode += this->extrude_support( - // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths. - object_by_extruder.support->chained_path_from(m_last_pos, false, object_by_extruder.support_extrusion_role)); - m_layer = layers[layer_id].layer(); - } - for (ObjectByExtruder::Island &island : object_by_extruder.islands) { - if (print.config.infill_first) { - gcode += this->extrude_infill(print, island.by_region_per_copy(copy_id)); - gcode += this->extrude_perimeters(print, island.by_region_per_copy(copy_id), lower_layer_edge_grids[layer_id]); - } else { - gcode += this->extrude_perimeters(print, island.by_region_per_copy(copy_id), lower_layer_edge_grids[layer_id]); - gcode += this->extrude_infill(print, island.by_region_per_copy(copy_id)); + m_config.apply(print_object->config, true); + m_layer = layers[layer_id].layer(); + if (m_config.avoid_crossing_perimeters) + m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true)); + Points copies; + if (single_object_idx == size_t(-1)) + copies = print_object->_shifted_copies; + else + copies.push_back(print_object->_shifted_copies[single_object_idx]); + // Sort the copies by the closest point starting with the current print position. + + unsigned int copy_id = 0; + for (const Point © : copies) { + // When starting a new object, use the external motion planner for the first travel move. + std::pair this_object_copy(print_object, copy); + if (m_last_obj_copy != this_object_copy) + m_avoid_crossing_perimeters.use_external_mp_once = true; + m_last_obj_copy = this_object_copy; + this->set_origin(unscale(copy.x), unscale(copy.y)); + if (object_by_extruder.support != nullptr) { + m_layer = layers[layer_id].support_layer; + gcode += this->extrude_support( + // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths. + object_by_extruder.support->chained_path_from(m_last_pos, false, object_by_extruder.support_extrusion_role)); + m_layer = layers[layer_id].layer(); } + for (ObjectByExtruder::Island &island : object_by_extruder.islands) { + const auto& by_region_specific = layer_tools.wiping_extrusions.is_anything_overridden() ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region; + + 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[layer_id]); + } else { + gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]); + gcode += this->extrude_infill(print,by_region_specific); + } + } + ++copy_id; } - ++copy_id; } } } @@ -2512,29 +2441,61 @@ Point GCode::gcode_to_point(const Pointf &point) const } -// Goes through by_region std::vector and returns reference to a subvector of entities to be printed in usual time -// i.e. not when it's going to be done during infill wiping -const std::vector& GCode::ObjectByExtruder::Island::by_region_per_copy(unsigned int copy) +// 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::by_region_per_copy(unsigned int copy, int extruder, bool wiping_entities) { - if (copy == last_copy) - return by_region_per_copy_cache; - else { - by_region_per_copy_cache.clear(); - last_copy = copy; - } + by_region_per_copy_cache.clear(); for (const auto& reg : by_region) { - by_region_per_copy_cache.push_back(ObjectByExtruder::Island::Region()); + by_region_per_copy_cache.push_back(ObjectByExtruder::Island::Region()); // creates a region in the newly created Island - if (!reg.infills_per_copy_ids.empty()) - for (unsigned int i=0; i& overrides = (iter ? reg.infills_overrides : reg.perimeters_overrides); - if (!reg.perimeters_per_copy_ids.empty()) - for (unsigned int i=0; iat(copy) == this_extruder_mark) // this copy should be printed with this extruder + 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, unsigned int object_copies_num) +{ + // 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* perimeters_or_infills_overrides = &infills_overrides; + + if (type == "perimeters") { + perimeters_or_infills = &perimeters; + perimeters_or_infills_overrides = &perimeters_overrides; + } + else + if (type != "infills") { + CONFESS("Unknown parameter!"); + return; + } + + + // First we append the entities, there are eec->entities.size() of them: + perimeters_or_infills->append(eec->entities); + + for (unsigned int i=0;ientities.size();++i) + perimeters_or_infills_overrides->push_back(copies_extruder); +} + } // namespace Slic3r diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index 35f80b578c..ad3f1e26b9 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -200,6 +200,7 @@ 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 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. @@ -215,15 +216,19 @@ protected: struct Region { ExtrusionEntityCollection perimeters; ExtrusionEntityCollection infills; - std::vector> infills_per_copy_ids; // indices of infill.entities that are not part of infill wiping (an element for each object copy) - std::vector> perimeters_per_copy_ids; // indices of infill.entities that are not part of infill wiping (an element for each object copy) + + std::vector infills_overrides; + std::vector perimeters_overrides; + + // 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, unsigned int object_copies_num); }; + std::vector by_region; // all extrusions for this island, grouped by regions - const std::vector& by_region_per_copy(unsigned int copy); // returns reference to subvector of by_region (only extrusions that are NOT printed during wiping into infill for this copy) + const std::vector& by_region_per_copy(unsigned int copy, int extruder, bool wiping_entities = false); // returns reference to subvector of by_region private: std::vector by_region_per_copy_cache; // caches vector generated by function above to avoid copying and recalculating - unsigned int last_copy = (unsigned int)(-1); // index of last copy that by_region_per_copy was called for }; std::vector islands; }; diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp index e0aa2b1c54..d2532d72d8 100644 --- a/xs/src/libslic3r/GCode/ToolOrdering.cpp +++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp @@ -330,4 +330,42 @@ void ToolOrdering::collect_extruder_statistics(bool prime_multi_material) } } + // This function is called from Print::mark_wiping_extrusions and sets extruder that it 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()))).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; + } + + + + // 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* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int 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()))).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 diff --git a/xs/src/libslic3r/GCode/ToolOrdering.hpp b/xs/src/libslic3r/GCode/ToolOrdering.hpp index c92806b19b..6dbb9715c6 100644 --- a/xs/src/libslic3r/GCode/ToolOrdering.hpp +++ b/xs/src/libslic3r/GCode/ToolOrdering.hpp @@ -10,6 +10,36 @@ namespace Slic3r { class Print; class PrintObject; + + +// Object of this class holds information about whether an extrusion is printed immediately +// after a toolchange (as part of infill/perimeter wiping) or not. One extrusion can be a part +// of several copies - this has to be taken into account. +class WipingExtrusions +{ + 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; + } + + // 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* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies); + +private: + std::map> entity_map; // to keep track of who prints what + bool something_overridden = false; +}; + + + class ToolOrdering { public: @@ -39,6 +69,11 @@ public: // and to support the wipe tower partitions above this one. size_t wipe_tower_partitions; coordf_t wipe_tower_layer_height; + + + // This holds list of extrusion that will be used for extruder wiping + WipingExtrusions wiping_extrusions; + }; ToolOrdering() {} @@ -72,7 +107,7 @@ public: std::vector::const_iterator begin() const { return m_layer_tools.begin(); } std::vector::const_iterator end() const { return m_layer_tools.end(); } bool empty() const { return m_layer_tools.empty(); } - const std::vector& layer_tools() const { return m_layer_tools; } + std::vector& layer_tools() { return m_layer_tools; } bool has_wipe_tower() const { return ! m_layer_tools.empty() && m_first_printing_extruder != (unsigned int)-1 && m_layer_tools.front().wipe_tower_partitions > 0; } private: diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index d448ab2f3d..1b0627f781 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -1121,21 +1121,14 @@ void Print::_make_wipe_tower() this->config.filament_ramming_parameters.get_at(i), this->config.nozzle_diameter.get_at(i)); - // When printing the first layer's wipe tower, the first extruder is expected to be active and primed. - // Therefore the number of wipe sections at the wipe tower will be (m_tool_ordering.front().extruders-1) at the 1st layer. - // The following variable is true if the last priming section cannot be squeezed inside the wipe tower. - bool last_priming_wipe_full = m_tool_ordering.front().extruders.size() > m_tool_ordering.front().wipe_tower_partitions; - m_wipe_tower_priming = Slic3r::make_unique( - wipe_tower.prime(this->skirt_first_layer_height(), m_tool_ordering.all_extruders(), ! last_priming_wipe_full)); - - reset_wiping_extrusions(); // if this is not the first time the wipe tower is generated, some extrusions might remember their last wiping status + wipe_tower.prime(this->skirt_first_layer_height(), m_tool_ordering.all_extruders(), false)); // Lets go through the wipe tower layers and determine pairs of extruder changes for each // to pass to wipe_tower (so that it can use it for planning the layout of the tower) { unsigned int current_extruder_id = m_tool_ordering.all_extruders().back(); - for (const auto &layer_tools : m_tool_ordering.layer_tools()) { // for all layers + for (auto &layer_tools : m_tool_ordering.layer_tools()) { // for all layers if (!layer_tools.has_wipe_tower) continue; bool first_layer = &layer_tools == &m_tool_ordering.front(); wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id,false); @@ -1180,111 +1173,100 @@ void Print::_make_wipe_tower() } - -void Print::reset_wiping_extrusions() { - for (size_t i = 0; i < objects.size(); ++ i) { - for (auto& this_layer : objects[i]->layers) { - for (size_t region_id = 0; region_id < objects[i]->print()->regions.size(); ++ region_id) { - for (unsigned int copy = 0; copy < objects[i]->_shifted_copies.size(); ++copy) { - this_layer->regions[region_id]->fills.set_extruder_override(copy, -1); - this_layer->regions[region_id]->perimeters.set_extruder_override(copy, -1); - } - } - } - } -} - - - -// Strategy for wiping (TODO): -// if !infill_first -// start with dedicated objects -// print a perimeter and its corresponding infill immediately after -// repeat until there are no dedicated objects left -// if there are some left and this is the last toolchange on the layer, mark all remaining extrusions of the object (so we don't have to travel back to it later) -// move to normal objects -// start with one object and start assigning its infill, if their perimeters ARE ALREADY EXTRUDED -// never touch perimeters -// -// if infill first -// start with dedicated objects -// print an infill and its corresponding perimeter immediately after -// repeat until you run out of infills -// move to normal objects -// start assigning infills (one copy after another) -// repeat until you run out of infills, leave perimeters be - - -float Print::mark_wiping_extrusions(const ToolOrdering::LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe) +// 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) { + // Strategy for wiping (TODO): + // if !infill_first + // start with dedicated objects + // print a perimeter and its corresponding infill immediately after + // repeat until there are no dedicated objects left + // if there are some left and this is the last toolchange on the layer, mark all remaining extrusions of the object (so we don't have to travel back to it later) + // move to normal objects + // start with one object and start assigning its infill, if their perimeters ARE ALREADY EXTRUDED + // never touch perimeters + // + // if infill first + // start with dedicated objects + // print an infill and its corresponding perimeter immediately after + // repeat until you run out of infills + // move to normal objects + // start assigning infills (one copy after another) + // repeat until you run out of infills, leave perimeters be + const float min_infill_volume = 0.f; // ignore infill with smaller volume than this - if (!config.filament_soluble.get_at(new_extruder)) { // Soluble filament cannot be wiped in a random infill - for (size_t i = 0; i < objects.size(); ++ i) { // Let's iterate through all objects... + if (config.filament_soluble.get_at(new_extruder)) + return volume_to_wipe; // Soluble filament cannot be wiped in a random infill - if (!objects[i]->config.wipe_into_infill && !objects[i]->config.wipe_into_objects) - continue; - Layer* this_layer = nullptr; - for (unsigned int a = 0; a < objects[i]->layers.size(); ++a) // Finds this layer - if (std::abs(layer_tools.print_z - objects[i]->layers[a]->print_z) < EPSILON) { - this_layer = objects[i]->layers[a]; - break; - } - if (this_layer == nullptr) - continue; - for (unsigned int copy = 0; copy < objects[i]->_shifted_copies.size(); ++copy) { // iterate through copies first, so that we mark neighbouring infills - for (size_t region_id = 0; region_id < objects[i]->print()->regions.size(); ++ region_id) { + for (size_t i = 0; i < objects.size(); ++ i) { // Let's iterate through all objects... + if (!objects[i]->config.wipe_into_infill && !objects[i]->config.wipe_into_objects) + continue; - unsigned int region_extruder = objects[i]->print()->regions[region_id]->config.infill_extruder - 1; // config value is 1-based - if (config.filament_soluble.get_at(region_extruder)) // if this infill is meant to be soluble, keep it that way + Layer* this_layer = nullptr; + for (unsigned int a = 0; a < objects[i]->layers.size(); ++a) // Finds this layer + if (std::abs(layer_tools.print_z - objects[i]->layers[a]->print_z) < EPSILON) { + this_layer = objects[i]->layers[a]; + break; + } + if (this_layer == nullptr) + continue; + + unsigned int num_of_copies = objects[i]->_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 < objects[i]->print()->regions.size(); ++ region_id) { + unsigned int region_extruder = objects[i]->print()->regions[region_id]->config.infill_extruder - 1; // config value is 1-based + if (config.filament_soluble.get_at(region_extruder)) // if this entity is meant to be soluble, keep it that way + continue; + + if (!config.infill_first) { // in this case we must verify that region_extruder was already used at this layer (and perimeters of the infill are therefore extruded) + 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] == region_extruder) + break; + } + if (unused_yet) continue; + } - if (!config.infill_first) { // in this case we must verify that region_extruder was already used at this layer (and perimeters of the infill are therefore extruded) - 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] == region_extruder) + if (objects[i]->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) break; - } - if (unused_yet) + auto* fill = dynamic_cast(ee); + if (fill->role() == erTopSolidInfill || fill->role() == erGapFill) // these cannot be changed - such infill is / may be visible continue; - } - - if (objects[i]->config.wipe_into_infill) { - ExtrusionEntityCollection& eec = this_layer->regions[region_id]->fills; - for (ExtrusionEntity* ee : eec.entities) { // iterate through all infill Collections - auto* fill = dynamic_cast(ee); - if (fill->role() == erTopSolidInfill || fill->role() == erGapFill) continue; // these cannot be changed - it is / may be visible - if (volume_to_wipe <= 0.f) - break; - if (!fill->is_extruder_overridden(copy) && fill->total_volume() > min_infill_volume) { // this infill will be used to wipe this extruder - fill->set_extruder_override(copy, new_extruder); - volume_to_wipe -= fill->total_volume(); - } + if (/*!fill->is_extruder_overridden(copy)*/ !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 (objects[i]->config.wipe_into_objects) - { - ExtrusionEntityCollection& eec = this_layer->regions[region_id]->perimeters; - for (ExtrusionEntity* ee : eec.entities) { // iterate through all perimeter Collections - auto* fill = dynamic_cast(ee); - if (volume_to_wipe <= 0.f) - break; - if (!fill->is_extruder_overridden(copy) && fill->total_volume() > min_infill_volume) { - fill->set_extruder_override(copy, new_extruder); - volume_to_wipe -= fill->total_volume(); - } + if (objects[i]->config.wipe_into_objects) + { + ExtrusionEntityCollection& eec = this_layer->regions[region_id]->perimeters; + for (ExtrusionEntity* ee : eec.entities) { // iterate through all perimeter Collections + if (volume_to_wipe <= 0.f) + break; + auto* fill = dynamic_cast(ee); + if (/*!fill->is_extruder_overridden(copy)*/ !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); } diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 77787063e7..57b1f4015c 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -317,10 +317,7 @@ private: // 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 ToolOrdering::LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe); - - // A function to go through all entities and unsets their extruder_override flag - void reset_wiping_extrusions(); + float mark_wiping_extrusions(ToolOrdering::LayerTools& layer_tools, unsigned int new_extruder, float volume_to_wipe); // Has the calculation been canceled? tbb::atomic m_canceled;