Skip to content

Commit

Permalink
Semi-parallel printing
Browse files Browse the repository at this point in the history
* Semi-parallel: print all first layers at once for better adhesion.
* Check extruder/heatblock collisions
* supermeril: little change to tooltip & ui file
TODO: check layer time computation for cooling time.
#3445
  • Loading branch information
Vova authored and supermerill committed Apr 25, 2023
1 parent 2e6c5e3 commit 6207cb6
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 61 deletions.
1 change: 1 addition & 0 deletions resources/ui_layout/default/print.ui
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ group:Plater
setting:duplicate_distance
group:Sequential printing
setting:tags$Advanced$Expert$Prusa:complete_objects
setting:parallel_objects_step
setting:complete_objects_one_skirt
setting:complete_objects_sort
line:Extruder clearance (mm)
Expand Down
1 change: 1 addition & 0 deletions resources/ui_layout/example/print.ui
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ group:Plater
setting:duplicate_distance
group:Sequential printing
setting:tags$Advanced$Expert$Prusa:complete_objects
setting:parallel_objects_step
setting:complete_objects_one_skirt
setting:complete_objects_sort
line:Extruder clearance (mm)
Expand Down
169 changes: 115 additions & 54 deletions src/libslic3r/GCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1806,20 +1806,20 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
this->_print_first_layer_bed_temperature(file, print, start_all_gcode, initial_extruder_id, true);

