Refactoring of perimeters/infills wiping (ToolOrdering::WipingExtrusions now takes care of the agenda)

Squashed commit of the following:

commit 931eb2684103e8571b4a2e9804765fef268361c3
Author: Lukas Matena <lukasmatena@seznam.cz>
Date:   Wed Jun 20 12:50:27 2018 +0200

    ToolOrdering::WipingExtrusions now holds all information necessary for infill/perimeter wiping

commit cc8becfbdd771f7e279434c8bd6be147e4b321ee
Author: Lukas Matena <lukasmatena@seznam.cz>
Date:   Tue Jun 19 10:52:03 2018 +0200

    Wiping is now done as normal print would be (less extra code in process_layer)

commit 1b120754b0691cce46ee5e10f3840480c559ac1f
Author: Lukas Matena <lukasmatena@seznam.cz>
Date:   Fri Jun 15 15:55:15 2018 +0200

    Refactoring: ObjectByExtruder changed so that it is aware of the wiping extrusions

commit 1641e326bb5e0a0c69d6bfc6efa23153dc2e4543
Author: Lukas Matena <lukasmatena@seznam.cz>
Date:   Thu Jun 14 12:22:18 2018 +0200

    Refactoring: new class WipingExtrusion in ToolOrdering.hpp
This commit is contained in:
Lukas Matena 2018-06-20 12:52:00 +02:00
parent 29dd305aaa
commit 8a47852be2
8 changed files with 301 additions and 303 deletions

View file

@ -1147,7 +1147,6 @@ void GCode::process_layer(
// Group extrusions by an extruder, then by an object, an island and a region.
std::map<unsigned int, std::vector<ObjectByExtruder>> 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 &region = *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<const ExtrusionEntityCollection*>(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<ObjectByExtruder::Island> &islands = object_islands_by_extruder(
by_extruder,
std::max<int>(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<unsigned int>());
if (!perimeter_coll->is_extruder_overridden(copy_id))
for (int j=first_added_entity_index; j<islands[i].by_region[region_id].perimeters.entities.size(); ++j)
islands[i].by_region[region_id].perimeters_per_copy_ids[copy_id].push_back(j);
// 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.
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<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
std::max<int>(region.config.perimeter_extruder.value - 1, 0);
// Let's recover vector of extruder overrides:
const ExtruderPerCopy* entity_overrides = const_cast<ToolOrdering::LayerTools&>(layer_tools).wiping_extrusions.get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->_shifted_copies.size());
// 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<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)
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<const ExtrusionEntityCollection*>(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<int>(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<ObjectByExtruder::Island> &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<unsigned int>());
if (!fill->is_extruder_overridden(copy_id))
for (int j=first_added_entity_index; j<islands[i].by_region[region_id].infills.entities.size(); ++j)
islands[i].by_region[region_id].infills_per_copy_ids[copy_id].push_back(j);
}
break;
}
}
}
} // for regions
}
} // for objects
// Extrude the skirt, brim, support, perimeters, infill ordered by the extruders.
std::vector<std::unique_ptr<EdgeGrid::Grid>> lower_layer_edge_grids(layers.size());
for (unsigned int extruder_id : 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<ObjectByExtruder::Island::Region> 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<ExtrusionEntityCollection*>(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<ExtrusionEntityCollection*>(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<EdgeGrid::Grid> 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 &copy : copies) {
// When starting a new object, use the external motion planner for the first travel move.
std::pair<const PrintObject*, Point> 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 &copy : copies) {
// When starting a new object, use the external motion planner for the first travel move.
std::pair<const PrintObject*, Point> 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::Region>& 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::Region>& 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<reg.infills_per_copy_ids[copy].size(); ++i)
by_region_per_copy_cache.back().infills.append(*(reg.infills.entities[reg.infills_per_copy_ids[copy][i]]));
// 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);
if (!reg.perimeters_per_copy_ids.empty())
for (unsigned int i=0; i<reg.perimeters_per_copy_ids[copy].size(); ++i)
by_region_per_copy_cache.back().perimeters.append(*(reg.perimeters.entities[reg.perimeters_per_copy_ids[copy][i]]));
// 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]));
}
}
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<const ExtruderPerCopy*>* 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;i<eec->entities.size();++i)
perimeters_or_infills_overrides->push_back(copies_extruder);
}
} // namespace Slic3r