Refactoring of GCode::process_layer().

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.
This commit is contained in:
bubnikv 2020-01-14 10:31:18 +01:00
parent 79d7a0130f
commit 8bfc986fa7
13 changed files with 352 additions and 295 deletions

View file

@ -48,9 +48,6 @@ public:
double retract_length_toolchange() const; double retract_length_toolchange() const;
double retract_restart_extra_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:
// Private constructor to create a key for a search in std::set. // Private constructor to create a key for a search in std::set.
Extruder(unsigned int id) : m_id(id) {} Extruder(unsigned int id) : m_id(id) {}

View file

@ -1133,11 +1133,9 @@ void GCode::_do_export(Print& print, FILE* file)
m_enable_cooling_markers = true; m_enable_cooling_markers = true;
this->apply_print_config(print.config()); this->apply_print_config(print.config());
this->set_extruders(print.extruders());
// Initialize custom gcode // Initialize custom gcode iterator.
Model* model = print.get_object(0)->model_object()->get_model(); m_custom_gcode_per_print_z_it = print.model().custom_gcode_per_print_z.cbegin();
m_custom_gcode_per_print_z = model->custom_gcode_per_print_z;
m_volumetric_speed = DoExport::autospeed_volumetric_limit(print); m_volumetric_speed = DoExport::autospeed_volumetric_limit(print);
print.throw_if_canceled(); print.throw_if_canceled();
@ -1221,18 +1219,27 @@ void GCode::_do_export(Print& print, FILE* file)
if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1) if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1)
break; break;
} }
// We don't allow switching of extruders per layer by Model::custom_gcode_per_print_z in sequential mode.
// Use the extruder IDs collected from Regions.
this->set_extruders(print.extruders());
} else { } else {
// Find tool ordering for all the objects at once, and the initial extruder ID. // Find tool ordering for all the objects at once, and the initial extruder ID.
// If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it. // If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it.
tool_ordering = print.wipe_tower_data().tool_ordering.empty() ? tool_ordering = print.wipe_tower_data().tool_ordering.empty() ?
ToolOrdering(print, initial_extruder_id) : ToolOrdering(print, initial_extruder_id, false,
// Use the extruder switches from Model::custom_gcode_per_print_z to override the extruder to print the object.
// Do it only if all the objects were configured to be printed with a single extruder.
(print.object_extruders().size() == 1) ? &custom_tool_changes(print.model(), (unsigned int)m_config.nozzle_diameter.size()) : nullptr) :
print.wipe_tower_data().tool_ordering; print.wipe_tower_data().tool_ordering;
has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower(); has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower();
initial_extruder_id = (has_wipe_tower && ! print.config().single_extruder_multi_material_priming) ? initial_extruder_id = (has_wipe_tower && ! print.config().single_extruder_multi_material_priming) ?
// The priming towers will be skipped. // The priming towers will be skipped.
tool_ordering.all_extruders().back() : tool_ordering.all_extruders().back() :
// Don't skip the priming towers. // Don't skip the priming towers.
tool_ordering.first_extruder(); tool_ordering.first_extruder();
// In non-sequential print, the printing extruders may have been modified by the extruder switches stored in Model::custom_gcode_per_print_z.
// Therefore initialize the printing extruders from there.
this->set_extruders(tool_ordering.all_extruders());
} }
if (initial_extruder_id == (unsigned int)-1) { if (initial_extruder_id == (unsigned int)-1) {
// Nothing to print! // Nothing to print!
@ -1247,7 +1254,7 @@ void GCode::_do_export(Print& print, FILE* file)
// #ys_FIXME_no_exported_codes // #ys_FIXME_no_exported_codes
/* /*
/* To avoid change filament for non-used extruder for Multi-material, /* To avoid change filament for non-used extruder for Multi-material,
* check model->custom_gcode_per_print_z using tool_ordering values * check print.model().custom_gcode_per_print_z using tool_ordering values
* / * /
if (!m_custom_gcode_per_print_z. empty()) if (!m_custom_gcode_per_print_z. empty())
{ {
@ -1283,7 +1290,7 @@ void GCode::_do_export(Print& print, FILE* file)
} }
if (delete_executed) if (delete_executed)
model->custom_gcode_per_print_z = m_custom_gcode_per_print_z; print.model().custom_gcode_per_print_z = m_custom_gcode_per_print_z;
} }
*/ */
@ -1768,6 +1775,174 @@ std::vector<GCode::InstanceToPrint> GCode::sort_print_object_instances(
return out; return out;
} }
namespace ProcessLayer
{
std::string emit_custom_gcode_per_print_z(
// Last processed CustomGCode.
std::vector<Model::CustomGCode>::const_iterator &custom_gcode_per_print_z_it,
const std::vector<Model::CustomGCode>::const_iterator custom_gcode_per_print_z_end,
// This layer's print_z.
coordf_t current_print_z,
// ID of the first extruder printing this layer.
unsigned int first_extruder_id,
size_t num_extruders)
{
// Let's issue a filament change command if requested at this layer.
// In case there are more toolchange requests that weren't done yet and should happen simultaneously, erase them all.
// (Layers can be close to each other, model could have been resliced with bigger layer height, ...).
bool has_colorchange = false;
std::string custom_code;
std::string pause_print_msg;
int m600_before_extruder = -1;
while (custom_gcode_per_print_z_it != custom_gcode_per_print_z_end) {
auto it_next = custom_gcode_per_print_z_it;
if ((++ it_next)->print_z >= current_print_z + EPSILON)
break;
custom_gcode_per_print_z_it = it_next;
}
if (custom_gcode_per_print_z_it != custom_gcode_per_print_z_end && custom_gcode_per_print_z_it->print_z < current_print_z + EPSILON) {
custom_code = custom_gcode_per_print_z_it->gcode;
if (custom_code == ColorChangeCode && custom_gcode_per_print_z_it->extruder > 0)
m600_before_extruder = custom_gcode_per_print_z_it->extruder - 1;
if (custom_code == PausePrintCode)
pause_print_msg = custom_gcode_per_print_z_it->color;
// This color change is consumed, don't use it again.
++ custom_gcode_per_print_z_it;
has_colorchange = true;
}
// we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count
// don't save "tool_change"(ExtruderChangeCode) code to GCode
std::string gcode;
if (has_colorchange && custom_code != ExtruderChangeCode) {
const bool single_material_print = num_extruders == 1;
if (custom_code == ColorChangeCode) // color change
{
// add tag for analyzer
gcode += "; " + GCodeAnalyzer::Color_Change_Tag + ",T" + std::to_string(m600_before_extruder) + "\n";
// add tag for time estimator
gcode += "; " + GCodeTimeEstimator::Color_Change_Tag + "\n";
if (!single_material_print && m600_before_extruder >= 0 && first_extruder_id != m600_before_extruder
// && !MMU1
) {
//! FIXME_in_fw show message during print pause
gcode += "M601\n"; // pause print
gcode += "M117 Change filament for Extruder " + std::to_string(m600_before_extruder) + "\n";
}
else
gcode += custom_code + "\n";
}
else
{
if (custom_code == PausePrintCode) // Pause print
{
// add tag for analyzer
gcode += "; " + GCodeAnalyzer::Pause_Print_Tag + "\n";
//! FIXME_in_fw show message during print pause
if (!pause_print_msg.empty())
gcode += "M117 " + pause_print_msg + "\n";
// add tag for time estimator
//gcode += "; " + GCodeTimeEstimator::Pause_Print_Tag + "\n";
}
else // custom Gcode
{
// add tag for analyzer
gcode += "; " + GCodeAnalyzer::Custom_Code_Tag + "\n";
// add tag for time estimator
//gcode += "; " + GCodeTimeEstimator::Custom_Code_Tag + "\n";
}
gcode += custom_code + "\n";
}
}
return gcode;
}
} // namespace ProcessLayer
namespace Skirt {
std::map<unsigned int, std::pair<size_t, size_t>> make_skirt_loops_per_extruder_1st_layer(
const Print &print,
const std::vector<GCode::LayerToPrint> &layers,
const LayerTools &layer_tools,
std::vector<unsigned int> extruder_ids,
// Heights at which the skirt has already been extruded.
std::vector<coordf_t> &skirt_done)
{
// Extrude skirt at the print_z of the raft layers and normal object layers
// not at the print_z of the interlaced support material layers.
std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder_out;
assert(skirt_done.empty());
if (print.has_skirt() && ! print.skirt().entities.empty()) {
// Prime all the printing extruders over the skirt lines.
// Reorder the extruders, so that the last used extruder is at the front.
unsigned int first_extruder_id = layer_tools.extruders.front();
for (size_t i = 1; i < extruder_ids.size(); ++ i)
if (extruder_ids[i] == first_extruder_id) {
// Move the last extruder to the front.
memmove(extruder_ids.data() + 1, extruder_ids.data(), i * sizeof(unsigned int));
extruder_ids.front() = first_extruder_id;
break;
}
size_t n_loops = print.skirt().entities.size();
if (n_loops <= extruder_ids.size()) {
for (size_t i = 0; i < n_loops; ++i)
skirt_loops_per_extruder_out[extruder_ids[i]] = std::pair<size_t, size_t>(i, i + 1);
} else {
// Assign skirt loops to the extruders.
std::vector<unsigned int> extruder_loops(extruder_ids.size(), 1);
n_loops -= extruder_loops.size();
while (n_loops > 0) {
for (size_t i = 0; i < extruder_ids.size() && n_loops > 0; ++i, --n_loops)
++extruder_loops[i];
}
for (size_t i = 0; i < extruder_ids.size(); ++i)
skirt_loops_per_extruder_out[extruder_ids[i]] = std::make_pair<size_t, size_t>(
(i == 0) ? 0 : extruder_loops[i - 1],
((i == 0) ? 0 : extruder_loops[i - 1]) + extruder_loops[i]);
}
skirt_done.emplace_back(layer_tools.print_z - (skirt_done.empty() ? 0. : skirt_done.back()));
}
return skirt_loops_per_extruder_out;
}
std::map<unsigned int, std::pair<size_t, size_t>> make_skirt_loops_per_extruder_other_layers(
const Print &print,
const std::vector<GCode::LayerToPrint> &layers,
const LayerTools &layer_tools,
// Heights at which the skirt has already been extruded.
std::vector<coordf_t> &skirt_done)
{
// Extrude skirt at the print_z of the raft layers and normal object layers
// not at the print_z of the interlaced support material layers.
std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder_out;
if (print.has_skirt() && ! print.skirt().entities.empty() &&
// Not enough skirt layers printed yet.
//FIXME infinite or high skirt does not make sense for sequential print!
(skirt_done.size() < (size_t)print.config().skirt_height.value || print.has_infinite_skirt()) &&
// This print_z has not been extruded yet
skirt_done.back() < layer_tools.print_z - EPSILON &&
// and this layer is an object layer, or it is a raft layer.
//FIXME one uses the number of raft layers from the 1st object!
(layer_tools.has_object || layers.front().support_layer->id() < (size_t)layers.front().support_layer->object()->config().raft_layers.value)) {
// Extrude all skirts with the current extruder.
unsigned int first_extruder_id = layer_tools.extruders.front();
skirt_loops_per_extruder_out[first_extruder_id] = std::pair<size_t, size_t>(0, print.config().skirts.value);
assert(!skirt_done.empty());
skirt_done.emplace_back(layer_tools.print_z - skirt_done.back());
}
return skirt_loops_per_extruder_out;
}
} // namespace Skirt
// In sequential mode, process_layer is called once per each object and its copy, // In sequential mode, process_layer is called once per each object and its copy,
// therefore layers will contain a single entry and single_object_instance_idx will point to the copy of the object. // therefore layers will contain a single entry and single_object_instance_idx will point to the copy of the object.
// In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated. // In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated.
@ -1804,7 +1979,7 @@ void GCode::process_layer(
if (l.support_layer != nullptr && support_layer == nullptr) if (l.support_layer != nullptr && support_layer == nullptr)
support_layer = l.support_layer; support_layer = l.support_layer;
} }
const Layer &layer = (object_layer != nullptr) ? *object_layer : *support_layer; const Layer &layer = (object_layer != nullptr) ? *object_layer : *support_layer;
coordf_t print_z = layer.print_z; coordf_t print_z = layer.print_z;
bool first_layer = layer.id() == 0; bool first_layer = layer.id() == 0;
unsigned int first_extruder_id = layer_tools.extruders.front(); unsigned int first_extruder_id = layer_tools.extruders.front();
@ -1868,120 +2043,21 @@ void GCode::process_layer(
m_second_layer_things_done = true; m_second_layer_things_done = true;
} }
// Let's issue a filament change command if requested at this layer. // Map from extruder ID to <begin, end> index of skirt loops to be extruded with that extruder.
// In case there are more toolchange requests that weren't done yet and should happen simultaneously, erase them all.
// (Layers can be close to each other, model could have been resliced with bigger layer height, ...).
bool colorprint_change = false;
std::string custom_code = "";
std::string pause_print_msg = "";
int m600_before_extruder = -1;
while (!m_custom_gcode_per_print_z.empty() && m_custom_gcode_per_print_z.front().print_z - EPSILON < layer.print_z) {
custom_code = m_custom_gcode_per_print_z.front().gcode;
if (custom_code == ColorChangeCode && m_custom_gcode_per_print_z.front().extruder > 0)
m600_before_extruder = m_custom_gcode_per_print_z.front().extruder - 1;
if (custom_code == PausePrintCode)
pause_print_msg = m_custom_gcode_per_print_z.front().color;
m_custom_gcode_per_print_z.erase(m_custom_gcode_per_print_z.begin());
colorprint_change = true;
}
// we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count
// don't save "tool_change"(ExtruderChangeCode) code to GCode
if (colorprint_change && custom_code != ExtruderChangeCode) {
const bool single_material_print = print.config().nozzle_diameter.size() == 1;
if (custom_code == ColorChangeCode) // color change
{
// add tag for analyzer
gcode += "; " + GCodeAnalyzer::Color_Change_Tag + ",T" + std::to_string(m600_before_extruder) + "\n";
// add tag for time estimator
gcode += "; " + GCodeTimeEstimator::Color_Change_Tag + "\n";
if (!single_material_print && m600_before_extruder >= 0 && first_extruder_id != m600_before_extruder
// && !MMU1
) {
//! FIXME_in_fw show message during print pause
gcode += "M601\n"; // pause print
gcode += "M117 Change filament for Extruder " + std::to_string(m600_before_extruder) + "\n";
}
else
gcode += custom_code + "\n";
}
else
{
if (custom_code == PausePrintCode) // Pause print
{
// add tag for analyzer
gcode += "; " + GCodeAnalyzer::Pause_Print_Tag + "\n";
//! FIXME_in_fw show message during print pause
if (!pause_print_msg.empty())
gcode += "M117 " + pause_print_msg + "\n";
// add tag for time estimator
//gcode += "; " + GCodeTimeEstimator::Pause_Print_Tag + "\n";
}
else // custom Gcode
{
// add tag for analyzer
gcode += "; " + GCodeAnalyzer::Custom_Code_Tag + "\n";
// add tag for time estimator
//gcode += "; " + GCodeTimeEstimator::Custom_Code_Tag + "\n";
}
gcode += custom_code + "\n";
}
}
// Extrude skirt at the print_z of the raft layers and normal object layers
// not at the print_z of the interlaced support material layers.
bool extrude_skirt =
! print.skirt().entities.empty() &&
// Not enough skirt layers printed yet.
(m_skirt_done.size() < (size_t)print.config().skirt_height.value || print.has_infinite_skirt()) &&
// This print_z has not been extruded yet
(m_skirt_done.empty() ? 0. : m_skirt_done.back()) < print_z - EPSILON &&
// and this layer is the 1st layer, or it is an object layer, or it is a raft layer.
(first_layer || object_layer != nullptr || support_layer->id() < (size_t)m_config.raft_layers.value);
std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder; std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder;
coordf_t skirt_height = 0.;
if (extrude_skirt) { if (single_object_instance_idx == size_t(-1) && object_layer != nullptr) {
// Fill in skirt_loops_per_extruder. // Normal (non-sequential) print.
skirt_height = print_z - (m_skirt_done.empty() ? 0. : m_skirt_done.back()); gcode += ProcessLayer::emit_custom_gcode_per_print_z(
m_skirt_done.push_back(print_z); // input / output
if (first_layer) { m_custom_gcode_per_print_z_it,
// Prime the extruders over the skirt lines. // inputs
std::vector<unsigned int> extruder_ids = m_writer.extruder_ids(); print.model().custom_gcode_per_print_z.cend(), layer.print_z, first_extruder_id, print.config().nozzle_diameter.size());
// Reorder the extruders, so that the last used extruder is at the front. // Extrude skirt at the print_z of the raft layers and normal object layers
for (size_t i = 1; i < extruder_ids.size(); ++ i) // not at the print_z of the interlaced support material layers.
if (extruder_ids[i] == first_extruder_id) { skirt_loops_per_extruder = first_layer ?
// Move the last extruder to the front. Skirt::make_skirt_loops_per_extruder_1st_layer(print, layers, layer_tools, m_writer.extruder_ids(), m_skirt_done) :
memmove(extruder_ids.data() + 1, extruder_ids.data(), i * sizeof(unsigned int)); Skirt::make_skirt_loops_per_extruder_other_layers(print, layers, layer_tools, m_skirt_done);
extruder_ids.front() = first_extruder_id;
break;
}
size_t n_loops = print.skirt().entities.size();
if (n_loops <= extruder_ids.size()) {
for (size_t i = 0; i < n_loops; ++i)
skirt_loops_per_extruder[extruder_ids[i]] = std::pair<size_t, size_t>(i, i + 1);
} else {
// Assign skirt loops to the extruders.
std::vector<unsigned int> extruder_loops(extruder_ids.size(), 1);
n_loops -= extruder_loops.size();
while (n_loops > 0) {
for (size_t i = 0; i < extruder_ids.size() && n_loops > 0; ++ i, -- n_loops)
++ extruder_loops[i];
}
for (size_t i = 0; i < extruder_ids.size(); ++ i)
skirt_loops_per_extruder[extruder_ids[i]] = std::make_pair<size_t, size_t>(
(i == 0) ? 0 : extruder_loops[i - 1],
((i == 0) ? 0 : extruder_loops[i - 1]) + extruder_loops[i]);
}
} else
// Extrude all skirts with the current extruder.
skirt_loops_per_extruder[first_extruder_id] = std::pair<size_t, size_t>(0, print.config().skirts.value);
} }
// Group extrusions by an extruder, then by an object, an island and a region. // Group extrusions by an extruder, then by an object, an island and a region.
@ -2085,7 +2161,7 @@ void GCode::process_layer(
continue; continue;
// This extrusion is part of certain Region, which tells us which extruder should be used for it: // This extrusion is part of certain Region, which tells us which extruder should be used for it:
int correct_extruder_id = Print::get_extruder(*extrusions, region); int correct_extruder_id = layer_tools.extruder(*extrusions, region);
// Let's recover vector of extruder overrides: // Let's recover vector of extruder overrides:
const WipingExtrusions::ExtruderPerCopy *entity_overrides = nullptr; const WipingExtrusions::ExtruderPerCopy *entity_overrides = nullptr;
@ -2152,30 +2228,27 @@ void GCode::process_layer(
if (m_enable_analyzer && layer_tools.has_wipe_tower && m_wipe_tower) if (m_enable_analyzer && layer_tools.has_wipe_tower && m_wipe_tower)
m_last_analyzer_extrusion_role = erWipeTower; m_last_analyzer_extrusion_role = erWipeTower;
if (extrude_skirt) { if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) {
auto loops_it = skirt_loops_per_extruder.find(extruder_id); const std::pair<size_t, size_t> loops = loops_it->second;
if (loops_it != skirt_loops_per_extruder.end()) { this->set_origin(0., 0.);
const std::pair<size_t, size_t> loops = loops_it->second; m_avoid_crossing_perimeters.use_external_mp = true;
this->set_origin(0.,0.); Flow layer_skirt_flow(print.skirt_flow());
m_avoid_crossing_perimeters.use_external_mp = true; layer_skirt_flow.height = (float)(m_skirt_done.back() - ((m_skirt_done.size() == 1) ? 0. : m_skirt_done[m_skirt_done.size() - 2]));
Flow skirt_flow = print.skirt_flow(); double mm3_per_mm = layer_skirt_flow.mm3_per_mm();
for (size_t i = loops.first; i < loops.second; ++ i) { for (size_t i = loops.first; i < loops.second; ++i) {
// Adjust flow according to this layer's layer height. // Adjust flow according to this layer's layer height.
ExtrusionLoop loop = *dynamic_cast<const ExtrusionLoop*>(print.skirt().entities[i]); ExtrusionLoop loop = *dynamic_cast<const ExtrusionLoop*>(print.skirt().entities[i]);
Flow layer_skirt_flow(skirt_flow); for (ExtrusionPath &path : loop.paths) {
layer_skirt_flow.height = (float)skirt_height; path.height = layer_skirt_flow.height;
double mm3_per_mm = layer_skirt_flow.mm3_per_mm(); path.mm3_per_mm = mm3_per_mm;
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; //FIXME using the support_material_speed of the 1st object printed.
// Allow a straight travel move to the first object point if this is the first layer (but don't in next layers). gcode += this->extrude_loop(loop, "skirt", m_config.support_material_speed.value);
if (first_layer && loops.first == 0)
m_avoid_crossing_perimeters.disable_once = true;
} }
m_avoid_crossing_perimeters.use_external_mp = false;
// Allow a straight travel move to the first object point if this is the first layer (but don't in next layers).
if (first_layer && loops.first == 0)
m_avoid_crossing_perimeters.disable_once = true;
} }
// Extrude brim with the extruder of the 1st region. // Extrude brim with the extruder of the 1st region.

View file

@ -197,23 +197,25 @@ public:
// append full config to the given string // append full config to the given string
static void append_full_config(const Print& print, std::string& str); 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: private:
#if ENABLE_THUMBNAIL_GENERATOR #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 #else
void _do_export(Print &print, FILE *file); void _do_export(Print &print, FILE *file);
#endif //ENABLE_THUMBNAIL_GENERATOR #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<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); static std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> collect_layers_to_print(const Print &print);
void process_layer( void process_layer(
@ -372,11 +374,9 @@ private:
bool m_second_layer_things_done; bool m_second_layer_things_done;
// Index of a last object copy extruded. // Index of a last object copy extruded.
std::pair<const PrintObject*, Point> m_last_obj_copy; std::pair<const PrintObject*, Point> m_last_obj_copy;
// Extensions for colorprint - now it's not a just color_print_heights, // Iterator to Model::custom_gcode_per_print_z, which is being increased by process_layer.
// there can be some custom gcode. // The Model::custom_gcode_per_print_z may contain color changes, extruder switches, pauses and custom G-codes.
// Updated before the export and erased during the process, std::vector<Model::CustomGCode>::const_iterator m_custom_gcode_per_print_z_it;
// so no toolchange occurs twice.
std::vector<Model::CustomGCode> m_custom_gcode_per_print_z;
// Time estimators // Time estimators
GCodeTimeEstimator m_normal_time_estimator; GCodeTimeEstimator m_normal_time_estimator;

View file

@ -34,6 +34,39 @@ bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const
return false; 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 // For the use case when each object is printed separately
// (print.config().complete_objects is true). // (print.config().complete_objects is true).
@ -54,7 +87,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
} }
// Collect extruders reuqired to print the layers. // Collect extruders reuqired to print the layers.
this->collect_extruders(object); this->collect_extruders(object, nullptr);
// Reorder the extruders to minimize tool switches. // Reorder the extruders to minimize tool switches.
this->reorder_extruders(first_extruder); this->reorder_extruders(first_extruder);
@ -66,7 +99,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
// For the use case when all objects are printed at once. // For the use case when all objects are printed at once.
// (print.config().complete_objects is false). // (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(); m_print_config_ptr = &print.config();
@ -93,7 +126,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
// Collect extruders reuqired to print the layers. // Collect extruders reuqired to print the layers.
for (auto object : print.objects()) 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. // Reorder the extruders to minimize tool switches.
this->reorder_extruders(first_extruder); this->reorder_extruders(first_extruder);
@ -119,7 +152,7 @@ void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs)
} }
// Collect extruders reuqired to print layers. // 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. // Collect the support extruders.
for (auto support_layer : object.support_layers()) { for (auto support_layer : object.support_layers()) {
@ -136,9 +169,25 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
if (has_support || has_interface) if (has_support || has_interface)
layer_tools.has_support = true; 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. // Collect the object extruders.
for (auto layer : object.layers()) { for (auto layer : object.layers()) {
LayerTools &layer_tools = this->tools_for_layer(layer->print_z); 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? // What extruders are required to print this object layer?
for (size_t region_id = 0; region_id < object.region_volumes.size(); ++ region_id) { 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; const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr;
@ -159,12 +208,11 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
} }
if (something_nonoverriddable) 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; layer_tools.has_object = true;
} }
bool has_infill = false; bool has_infill = false;
bool has_solid_infill = false; bool has_solid_infill = false;
bool something_nonoverriddable = false; bool something_nonoverriddable = false;
@ -183,12 +231,14 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
} }
} }
if (something_nonoverriddable || !m_print_config_ptr) if (something_nonoverriddable || !m_print_config_ptr) {
{ if (extruder_override == 0) {
if (has_solid_infill) if (has_solid_infill)
layer_tools.extruders.push_back(region.config().solid_infill_extruder); layer_tools.extruders.emplace_back(region.config().solid_infill_extruder);
if (has_infill) if (has_infill)
layer_tools.extruders.push_back(region.config().infill_extruder); 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) if (has_solid_infill || has_infill)
layer_tools.has_object = true; layer_tools.has_object = true;
@ -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) // 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) 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
} }
} }
@ -256,11 +306,9 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id)
for (unsigned int &extruder_id : lt.extruders) { for (unsigned int &extruder_id : lt.extruders) {
assert(extruder_id > 0); assert(extruder_id > 0);
-- extruder_id; -- extruder_id;
} }
} }
void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z) void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z)
{ {
if (m_layer_tools.empty()) if (m_layer_tools.empty())
@ -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) // 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; something_overridden = true;
@ -449,11 +497,10 @@ int WipingExtrusions::last_nonsoluble_extruder_on_layer(const PrintConfig& print
return (-1); return (-1);
} }
// Decides whether this entity could be overridden // 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 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; return false;
if (object.config().wipe_into_objects) if (object.config().wipe_into_objects)
@ -522,7 +569,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
if (wipe_into_infill_only && ! print.config().infill_first) 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 // 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): // (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; continue;
if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { // this infill will be used to wipe this extruder if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { // this infill will be used to wipe this extruder
@ -597,8 +644,8 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
// Either way, we will now force-override it with something suitable: // Either way, we will now force-override it with something suitable:
if (print.config().infill_first 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 || 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.is_extruder_order(lt.perimeter_extruder(region), 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.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); set_extruder_override(fill, copy, (print.config().infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies);
else { else {
// In this case we can (and should) leave it to be printed normally. // In this case we can (and should) leave it to be printed normally.

View file

@ -61,7 +61,7 @@ private:
int last_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const; 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) // 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: // 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 { bool is_entity_overridden(const ExtrusionEntity* entity, size_t copy_id) const {
@ -84,6 +84,7 @@ public:
print_z(z), print_z(z),
has_object(false), has_object(false),
has_support(false), has_support(false),
extruder_override(0),
has_wipe_tower(false), has_wipe_tower(false),
wipe_tower_partitions(0), wipe_tower_partitions(0),
wipe_tower_layer_height(0.) {} wipe_tower_layer_height(0.) {}
@ -96,11 +97,21 @@ public:
bool is_extruder_order(unsigned int a, unsigned int b) const; 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(); } 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; coordf_t print_z;
bool has_object; bool has_object;
bool has_support; bool has_support;
// Zero based extruder IDs, ordered to minimize tool switches. // Zero based extruder IDs, ordered to minimize tool switches.
std::vector<unsigned int> extruders; 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? // Will there be anything extruded on this layer for the wipe tower?
// Due to the support layers possibly interleaving the object layers, // Due to the support layers possibly interleaving the object layers,
// wipe tower will be disabled for some support only layers. // wipe tower will be disabled for some support only layers.
@ -129,11 +140,11 @@ public:
// For the use case when each object is printed separately // For the use case when each object is printed separately
// (print.config.complete_objects is true). // (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. // For the use case when all objects are printed at once.
// (print.config.complete_objects is false). // (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(); } void clear() { m_layer_tools.clear(); }
@ -160,7 +171,7 @@ public:
private: private:
void initialize_layers(std::vector<coordf_t> &zs); 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 reorder_extruders(unsigned int last_extruder_id);
void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z); void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z);
void collect_extruder_statistics(bool prime_multi_material); void collect_extruder_statistics(bool prime_multi_material);

View file

@ -19,8 +19,8 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config)
this->config.apply(print_config, true); this->config.apply(print_config, true);
m_extrusion_axis = this->config.get_extrusion_axis(); m_extrusion_axis = this->config.get_extrusion_axis();
m_single_extruder_multi_material = print_config.single_extruder_multi_material.value; m_single_extruder_multi_material = print_config.single_extruder_multi_material.value;
m_max_acceleration = (print_config.gcode_flavor.value == gcfMarlin) ? m_max_acceleration = std::lrint((print_config.gcode_flavor.value == gcfMarlin) ?
print_config.machine_max_acceleration_extruding.values.front() : 0; print_config.machine_max_acceleration_extruding.values.front() : 0);
} }
void GCodeWriter::set_extruders(const std::vector<unsigned int> &extruder_ids) void GCodeWriter::set_extruders(const std::vector<unsigned int> &extruder_ids)
@ -247,9 +247,9 @@ std::string GCodeWriter::toolchange_prefix() const
std::string GCodeWriter::toolchange(unsigned int extruder_id) std::string GCodeWriter::toolchange(unsigned int extruder_id)
{ {
// set the new extruder // set the new extruder
auto it_extruder = std::lower_bound(m_extruders.begin(), m_extruders.end(), Extruder::key(extruder_id)); 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()); assert(it_extruder != m_extruders.end() && it_extruder->id() == extruder_id);
m_extruder = const_cast<Extruder*>(&*it_extruder); m_extruder = &*it_extruder;
// return the toolchange command // return the toolchange command
// if we are running a single-extruder setup, just set the extruder and return nothing // if we are running a single-extruder setup, just set the extruder and return nothing

View file

@ -74,7 +74,8 @@ public:
Vec3d get_position() const { return m_pos; } Vec3d get_position() const { return m_pos; }
private: 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; std::string m_extrusion_axis;
bool m_single_extruder_multi_material; bool m_single_extruder_multi_material;
Extruder* m_extruder; Extruder* m_extruder;

View file

@ -598,21 +598,6 @@ std::string Model::propose_export_file_name_and_path(const std::string &new_exte
return boost::filesystem::path(this->propose_export_file_name_and_path()).replace_extension(new_extension).string(); return boost::filesystem::path(this->propose_export_file_name_and_path()).replace_extension(new_extension).string();
} }
std::vector<std::pair<double, DynamicPrintConfig>> Model::get_custom_tool_changes(double default_layer_height, size_t num_extruders) const
{
std::vector<std::pair<double, DynamicPrintConfig>> custom_tool_changes;
for (const CustomGCode& custom_gcode : custom_gcode_per_print_z)
if (custom_gcode.gcode == ExtruderChangeCode) {
DynamicPrintConfig config;
// If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders
config.set_key_value("extruder", new ConfigOptionInt(custom_gcode.extruder > num_extruders ? 0 : custom_gcode.extruder));
// For correct extruders(tools) changing, we should decrease custom_gcode.height value by one default layer height
custom_tool_changes.push_back({ custom_gcode.print_z - default_layer_height, config });
}
return custom_tool_changes;
}
ModelObject::~ModelObject() ModelObject::~ModelObject()
{ {
this->clear_volumes(); this->clear_volumes();
@ -1856,6 +1841,19 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
return ret; return ret;
} }
// Return pairs of <print_z, 1-based extruder ID> sorted by increasing print_z from custom_gcode_per_print_z.
// print_z corresponds to the first layer printed with the new extruder.
std::vector<std::pair<double, unsigned int>> custom_tool_changes(const Model &model, size_t num_extruders)
{
std::vector<std::pair<double, unsigned int>> custom_tool_changes;
for (const Model::CustomGCode &custom_gcode : model.custom_gcode_per_print_z)
if (custom_gcode.gcode == ExtruderChangeCode) {
// If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders
custom_tool_changes.emplace_back(custom_gcode.print_z, static_cast<unsigned int>(custom_gcode.extruder > num_extruders ? 1 : custom_gcode.extruder));
}
return custom_tool_changes;
}
// Test whether the two models contain the same number of ModelObjects with the same set of IDs // Test whether the two models contain the same number of ModelObjects with the same set of IDs
// ordered in the same order. In that case it is not necessary to kill the background processing. // ordered in the same order. In that case it is not necessary to kill the background processing.
bool model_object_list_equal(const Model &model_old, const Model &model_new) bool model_object_list_equal(const Model &model_old, const Model &model_new)

