Skip to content

Commit

Permalink
Refactoring of GCode::process_layer().
Browse files Browse the repository at this point in the history
Refactoring of GCode export of color changes, extruder switches etc,
so that the "color change" like extruder switches are applied first
at the Wipe Tower / G-code export, so that adding / removing
an extruder switch at the G-code preview slider does not invalidate
slicing.
  • Loading branch information
bubnikv committed Jan 14, 2020
1 parent 79d7a01 commit 8bfc986
Show file tree
Hide file tree
Showing 13 changed files with 352 additions and 295 deletions.
3 changes: 0 additions & 3 deletions src/libslic3r/Extruder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ class Extruder
double retract_length_toolchange() const;
double retract_restart_extra_toolchange() const;

// Constructor for a key object, to be used by the stdlib search functions.
static Extruder key(unsigned int id) { return Extruder(id); }

private:
// Private constructor to create a key for a search in std::set.
Extruder(unsigned int id) : m_id(id) {}
Expand Down
365 changes: 219 additions & 146 deletions src/libslic3r/GCode.cpp

Large diffs are not rendered by default.

32 changes: 16 additions & 16 deletions src/libslic3r/GCode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,23 +197,25 @@ class GCode {
// append full config to the given string
static void append_full_config(const Print& print, std::string& str);

// Object and support extrusions of the same PrintObject at the same print_z.
// public, so that it could be accessed by free helper functions from GCode.cpp
struct LayerToPrint
{
LayerToPrint() : object_layer(nullptr), support_layer(nullptr) {}
const Layer* object_layer;
const SupportLayer* support_layer;
const Layer* layer() const { return (object_layer != nullptr) ? object_layer : support_layer; }
const PrintObject* object() const { return (this->layer() != nullptr) ? this->layer()->object() : nullptr; }
coordf_t print_z() const { return (object_layer != nullptr && support_layer != nullptr) ? 0.5 * (object_layer->print_z + support_layer->print_z) : this->layer()->print_z; }
};

private:
#if ENABLE_THUMBNAIL_GENERATOR
void _do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb);
void _do_export(Print &print, FILE *file, ThumbnailsGeneratorCallback thumbnail_cb);
#else
void _do_export(Print &print, FILE *file);
#endif //ENABLE_THUMBNAIL_GENERATOR