// Do all objects for each layer.
if (initial_extruder_id != (uint16_t)-1)
if (initial_extruder_id != (uint16_t)-1) {
if (print.config().complete_objects.value) {
size_t finished_objects = 0;
const PrintObject *prev_object = (*print_object_instance_sequential_active)->print_object;
for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) {
const PrintObject &object = *(*print_object_instance_sequential_active)->print_object;
const PrintObject* prev_object = (*print_object_instance_sequential_active)->print_object;
for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++print_object_instance_sequential_active) {
const PrintObject& object = *(*print_object_instance_sequential_active)->print_object;
if (&object != prev_object || tool_ordering.first_extruder() != final_extruder_id) {
tool_ordering = ToolOrdering(object, final_extruder_id);
uint16_t new_extruder_id = tool_ordering.first_extruder();
if (new_extruder_id == (uint16_t)-1)
// Skip this object.
continue;
initial_extruder_id = new_extruder_id;
final_extruder_id = tool_ordering.last_extruder();
final_extruder_id = tool_ordering.last_extruder();
assert(final_extruder_id != (uint16_t)-1);
}
print.throw_if_canceled();
Expand Down Expand Up @@ -1860,68 +1860,128 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser
// and export G-code into file.
this->process_layers(print, print.m_print_statistics, tool_ordering, collect_layers_to_print(object), *print_object_instance_sequential_active - object.instances().data(), file);
++ finished_objects;
++finished_objects;
// Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
// Reset it when starting another object from 1st layer.
m_second_layer_things_done = false;
prev_object = &object;
}
set_extra_lift(m_last_layer_z, prev_object->layers().back()->id(), print.config(), m_writer, initial_extruder_id /* osef, it's only for the lift_min */);
} else {
// Sort layers by Z.
// All extrusion moves with the same top layer height are extruded uninterrupted.
std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print);
// Prusa Multi-Material wipe tower.
if (has_wipe_tower && ! layers_to_print.empty()) {
m_wipe_tower.reset(new WipeTowerIntegration(print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get()));
file.write(m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
if (print.config().single_extruder_multi_material_priming) {
file.write(m_wipe_tower->prime(*this));
// Verify, whether the print overaps the priming extrusions.
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
for (const PrintObject *print_object : print.objects())
bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
bbox_prime.offset(0.5f);
bool overlap = bbox_prime.overlap(bbox_print);

if (print.config().gcode_flavor.value == gcfMarlinLegacy || print.config().gcode_flavor.value == gcfMarlinFirmware) {
file.write(this->retract());
file.write("M300 S800 P500\n"); // Beep for 500ms, tone 800Hz.
if (overlap) {
// Wait for the user to remove the priming extrusions.
file.write("M1 Remove priming towers and click button.\n");
} else {
// Just wait for a bit to let the user check, that the priming succeeded.
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
file.write("M1 S10\n");
/////////////////////////////////////////////// begin parallel_objects_step
if (print.config().parallel_objects_step > 0 && !has_wipe_tower) {

float range = print.config().parallel_objects_step + EPSILON;
print_object_instances_ordering = sort_object_instances_by_model_order(print);
std::vector<const PrintInstance*>::const_iterator prev_object = print_object_instances_ordering.begin();

bool first_layer = true;
proceed_layers:

for (coordf_t Rstart = 0, Rend = range;; Rstart += range, Rend += range) {
bool is_layers = false;
print_object_instance_sequential_active = print_object_instances_ordering.begin();

for (size_t i = 0; i < print.objects().size(); ++i, ++print_object_instance_sequential_active) {
std::vector<LayerToPrint> object_layers = collect_layers_to_print(*print.objects()[i]);
std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print_range;

int layer_num = 0;
for (const LayerToPrint& ltp : object_layers) {
layer_num++;
if (!first_layer && layer_num == 1)
continue;

if (ltp.print_z() >= Rstart && ltp.print_z() < Rend) {
std::pair<coordf_t, std::vector<LayerToPrint>> merged;
merged.first = ltp.print_z();
merged.second.emplace_back(ltp);
layers_to_print_range.emplace_back(merged);
if (first_layer)
break;
}
}
} else {
// This is not Marlin, M1 command is probably not supported.
// (See https://github.com/prusa3d/PrusaSlicer/issues/5441.)
if (overlap) {
print.active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL,
_(L("Your print is very close to the priming regions. "
"Make sure there is no collision.")));
} else {
// Just continue printing, no action necessary.

if (!layers_to_print_range.empty())
{
if (print_object_instance_sequential_active != prev_object) {
this->set_origin(unscale((*print_object_instance_sequential_active)->shift));
std::string gcode;
//go to origin of the next object (it's 0,0 because we shifted the origin to it)
Polyline polyline = this->travel_to(gcode, Point(0, 0), erNone);
this->write_travel_to(gcode, polyline, "move to origin position for next object");
file.write(gcode);
}
this->process_layers(print, print.m_print_statistics, tool_ordering, print_object_instances_ordering, layers_to_print_range, file);
prev_object = print_object_instance_sequential_active;
is_layers = true;
}
}
if (first_layer) {
first_layer = false;
goto proceed_layers;
}
if (!is_layers) {
break;
}
}
/////////////////////////////////////////////// end parallel_objects_step
} else {
// Sort layers by Z.
// All extrusion moves with the same top layer height are extruded uninterrupted.
std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print);
// Prusa Multi-Material wipe tower.
if (has_wipe_tower && !layers_to_print.empty()) {
m_wipe_tower.reset(new WipeTowerIntegration(print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get()));
file.write(m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
if (print.config().single_extruder_multi_material_priming) {
file.write(m_wipe_tower->prime(*this));
// Verify, whether the print overaps the priming extrusions.
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
for (const PrintObject* print_object : print.objects())
bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
bbox_prime.offset(0.5f);
bool overlap = bbox_prime.overlap(bbox_print);

if (print.config().gcode_flavor.value == gcfMarlinLegacy || print.config().gcode_flavor.value == gcfMarlinFirmware) {
file.write(this->retract());
file.write("M300 S800 P500\n"); // Beep for 500ms, tone 800Hz.
if (overlap) {
// Wait for the user to remove the priming extrusions.
file.write("M1 Remove priming towers and click button.\n");
} else {
// Just wait for a bit to let the user check, that the priming succeeded.
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
file.write("M1 S10\n");
}
} else {
// This is not Marlin, M1 command is probably not supported.
// (See https://github.com/prusa3d/PrusaSlicer/issues/5441.)
if (overlap) {
print.active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL,
_(L("Your print is very close to the priming regions. "
"Make sure there is no collision.")));
} else {
// Just continue printing, no action necessary.
}

}
}
print.throw_if_canceled();
}
print.throw_if_canceled();
// Process all layers of all objects (non-sequential mode) with a parallel pipeline:
// Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser
// and export G-code into file.
this->process_layers(print, print.m_print_statistics, tool_ordering, print_object_instances_ordering, layers_to_print, file);
if (m_wipe_tower)
// Purge the extruder, pull out the active filament.
file.write(m_wipe_tower->finalize(*this));
}
// Process all layers of all objects (non-sequential mode) with a parallel pipeline:
// Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser
// and export G-code into file.
this->process_layers(print, print.m_print_statistics, tool_ordering, print_object_instances_ordering, layers_to_print, file);
if (m_wipe_tower)
// Purge the extruder, pull out the active filament.
file.write(m_wipe_tower->finalize(*this));
}

}
// Write end commands to file.
file.write(this->retract());
//if needed, write the gcode_label_objects_end
Expand Down Expand Up @@ -5813,7 +5873,8 @@ Polyline GCode::travel_to(std::string &gcode, const Point &point, ExtrusionRole

void GCode::write_travel_to(std::string &gcode, const Polyline& travel, std::string comment)
{
if (travel.size() > 4) {
if (travel.size() > 4)
{
//ensure that you won't overload the firmware.
// travel are strait lines, but with avoid_crossing_perimeters, there can be many points. Reduce speed instead of deleting points, as it's already optimised as much as possible, even if it can be a bit more => TODO?)
// we are using a window of 10 moves.
Expand Down
1 change: 1 addition & 0 deletions src/libslic3r/Preset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,7 @@ static std::vector<std::string> s_Preset_print_options {
"notes",
"print_custom_variables",
"complete_objects",
"parallel_objects_step",
"complete_objects_one_skirt",
"complete_objects_sort",
"extruder_clearance_radius",
Expand Down
4 changes: 2 additions & 2 deletions src/libslic3r/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -649,10 +649,10 @@ std::pair<PrintBase::PrintValidationError, std::string> Print::validate(std::str
if (extruders.empty())
return { PrintBase::PrintValidationError::pveNoPrint, L("The supplied settings will cause an empty print.") };

if (m_config.complete_objects) {
if (m_config.complete_objects || m_config.parallel_objects_step > 0) {
if (! sequential_print_horizontal_clearance_valid(*this))
return { PrintBase::PrintValidationError::pveWrongPosition, L("Some objects are too close; your extruder will collide with them.") };
if (! sequential_print_vertical_clearance_valid(*this))
if (m_config.complete_objects && ! sequential_print_vertical_clearance_valid(*this))
return { PrintBase::PrintValidationError::pveWrongPosition,L("Some objects are too tall and cannot be printed without extruder collisions.") };
}

Expand Down
15 changes: 14 additions & 1 deletion src/libslic3r/PrintConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,19 @@ void PrintConfigDef::init_fff_params()
def->mode = comSimpleAE | comPrusa;
def->set_default_value(new ConfigOptionBool(false));

def = this->add("parallel_objects_step", coFloat);
def->label = L("Parallel printing step");
def->category = OptionCategory::output;
def->tooltip = L("When multiple objects are present, instead of jumping form one to another at each layer"
" the printer will continue to print the curernt object layers up to this height before moving to the next object."
" (first layers will be still printed one by one)."
"\nThis feature also use the same extruder clearance radius field as 'complete individual objects' (complete_objects)"
", but you can modify them to instead reflect the clerance of the nozzle, if this field reflect the z-clearance of it."
"\nThis field is exclusive with 'complete individual objects' (complete_objects). Set to 0 to deactivate.");
def->sidetext = L("mm");
def->mode = comAdvancedE | comSuSi;
def->set_default_value(new ConfigOptionFloat(0));

def = this->add("complete_objects_one_skirt", coBool);
def->label = L("Allow only one skirt loop");
def->category = OptionCategory::output;
Expand Down Expand Up @@ -7966,7 +7979,7 @@ double min_object_distance(const ConfigBase *config, double ref_height /* = 0*/)
double base_dist = 0;
//std::cout << "START min_object_distance =>" << base_dist << "\n";
const ConfigOptionBool* co_opt = config->option<ConfigOptionBool>("complete_objects");
if (co_opt && co_opt->value) {
if (config->option("parallel_objects_step")->getFloat() > 0 || co_opt && co_opt->value) {
double skirt_dist = 0;
try {
std::vector<double> vals = dynamic_cast<const ConfigOptionFloats*>(config->option("nozzle_diameter"))->values;
Expand Down
1 change: 1 addition & 0 deletions src/libslic3r/PrintConfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1131,6 +1131,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionFloatOrPercent, brim_acceleration))
((ConfigOptionInts, chamber_temperature))
((ConfigOptionBool, complete_objects))
((ConfigOptionFloat, parallel_objects_step))
((ConfigOptionBool, complete_objects_one_skirt))
((ConfigOptionBool, complete_objects_one_brim))
((ConfigOptionEnum<CompleteObjectSort>, complete_objects_sort))
Expand Down
1 change: 1 addition & 0 deletions src/slic3r/GUI/ConfigManipulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
for (auto el : { /*"extruder_clearance_radius", "extruder_clearance_height",*/ "complete_objects_one_skirt",
"complete_objects_sort"})
toggle_field(el, have_sequential_printing);
toggle_field("parallel_objects_step", !have_sequential_printing);

bool have_ooze_prevention = config->opt_bool("ooze_prevention");
toggle_field("standby_temperature_delta", have_ooze_prevention);
Expand Down
Loading

0 comments on commit 6207cb6

Please sign in to comment.