View file

@ -838,9 +838,6 @@ public:
// Propose an output path, replace extension. The new_extension shall contain the initial dot. // Propose an output path, replace extension. The new_extension shall contain the initial dot.
std::string propose_export_file_name_and_path(const std::string &new_extension) const; std::string propose_export_file_name_and_path(const std::string &new_extension) const;
// from custom_gcode_per_print_z get just tool_change codes
std::vector<std::pair<double, DynamicPrintConfig>> get_custom_tool_changes(double default_layer_height, size_t num_extruders) const;
private: private:
explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); }; explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); };
void assign_new_unique_ids_recursive(); void assign_new_unique_ids_recursive();
@ -857,6 +854,10 @@ private:
#undef OBJECTBASE_DERIVED_COPY_MOVE_CLONE #undef OBJECTBASE_DERIVED_COPY_MOVE_CLONE
#undef OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE #undef OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE
// Return pairs of <print_z, 1-based extruder ID> sorted by increasing print_z from custom_gcode_per_print_z.
// print_z corresponds to the first layer printed with the new extruder.
extern std::vector<std::pair<double, unsigned int>> custom_tool_changes(const Model &model, size_t num_extruders);
// Test whether the two models contain the same number of ModelObjects with the same set of IDs // Test whether the two models contain the same number of ModelObjects with the same set of IDs
// ordered in the same order. In that case it is not necessary to kill the background processing. // ordered in the same order. In that case it is not necessary to kill the background processing.
extern bool model_object_list_equal(const Model &model_old, const Model &model_new); extern bool model_object_list_equal(const Model &model_old, const Model &model_new);