// Object and support extrusions of the same PrintObject at the same print_z.
struct LayerToPrint
{
LayerToPrint() : object_layer(nullptr), support_layer(nullptr) {}
const Layer *object_layer;
const SupportLayer *support_layer;
const Layer* layer() const { return (object_layer != nullptr) ? object_layer : support_layer; }
const PrintObject* object() const { return (this->layer() != nullptr) ? this->layer()->object() : nullptr; }
coordf_t print_z() const { return (object_layer != nullptr && support_layer != nullptr) ? 0.5 * (object_layer->print_z + support_layer->print_z) : this->layer()->print_z; }
};
static std::vector<LayerToPrint> collect_layers_to_print(const PrintObject &object);
static std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> collect_layers_to_print(const Print &print);
void process_layer(
Expand Down Expand Up @@ -372,11 +374,9 @@ class GCode {
bool m_second_layer_things_done;
// Index of a last object copy extruded.
std::pair<const PrintObject*, Point> m_last_obj_copy;
// Extensions for colorprint - now it's not a just color_print_heights,
// there can be some custom gcode.
// Updated before the export and erased during the process,
// so no toolchange occurs twice.
std::vector<Model::CustomGCode> m_custom_gcode_per_print_z;
// Iterator to Model::custom_gcode_per_print_z, which is being increased by process_layer.
// The Model::custom_gcode_per_print_z may contain color changes, extruder switches, pauses and custom G-codes.
std::vector<Model::CustomGCode>::const_iterator m_custom_gcode_per_print_z_it;

// Time estimators
GCodeTimeEstimator m_normal_time_estimator;
Expand Down
91 changes: 69 additions & 22 deletions src/libslic3r/GCode/ToolOrdering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,39 @@ bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const
return false;
}

// Return a zero based extruder from the region, or extruder_override if overriden.
unsigned int LayerTools::perimeter_extruder(const PrintRegion &region) const
{
assert(region.config().perimeter_extruder.value > 0);
return ((this->extruder_override == 0) ? region.config().perimeter_extruder.value : this->extruder_override) - 1;
}

unsigned int LayerTools::infill_extruder(const PrintRegion &region) const
{
assert(region.config().infill_extruder.value > 0);
return ((this->extruder_override == 0) ? region.config().infill_extruder.value : this->extruder_override) - 1;
}

unsigned int LayerTools::solid_infill_extruder(const PrintRegion &region) const
{
assert(region.config().solid_infill_extruder.value > 0);
return ((this->extruder_override == 0) ? region.config().solid_infill_extruder.value : this->extruder_override) - 1;
}

// Returns a zero based extruder this eec should be printed with, according to PrintRegion config or extruder_override if overriden.
unsigned int LayerTools::extruder(const ExtrusionEntityCollection &extrusions, const PrintRegion &region) const
{
assert(region.config().perimeter_extruder.value > 0);
assert(region.config().infill_extruder.value > 0);
assert(region.config().solid_infill_extruder.value > 0);
// 1 based extruder ID.
unsigned int extruder = ((this->extruder_override == 0) ?
(is_infill(extrusions.role()) ?
(is_solid_infill(extrusions.entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) :
region.config().perimeter_extruder.value) :
this->extruder_override);
return (extruder == 0) ? 0 : extruder - 1;
}

// For the use case when each object is printed separately
// (print.config().complete_objects is true).
Expand All @@ -54,7 +87,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
}

// Collect extruders reuqired to print the layers.
this->collect_extruders(object);
this->collect_extruders(object, nullptr);

// Reorder the extruders to minimize tool switches.
this->reorder_extruders(first_extruder);
Expand All @@ -66,7 +99,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude

// 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)
ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material, const std::vector<std::pair<double, unsigned int>> *per_layer_extruder_switches)
{
m_print_config_ptr = &print.config();

Expand All @@ -93,7 +126,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool

// Collect extruders reuqired to print the layers.
for (auto object : print.objects())
this->collect_extruders(*object);
this->collect_extruders(*object, (per_layer_extruder_switches != nullptr && ! per_layer_extruder_switches->empty()) ? per_layer_extruder_switches : nullptr);

// Reorder the extruders to minimize tool switches.
this->reorder_extruders(first_extruder);
Expand All @@ -119,7 +152,7 @@ void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs)
}

// Collect extruders reuqired to print layers.
void ToolOrdering::collect_extruders(const PrintObject &object)
void ToolOrdering::collect_extruders(const PrintObject &object, const std::vector<std::pair<double, unsigned int>> *per_layer_extruder_switches)
{
// Collect the support extruders.
for (auto support_layer : object.support_layers()) {
Expand All @@ -136,9 +169,25 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
if (has_support || has_interface)
layer_tools.has_support = true;
}

// Extruder overrides are ordered by print_z.
std::vector<std::pair<double, unsigned int>>::const_iterator it_per_layer_extruder_override;
if (per_layer_extruder_switches != nullptr)
it_per_layer_extruder_override = per_layer_extruder_switches->begin();
unsigned int extruder_override = 0;

// Collect the object extruders.
for (auto layer : object.layers()) {
LayerTools &layer_tools = this->tools_for_layer(layer->print_z);

// Override extruder with the next
if (per_layer_extruder_switches != nullptr)
for (; it_per_layer_extruder_override != per_layer_extruder_switches->end() && it_per_layer_extruder_override->first < layer->print_z + EPSILON; ++ it_per_layer_extruder_override)
extruder_override = (int)it_per_layer_extruder_override->second;

// Store the current extruder override (set to zero if no overriden), so that layer_tools.wiping_extrusions().is_overridable_and_mark() will use it.
layer_tools.extruder_override = extruder_override;

// 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;
Expand All @@ -159,12 +208,11 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
}

if (something_nonoverriddable)
layer_tools.extruders.push_back(region.config().perimeter_extruder.value);
layer_tools.extruders.emplace_back((extruder_override == 0) ? region.config().perimeter_extruder.value : extruder_override);

layer_tools.has_object = true;
}


bool has_infill = false;
bool has_solid_infill = false;
bool something_nonoverriddable = false;
Expand All @@ -183,12 +231,14 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
}
}

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 (something_nonoverriddable || !m_print_config_ptr) {
if (extruder_override == 0) {
if (has_solid_infill)
layer_tools.extruders.emplace_back(region.config().solid_infill_extruder);
if (has_infill)
layer_tools.extruders.emplace_back(region.config().infill_extruder);
} else if (has_solid_infill || has_infill)
layer_tools.extruders.emplace_back(extruder_override);
}
if (has_solid_infill || has_infill)
layer_tools.has_object = true;
Expand All @@ -201,7 +251,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object)

// 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
layer.extruders.emplace_back(0); // 0="dontcare" extruder - it will be taken care of in reorder_extruders
}
}

Expand Down Expand Up @@ -256,11 +306,9 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id)
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())
Expand Down Expand Up @@ -413,7 +461,7 @@ const LayerTools& ToolOrdering::tools_for_layer(coordf_t print_z) const
}

// 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)
void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, size_t copy_id, int extruder, size_t num_of_copies)
{
something_overridden = true;

Expand Down Expand Up @@ -449,11 +497,10 @@ int WipingExtrusions::last_nonsoluble_extruder_on_layer(const PrintConfig& print
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)))
if (print_config.filament_soluble.get_at(m_layer_tools->extruder(eec, region)))
return false;

if (object.config().wipe_into_objects)
Expand Down Expand Up @@ -522,7 +569,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
if (wipe_into_infill_only && ! print.config().infill_first)
// In this case we must check that the original extruder is used on this layer before the one we are overridding
// (and the perimeters will be finished before the infill is printed):
if (!lt.is_extruder_order(region.config().perimeter_extruder - 1, new_extruder))
if (!lt.is_extruder_order(lt.perimeter_extruder(region), new_extruder))
continue;

if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { // this infill will be used to wipe this extruder
Expand Down Expand Up @@ -597,8 +644,8 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
// 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
|| ! lt.has_extruder(region.config().infill_extruder - 1))) // we have to force override - this could violate infill_first (FIXME)
|| lt.is_extruder_order(lt.perimeter_extruder(region), last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints
|| ! lt.has_extruder(lt.infill_extruder(region)))) // 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.
Expand Down
19 changes: 15 additions & 4 deletions src/libslic3r/GCode/ToolOrdering.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class WipingExtrusions
int last_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const;

// This function is called from mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual)
void set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies);
void set_extruder_override(const ExtrusionEntity* entity, size_t copy_id, int extruder, size_t num_of_copies);

// Returns true in case that entity is not printed with its usual extruder for a given copy:
bool is_entity_overridden(const ExtrusionEntity* entity, size_t copy_id) const {
Expand All @@ -84,6 +84,7 @@ class LayerTools
print_z(z),
has_object(false),
has_support(false),
extruder_override(0),
has_wipe_tower(false),
wipe_tower_partitions(0),
wipe_tower_layer_height(0.) {}
Expand All @@ -96,11 +97,21 @@ class LayerTools
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(); }

// Return a zero based extruder from the region, or extruder_override if overriden.
unsigned int perimeter_extruder(const PrintRegion &region) const;
unsigned int infill_extruder(const PrintRegion &region) const;
unsigned int solid_infill_extruder(const PrintRegion &region) const;
// Returns a zero based extruder this eec should be printed with, according to PrintRegion config or extruder_override if overriden.
unsigned int extruder(const ExtrusionEntityCollection &extrusions, const PrintRegion &region) const;

coordf_t print_z;
bool has_object;
bool has_support;
// Zero based extruder IDs, ordered to minimize tool switches.
std::vector<unsigned int> extruders;
// If per layer extruder switches are inserted by the G-code preview slider, this value contains the new (1 based) extruder, with which the whole object layer is being printed with.
// If not overriden, it is set to 0.
unsigned int extruder_override;
// Will there be anything extruded on this layer for the wipe tower?
// Due to the support layers possibly interleaving the object layers,
// wipe tower will be disabled for some support only layers.
Expand Down Expand Up @@ -129,11 +140,11 @@ class ToolOrdering

// For the use case when each object is printed separately
// (print.config.complete_objects is true).
ToolOrdering(const PrintObject &object, unsigned int first_extruder = (unsigned int)-1, bool prime_multi_material = false);
ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material = false);

// For the use case when all objects are printed at once.
// (print.config.complete_objects is false).
ToolOrdering(const Print &print, unsigned int first_extruder = (unsigned int)-1, bool prime_multi_material = false);
ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material = false, const std::vector<std::pair<double, unsigned int>> *per_layer_extruder_switches = nullptr);

void clear() { m_layer_tools.clear(); }

Expand All @@ -160,7 +171,7 @@ class ToolOrdering

private:
void initialize_layers(std::vector<coordf_t> &zs);
void collect_extruders(const PrintObject &object);
void collect_extruders(const PrintObject &object, const std::vector<std::pair<double, unsigned int>> *per_layer_extruder_switches);
void reorder_extruders(unsigned int last_extruder_id);
void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z);
void collect_extruder_statistics(bool prime_multi_material);
Expand Down
10 changes: 5 additions & 5 deletions src/libslic3r/GCodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config)
this->config.apply(print_config, true);
m_extrusion_axis = this->config.get_extrusion_axis();
m_single_extruder_multi_material = print_config.single_extruder_multi_material.value;
m_max_acceleration = (print_config.gcode_flavor.value == gcfMarlin) ?
print_config.machine_max_acceleration_extruding.values.front() : 0;
m_max_acceleration = std::lrint((print_config.gcode_flavor.value == gcfMarlin) ?
print_config.machine_max_acceleration_extruding.values.front() : 0);
}

void GCodeWriter::set_extruders(const std::vector<unsigned int> &extruder_ids)
Expand Down Expand Up @@ -247,9 +247,9 @@ std::string GCodeWriter::toolchange_prefix() const
std::string GCodeWriter::toolchange(unsigned int extruder_id)
{
// set the new extruder
auto it_extruder = std::lower_bound(m_extruders.begin(), m_extruders.end(), Extruder::key(extruder_id));
assert(it_extruder != m_extruders.end());
m_extruder = const_cast<Extruder*>(&*it_extruder);
auto it_extruder = Slic3r::lower_bound_by_predicate(m_extruders.begin(), m_extruders.end(), [extruder_id](const Extruder &e) { return e.id() < extruder_id; });
assert(it_extruder != m_extruders.end() && it_extruder->id() == extruder_id);
m_extruder = &*it_extruder;

// return the toolchange command
// if we are running a single-extruder setup, just set the extruder and return nothing
Expand Down
3 changes: 2 additions & 1 deletion src/libslic3r/GCodeWriter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ class GCodeWriter {
Vec3d get_position() const { return m_pos; }

private:
std::vector<Extruder> m_extruders;
// Extruders are sorted by their ID, so that binary search is possible.
std::vector<Extruder> m_extruders;
std::string m_extrusion_axis;
bool m_single_extruder_multi_material;
Extruder* m_extruder;
Expand Down
Loading

0 comments on commit 8bfc986

Please sign in to comment.