View file

@ -638,48 +638,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr); m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr);
} }
// Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs,
// considering custom_tool_change values
void assign(const t_layer_config_ranges &in, const std::vector<std::pair<double, DynamicPrintConfig>> &custom_tool_changes) {
m_ranges.clear();
m_ranges.reserve(in.size());
// Input ranges are sorted lexicographically. First range trims the other ranges.
coordf_t last_z = 0;
for (const std::pair<const t_layer_height_range, DynamicPrintConfig> &range : in)
if (range.first.second > last_z) {
coordf_t min_z = std::max(range.first.first, 0.);
if (min_z > last_z + EPSILON) {
m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr);
last_z = min_z;
}
if (range.first.second > last_z + EPSILON) {
const DynamicPrintConfig* cfg = &range.second;
m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg);
last_z = range.first.second;
}
}
// add ranges for extruder changes from custom_tool_changes
for (size_t i = 0; i < custom_tool_changes.size(); i++) {
const DynamicPrintConfig* cfg = &custom_tool_changes[i].second;
coordf_t cur_Z = custom_tool_changes[i].first;
coordf_t next_Z = i == custom_tool_changes.size()-1 ? DBL_MAX : custom_tool_changes[i+1].first;
if (cur_Z > last_z + EPSILON) {
if (i==0)
m_ranges.emplace_back(t_layer_height_range(last_z, cur_Z), nullptr);
m_ranges.emplace_back(t_layer_height_range(cur_Z, next_Z), cfg);
}
else if (next_Z > last_z + EPSILON)
m_ranges.emplace_back(t_layer_height_range(last_z, next_Z), cfg);
}
if (m_ranges.empty())
m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr);
else if (m_ranges.back().second == nullptr)
m_ranges.back().first.second = DBL_MAX;
else if (m_ranges.back().first.second != DBL_MAX)
m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr);
}
const DynamicPrintConfig* config(const t_layer_height_range &range) const { const DynamicPrintConfig* config(const t_layer_height_range &range) const {
auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr)); auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr));
// #ys_FIXME_COLOR // #ys_FIXME_COLOR
@ -733,17 +691,15 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
for (const ModelObject *model_object : m_model.objects) for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::New); model_object_status.emplace(model_object->id(), ModelObjectStatus::New);
} else { } else {
if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) {
// If custom gcode per layer height was changed, we should stop background processing.
update_apply_status(this->invalidate_steps({ psWipeTower, psGCodeExport }));
m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
}
if (model_object_list_equal(m_model, model)) { if (model_object_list_equal(m_model, model)) {
// The object list did not change. // The object list did not change.
for (const ModelObject *model_object : m_model.objects) for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old); model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
// But if custom gcode per layer height was changed
if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) {
// we should stop background processing
update_apply_status(this->invalidate_step(psGCodeExport));
m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
}
} else if (model_object_list_extended(m_model, model)) { } else if (model_object_list_extended(m_model, model)) {
// Add new objects. Their volumes and configs will be synchronized later. // Add new objects. Their volumes and configs will be synchronized later.
update_apply_status(this->invalidate_step(psGCodeExport)); update_apply_status(this->invalidate_step(psGCodeExport));
@ -835,9 +791,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
for (PrintObject *print_object : m_objects) for (PrintObject *print_object : m_objects)
print_object_status.emplace(PrintObjectStatus(print_object)); print_object_status.emplace(PrintObjectStatus(print_object));
std::vector<std::pair<double, DynamicPrintConfig>> custom_tool_changes =
m_model.get_custom_tool_changes(m_default_object_config.layer_height, num_extruders);
// 3) Synchronize ModelObjects & PrintObjects. // 3) Synchronize ModelObjects & PrintObjects.
for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) { for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) {
ModelObject &model_object = *m_model.objects[idx_model_object]; ModelObject &model_object = *m_model.objects[idx_model_object];
@ -845,9 +798,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
assert(it_status != model_object_status.end()); assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted); assert(it_status->status != ModelObjectStatus::Deleted);
const ModelObject& model_object_new = *model.objects[idx_model_object]; const ModelObject& model_object_new = *model.objects[idx_model_object];
// ys_FIXME_COLOR const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges);
// const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges);
const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges, custom_tool_changes);
if (it_status->status == ModelObjectStatus::New) if (it_status->status == ModelObjectStatus::New)
// PrintObject instances will be added in the next loop. // PrintObject instances will be added in the next loop.
continue; continue;
@ -1015,8 +966,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
PrintRegionConfig this_region_config; PrintRegionConfig this_region_config;
bool this_region_config_set = false; bool this_region_config_set = false;
for (PrintObject *print_object : m_objects) { for (PrintObject *print_object : m_objects) {
if(m_force_update_print_regions && !custom_tool_changes.empty())
goto print_object_end;
const LayerRanges *layer_ranges; const LayerRanges *layer_ranges;
{ {
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id())); auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
@ -1989,7 +1938,9 @@ void Print::_make_wipe_tower()
wipe_volumes.push_back(std::vector<float>(wiping_matrix.begin()+i*number_of_extruders, wiping_matrix.begin()+(i+1)*number_of_extruders)); wipe_volumes.push_back(std::vector<float>(wiping_matrix.begin()+i*number_of_extruders, wiping_matrix.begin()+(i+1)*number_of_extruders));
// Let the ToolOrdering class know there will be initial priming extrusions at the start of the print. // Let the ToolOrdering class know there will be initial priming extrusions at the start of the print.
m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int)-1, true); m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int)-1, true,
(this->object_extruders().size() == 1) ? &custom_tool_changes(this->model(), (unsigned int)m_config.nozzle_diameter.size()) : nullptr);
if (! m_wipe_tower_data.tool_ordering.has_wipe_tower()) if (! m_wipe_tower_data.tool_ordering.has_wipe_tower())
// Don't generate any wipe tower. // Don't generate any wipe tower.
return; return;
@ -2107,13 +2058,6 @@ void Print::_make_wipe_tower()
m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges(); m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
} }
// Returns extruder this eec should be printed with, according to PrintRegion config
int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion &region)
{
return is_infill(fill.role()) ? 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);
}
// Generate a recommended G-code output file name based on the format template, default extension, and template parameters // Generate a recommended G-code output file name based on the format template, default extension, and template parameters
// (timestamps, object placeholders derived from the model, current placeholder prameters and print statistics. // (timestamps, object placeholders derived from the model, current placeholder prameters and print statistics.
// Use the final print statistics if available, or just keep the print statistics placeholders if not available yet (before G-code is finalized). // Use the final print statistics if available, or just keep the print statistics placeholders if not available yet (before G-code is finalized).

View file

@ -369,9 +369,6 @@ public:
// If zero, then the print is empty and the print shall not be executed. // If zero, then the print is empty and the print shall not be executed.
unsigned int num_object_instances() const; unsigned int num_object_instances() const;
// Returns extruder this eec should be printed with, according to PrintRegion config:
static int get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion &region);
const ExtrusionEntityCollection& skirt() const { return m_skirt; } const ExtrusionEntityCollection& skirt() const { return m_skirt; }
const ExtrusionEntityCollection& brim() const { return m_brim; } const ExtrusionEntityCollection& brim() const { return m_brim; }
@ -386,9 +383,6 @@ public:
// Accessed by SupportMaterial // Accessed by SupportMaterial
const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; } const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; }
// force update of PrintRegions, when custom_tool_change is not empty and (Re)Slicing is started
void set_force_update_print_regions(bool force_update_print_regions) { m_force_update_print_regions = force_update_print_regions; }
protected: protected:
// methods for handling regions // methods for handling regions
PrintRegion* get_region(size_t idx) { return m_regions[idx]; } PrintRegion* get_region(size_t idx) { return m_regions[idx]; }
@ -431,9 +425,6 @@ private:
// Estimated print time, filament consumed. // Estimated print time, filament consumed.
PrintStatistics m_print_statistics; PrintStatistics m_print_statistics;
// flag used
bool m_force_update_print_regions = false;
// To allow GCode to set the Print's GCodeExport step status. // To allow GCode to set the Print's GCodeExport step status.
friend class GCode; friend class GCode;
// Allow PrintObject to access m_mutex and m_cancel_callback. // Allow PrintObject to access m_mutex and m_cancel_callback.

View file

@ -132,11 +132,6 @@ public:
// This "finished" flag does not account for the final export of the output file (.gcode or zipped PNGs), // This "finished" flag does not account for the final export of the output file (.gcode or zipped PNGs),
// and it does not account for the OctoPrint scheduling. // and it does not account for the OctoPrint scheduling.
bool finished() const { return m_print->finished(); } bool finished() const { return m_print->finished(); }
void set_force_update_print_regions(bool force_update_print_regions) {
if (m_fff_print)
m_fff_print->set_force_update_print_regions(force_update_print_regions);
}
private: private:
void thread_proc(); void thread_proc();

View file

@ -3022,7 +3022,6 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
this->update_print_volume_state(); this->update_print_volume_state();
// Apply new config to the possibly running background task. // Apply new config to the possibly running background task.
bool was_running = this->background_process.running(); bool was_running = this->background_process.running();
this->background_process.set_force_update_print_regions(force_validation);
Print::ApplyStatus invalidated = this->background_process.apply(this->q->model(), wxGetApp().preset_bundle->full_config()); Print::ApplyStatus invalidated = this->background_process.apply(this->q->model(), wxGetApp().preset_bundle->full_config());
// Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile. // Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile.