From 6d41a76af77af2196eab759db594075706a667bd Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 28 Aug 2023 16:46:27 +0200 Subject: [PATCH 01/11] M486 cancel object implementation: - gcode_label_objects changed from coBool to coEnum - the start-end pairs are emitted at the start for all objects --- src/libslic3r/GCode.cpp | 61 +++++++++++++++++++++++++++++++---- src/libslic3r/PrintConfig.cpp | 20 ++++++++++-- src/libslic3r/PrintConfig.hpp | 7 +++- 3 files changed, 78 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 5afe6b34c4..1d72639f55 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -103,6 +103,41 @@ namespace Slic3r { } + // Accepts vector of PrintObjectPtrs and an object and instance ids. Returns starting tag for label object function. + static std::string label_object_start(LabelObjects label_object_style, const SpanOfConstPtrs& objects, int object_id, int instance_id) + { + int unique_id = 0; + for (size_t idx = 0; idx < size_t(object_id); ++idx) + unique_id += int(objects[idx]->model_object()->instances.size()); + unique_id += instance_id; + + std::string name = objects[object_id]->model_object()->name; + if (label_object_style == loMarlin && objects[object_id]->model_object()->instances.size() > 1u) + name += " (copy " + std::to_string(instance_id) + ")"; + + std::string out; + if (label_object_style == loOctoprint) + out += std::string("; printing object ") + name + " id:" + std::to_string(object_id) + " copy " + std::to_string(instance_id) + "\n"; + else if (label_object_style == loMarlin) { + out += std::string("M486 S") + std::to_string(unique_id) + "\n"; + out += std::string("M486 N") + name + "\n"; + } + return out; + } + + + static std::string label_object_stop(LabelObjects label_object_style, int object_id, int instance_id, const std::string& name) + { + std::string out; + if (label_object_style == loOctoprint) + out += std::string("; stop printing object ") + name + " id:" + std::to_string(object_id) + " copy " + std::to_string(instance_id) + "\n"; + else if (label_object_style == loMarlin) + out += std::string("M486 S-1\n"); + return out; + } + + + // Return true if tch_prefix is found in custom_gcode static bool custom_gcode_changes_tool(const std::string& custom_gcode, const std::string& tch_prefix, unsigned next_extruder) { @@ -1215,6 +1250,18 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail // Set other general things. file.write(this->preamble()); + // Label all objects so printer knows about them since the start. + if (config().gcode_label_objects != loDisabled) { + file.write("\n"); + for (size_t object_idx = 0; object_idx < print.objects().size(); ++object_idx) { + for (size_t inst_idx = 0; inst_idx < print.objects()[object_idx]->model_object()->instances.size(); ++inst_idx) { + file.write(label_object_start(config().gcode_label_objects, print.objects(), object_idx, inst_idx)); + file.write(label_object_stop(config().gcode_label_objects, object_idx, inst_idx, print.objects()[object_idx]->model_object()->name)); + } + } + file.write("\n"); + } + print.throw_if_canceled(); // Collect custom seam data from all objects. @@ -2354,13 +2401,13 @@ void GCodeGenerator::process_layer_single_object( m_avoid_crossing_perimeters.use_external_mp_once(); m_last_obj_copy = this_object_copy; this->set_origin(unscale(offset)); - if (this->config().gcode_label_objects) { - for (const PrintObject *po : print_object.print()->objects()) + if (this->config().gcode_label_objects != loDisabled) { + for (const PrintObject* po : print_object.print()->objects()) { if (po == &print_object) break; - else - ++ object_id; - gcode += std::string("; printing object ") + print_object.model_object()->name + " id:" + std::to_string(object_id) + " copy " + std::to_string(print_instance.instance_id) + "\n"; + ++object_id; + } + gcode += label_object_start(config().gcode_label_objects, print_object.print()->objects(), object_id, print_instance.instance_id); } } }; @@ -2538,8 +2585,8 @@ void GCodeGenerator::process_layer_single_object( } } } - if (! first && this->config().gcode_label_objects) - gcode += std::string("; stop printing object ") + print_object.model_object()->name + " id:" + std::to_string(object_id) + " copy " + std::to_string(print_instance.instance_id) + "\n"; + if (! first && config().gcode_label_objects != loDisabled) + gcode += label_object_stop(config().gcode_label_objects, object_id, print_instance.instance_id, print_object.model_object()->name); } void GCodeGenerator::apply_print_config(const PrintConfig &print_config) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 6d52484e9c..2c033c5d9c 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -229,6 +229,13 @@ static const t_config_enum_values s_keys_map_DraftShield = { }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(DraftShield) +static const t_config_enum_values s_keys_map_LabelObjects = { + { "disabled", loDisabled }, + { "octoprint", loOctoprint }, + { "marlin", loMarlin } +}; +CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(LabelObjects) + static const t_config_enum_values s_keys_map_GCodeThumbnailsFormat = { { "PNG", int(GCodeThumbnailsFormat::PNG) }, { "JPG", int(GCodeThumbnailsFormat::JPG) }, @@ -1493,13 +1500,18 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionEnum(gcfRepRapSprinter)); - def = this->add("gcode_label_objects", coBool); + def = this->add("gcode_label_objects", coEnum); def->label = L("Label objects"); def->tooltip = L("Enable this to add comments into the G-Code labeling print moves with what object they belong to," " which is useful for the Octoprint CancelObject plugin. This settings is NOT compatible with " "Single Extruder Multi Material setup and Wipe into Object / Wipe into Infill."); + def->set_enum({ + { "disabled", L("Disabled") }, + { "octoprint", L("OctoPrint comments") }, + { "marlin", L("Marlin (M486)") } + }); def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(0)); + def->set_default_value(new ConfigOptionEnum(loDisabled)); def = this->add("gcode_substitutions", coStrings); def->label = L("G-code substitutions"); @@ -4335,6 +4347,10 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va } else if (opt_key == "draft_shield" && (value == "1" || value == "0")) { // draft_shield used to be a bool, it was turned into an enum in PrusaSlicer 2.4.0. value = value == "1" ? "enabled" : "disabled"; + } else if (opt_key == "gcode_label_objects" && (value == "1" || value == "0")) { + // gcode_label_objects used to be a bool (the behavior was nothing or "octoprint"), it is + // and enum since PrusaSlicer 2.6.2. + value = value == "1" ? "octoprint" : "disabled"; } else if (opt_key == "octoprint_host") { opt_key = "print_host"; } else if (opt_key == "octoprint_cafile") { diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 390f91daf6..5466ad1fd3 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -147,6 +147,10 @@ enum DraftShield { dsDisabled, dsLimited, dsEnabled }; +enum LabelObjects { + loDisabled, loOctoprint, loMarlin +}; + enum class PerimeterGeneratorType { // Classic perimeter generator using Clipper offsets with constant extrusion width. @@ -183,6 +187,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLAPillarConnectionMode) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLASupportTreeType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BrimType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(DraftShield) +CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(LabelObjects) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeThumbnailsFormat) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ForwardCompatibilitySubstitutionRule) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PerimeterGeneratorType) @@ -729,7 +734,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloats, filament_multitool_ramming_flow)) ((ConfigOptionBool, gcode_comments)) ((ConfigOptionEnum, gcode_flavor)) - ((ConfigOptionBool, gcode_label_objects)) + ((ConfigOptionEnum, gcode_label_objects)) // Triples of strings: "search pattern", "replace with pattern", "attribs" // where "attribs" are one of: // r - regular expression From 5d50a91c30298ec767f5517d00dddf62db45c5d9 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 5 Sep 2023 15:43:13 +0200 Subject: [PATCH 02/11] Label objects refactoring (enum->enum class, initial header export separated in a dedicated function) --- src/libslic3r/GCode.cpp | 43 +++++++++++++++++++++-------------- src/libslic3r/PrintConfig.cpp | 8 +++---- src/libslic3r/PrintConfig.hpp | 4 ++-- 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 1d72639f55..6eca721c12 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -112,13 +112,13 @@ namespace Slic3r { unique_id += instance_id; std::string name = objects[object_id]->model_object()->name; - if (label_object_style == loMarlin && objects[object_id]->model_object()->instances.size() > 1u) + if (label_object_style == LabelObjects::Marlin && objects[object_id]->model_object()->instances.size() > 1u) name += " (copy " + std::to_string(instance_id) + ")"; std::string out; - if (label_object_style == loOctoprint) + if (label_object_style == LabelObjects::Octoprint) out += std::string("; printing object ") + name + " id:" + std::to_string(object_id) + " copy " + std::to_string(instance_id) + "\n"; - else if (label_object_style == loMarlin) { + else if (label_object_style == LabelObjects::Marlin) { out += std::string("M486 S") + std::to_string(unique_id) + "\n"; out += std::string("M486 N") + name + "\n"; } @@ -129,14 +129,32 @@ namespace Slic3r { static std::string label_object_stop(LabelObjects label_object_style, int object_id, int instance_id, const std::string& name) { std::string out; - if (label_object_style == loOctoprint) + if (label_object_style == LabelObjects::Octoprint) out += std::string("; stop printing object ") + name + " id:" + std::to_string(object_id) + " copy " + std::to_string(instance_id) + "\n"; - else if (label_object_style == loMarlin) + else if (label_object_style == LabelObjects::Marlin) out += std::string("M486 S-1\n"); return out; } + static std::string label_all_objects(LabelObjects label_objects_style, const Print& print) + { + std::string out; + + if (label_objects_style != LabelObjects::Disabled) { + out += "\n"; + for (size_t object_idx = 0; object_idx < print.objects().size(); ++object_idx) { + for (size_t inst_idx = 0; inst_idx < print.objects()[object_idx]->model_object()->instances.size(); ++inst_idx) { + out += label_object_start(label_objects_style, print.objects(), object_idx, inst_idx); + out += label_object_stop(label_objects_style, object_idx, inst_idx, print.objects()[object_idx]->model_object()->name); + } + } + out += "\n"; + } + return out; + } + + // Return true if tch_prefix is found in custom_gcode static bool custom_gcode_changes_tool(const std::string& custom_gcode, const std::string& tch_prefix, unsigned next_extruder) @@ -1251,16 +1269,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail file.write(this->preamble()); // Label all objects so printer knows about them since the start. - if (config().gcode_label_objects != loDisabled) { - file.write("\n"); - for (size_t object_idx = 0; object_idx < print.objects().size(); ++object_idx) { - for (size_t inst_idx = 0; inst_idx < print.objects()[object_idx]->model_object()->instances.size(); ++inst_idx) { - file.write(label_object_start(config().gcode_label_objects, print.objects(), object_idx, inst_idx)); - file.write(label_object_stop(config().gcode_label_objects, object_idx, inst_idx, print.objects()[object_idx]->model_object()->name)); - } - } - file.write("\n"); - } + file.write(label_all_objects(config().gcode_label_objects, print)); print.throw_if_canceled(); @@ -2401,7 +2410,7 @@ void GCodeGenerator::process_layer_single_object( m_avoid_crossing_perimeters.use_external_mp_once(); m_last_obj_copy = this_object_copy; this->set_origin(unscale(offset)); - if (this->config().gcode_label_objects != loDisabled) { + if (this->config().gcode_label_objects != LabelObjects::Disabled) { for (const PrintObject* po : print_object.print()->objects()) { if (po == &print_object) break; @@ -2585,7 +2594,7 @@ void GCodeGenerator::process_layer_single_object( } } } - if (! first && config().gcode_label_objects != loDisabled) + if (! first && config().gcode_label_objects != LabelObjects::Disabled) gcode += label_object_stop(config().gcode_label_objects, object_id, print_instance.instance_id, print_object.model_object()->name); } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 2c033c5d9c..c80189ae3c 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -230,9 +230,9 @@ static const t_config_enum_values s_keys_map_DraftShield = { CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(DraftShield) static const t_config_enum_values s_keys_map_LabelObjects = { - { "disabled", loDisabled }, - { "octoprint", loOctoprint }, - { "marlin", loMarlin } + { "disabled", int(LabelObjects::Disabled) }, + { "octoprint", int(LabelObjects::Octoprint) }, + { "marlin", int(LabelObjects::Marlin) } }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(LabelObjects) @@ -1511,7 +1511,7 @@ void PrintConfigDef::init_fff_params() { "marlin", L("Marlin (M486)") } }); def->mode = comAdvanced; - def->set_default_value(new ConfigOptionEnum(loDisabled)); + def->set_default_value(new ConfigOptionEnum(LabelObjects::Disabled)); def = this->add("gcode_substitutions", coStrings); def->label = L("G-code substitutions"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 5466ad1fd3..c01884c765 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -147,8 +147,8 @@ enum DraftShield { dsDisabled, dsLimited, dsEnabled }; -enum LabelObjects { - loDisabled, loOctoprint, loMarlin +enum class LabelObjects { + Disabled, Octoprint, Marlin }; enum class PerimeterGeneratorType From 395a5639cced0b6b2c0a8bf18ba09668919174d9 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 5 Sep 2023 16:00:16 +0200 Subject: [PATCH 03/11] Label objects: rename Marlin -> Firmware-specific, updated tooltip --- src/libslic3r/GCode.cpp | 6 +++--- src/libslic3r/PrintConfig.cpp | 12 +++++++----- src/libslic3r/PrintConfig.hpp | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 6eca721c12..04b12ba719 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -112,13 +112,13 @@ namespace Slic3r { unique_id += instance_id; std::string name = objects[object_id]->model_object()->name; - if (label_object_style == LabelObjects::Marlin && objects[object_id]->model_object()->instances.size() > 1u) + if (label_object_style == LabelObjects::Firmware && objects[object_id]->model_object()->instances.size() > 1u) name += " (copy " + std::to_string(instance_id) + ")"; std::string out; if (label_object_style == LabelObjects::Octoprint) out += std::string("; printing object ") + name + " id:" + std::to_string(object_id) + " copy " + std::to_string(instance_id) + "\n"; - else if (label_object_style == LabelObjects::Marlin) { + else if (label_object_style == LabelObjects::Firmware) { out += std::string("M486 S") + std::to_string(unique_id) + "\n"; out += std::string("M486 N") + name + "\n"; } @@ -131,7 +131,7 @@ namespace Slic3r { std::string out; if (label_object_style == LabelObjects::Octoprint) out += std::string("; stop printing object ") + name + " id:" + std::to_string(object_id) + " copy " + std::to_string(instance_id) + "\n"; - else if (label_object_style == LabelObjects::Marlin) + else if (label_object_style == LabelObjects::Firmware) out += std::string("M486 S-1\n"); return out; } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index c80189ae3c..5e79c97d3e 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -232,7 +232,7 @@ CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(DraftShield) static const t_config_enum_values s_keys_map_LabelObjects = { { "disabled", int(LabelObjects::Disabled) }, { "octoprint", int(LabelObjects::Octoprint) }, - { "marlin", int(LabelObjects::Marlin) } + { "firmware", int(LabelObjects::Firmware) } }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(LabelObjects) @@ -1502,13 +1502,15 @@ void PrintConfigDef::init_fff_params() def = this->add("gcode_label_objects", coEnum); def->label = L("Label objects"); - def->tooltip = L("Enable this to add comments into the G-Code labeling print moves with what object they belong to," - " which is useful for the Octoprint CancelObject plugin. This settings is NOT compatible with " - "Single Extruder Multi Material setup and Wipe into Object / Wipe into Infill."); + def->tooltip = L("Selects whether labels should be exported at object boundaries and in what format.\n" + " Octoprint = comments to be consumed by Octoprint CancelObject plugin.\n" + " Firmware = firmware specific G-code (it will be chosen based on firmware flavor and it can end up to be empty).\n\n" + "This settings is NOT compatible with Single Extruder Multi Material setup and Wipe into Object / Wipe into Infill."); + def->set_enum({ { "disabled", L("Disabled") }, { "octoprint", L("OctoPrint comments") }, - { "marlin", L("Marlin (M486)") } + { "firmware", L("Firmware-specific") } }); def->mode = comAdvanced; def->set_default_value(new ConfigOptionEnum(LabelObjects::Disabled)); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index c01884c765..d3fd0ff429 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -148,7 +148,7 @@ enum DraftShield { }; enum class LabelObjects { - Disabled, Octoprint, Marlin + Disabled, Octoprint, Firmware }; enum class PerimeterGeneratorType From ef31e4f4871d89c9849e9614b8fa3d4745a7a00d Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 5 Sep 2023 16:09:14 +0200 Subject: [PATCH 04/11] LabelObjects: differentiate based on firmware flavor, change M486 N to M486 A --- src/libslic3r/GCode.cpp | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 04b12ba719..da40218779 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -104,7 +104,7 @@ namespace Slic3r { // Accepts vector of PrintObjectPtrs and an object and instance ids. Returns starting tag for label object function. - static std::string label_object_start(LabelObjects label_object_style, const SpanOfConstPtrs& objects, int object_id, int instance_id) + static std::string label_object_start(LabelObjects label_object_style, GCodeFlavor flavor, const SpanOfConstPtrs& objects, int object_id, int instance_id) { int unique_id = 0; for (size_t idx = 0; idx < size_t(object_id); ++idx) @@ -119,25 +119,33 @@ namespace Slic3r { if (label_object_style == LabelObjects::Octoprint) out += std::string("; printing object ") + name + " id:" + std::to_string(object_id) + " copy " + std::to_string(instance_id) + "\n"; else if (label_object_style == LabelObjects::Firmware) { - out += std::string("M486 S") + std::to_string(unique_id) + "\n"; - out += std::string("M486 N") + name + "\n"; + if (flavor == GCodeFlavor::gcfMarlinFirmware || flavor == GCodeFlavor::gcfMarlinLegacy || flavor == GCodeFlavor::gcfRepRapFirmware) { + out += std::string("M486 S") + std::to_string(unique_id) + "\n"; + out += std::string("M486 A") + name + "\n"; + } else { + // Not supported by / implemented for the other firmware flavors. + } } return out; } - static std::string label_object_stop(LabelObjects label_object_style, int object_id, int instance_id, const std::string& name) + static std::string label_object_stop(LabelObjects label_object_style, GCodeFlavor flavor, int object_id, int instance_id, const std::string& name) { std::string out; if (label_object_style == LabelObjects::Octoprint) out += std::string("; stop printing object ") + name + " id:" + std::to_string(object_id) + " copy " + std::to_string(instance_id) + "\n"; else if (label_object_style == LabelObjects::Firmware) - out += std::string("M486 S-1\n"); + if (flavor == GCodeFlavor::gcfMarlinFirmware || flavor == GCodeFlavor::gcfMarlinLegacy || flavor == GCodeFlavor::gcfRepRapFirmware) + out += std::string("M486 S-1\n"); + else { + // Not supported by / implemented for the other firmware flavors. + } return out; } - static std::string label_all_objects(LabelObjects label_objects_style, const Print& print) + static std::string label_all_objects(LabelObjects label_objects_style, GCodeFlavor flavor, const Print& print) { std::string out; @@ -145,8 +153,8 @@ namespace Slic3r { out += "\n"; for (size_t object_idx = 0; object_idx < print.objects().size(); ++object_idx) { for (size_t inst_idx = 0; inst_idx < print.objects()[object_idx]->model_object()->instances.size(); ++inst_idx) { - out += label_object_start(label_objects_style, print.objects(), object_idx, inst_idx); - out += label_object_stop(label_objects_style, object_idx, inst_idx, print.objects()[object_idx]->model_object()->name); + out += label_object_start(label_objects_style, flavor, print.objects(), object_idx, inst_idx); + out += label_object_stop(label_objects_style, flavor, object_idx, inst_idx, print.objects()[object_idx]->model_object()->name); } } out += "\n"; @@ -1269,7 +1277,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail file.write(this->preamble()); // Label all objects so printer knows about them since the start. - file.write(label_all_objects(config().gcode_label_objects, print)); + file.write(label_all_objects(config().gcode_label_objects, config().gcode_flavor, print)); print.throw_if_canceled(); @@ -2416,7 +2424,7 @@ void GCodeGenerator::process_layer_single_object( break; ++object_id; } - gcode += label_object_start(config().gcode_label_objects, print_object.print()->objects(), object_id, print_instance.instance_id); + gcode += label_object_start(config().gcode_label_objects, config().gcode_flavor, print_object.print()->objects(), object_id, print_instance.instance_id); } } }; @@ -2595,7 +2603,7 @@ void GCodeGenerator::process_layer_single_object( } } if (! first && config().gcode_label_objects != LabelObjects::Disabled) - gcode += label_object_stop(config().gcode_label_objects, object_id, print_instance.instance_id, print_object.model_object()->name); + gcode += label_object_stop(config().gcode_label_objects, config().gcode_flavor, object_id, print_instance.instance_id, print_object.model_object()->name); } void GCodeGenerator::apply_print_config(const PrintConfig &print_config) From 37dc3378ec1f42db6cc71ae95f3f6e97ccb81ed6 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 6 Sep 2023 09:33:41 +0200 Subject: [PATCH 05/11] Label objects: List of objects moved before custom start gcode, quotes added for M486 A on RRF --- src/libslic3r/GCode.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index da40218779..6bd7a06cf8 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -121,7 +121,8 @@ namespace Slic3r { else if (label_object_style == LabelObjects::Firmware) { if (flavor == GCodeFlavor::gcfMarlinFirmware || flavor == GCodeFlavor::gcfMarlinLegacy || flavor == GCodeFlavor::gcfRepRapFirmware) { out += std::string("M486 S") + std::to_string(unique_id) + "\n"; - out += std::string("M486 A") + name + "\n"; + out += std::string("M486 A"); + out += (flavor == GCodeFlavor::gcfRepRapFirmware ? (std::string("\"") + name + "\"") : name) + "\n"; } else { // Not supported by / implemented for the other firmware flavors. } @@ -1205,6 +1206,9 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail // Emit machine envelope limits for the Marlin firmware. this->print_machine_envelope(file, print); + // Label all objects so printer knows about them since the start. + file.write(label_all_objects(config().gcode_label_objects, config().gcode_flavor, print)); + // Update output variables after the extruders were initialized. m_placeholder_parser_integration.init(m_writer); // Let the start-up script prime the 1st printing tool. @@ -1276,9 +1280,6 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail // Set other general things. file.write(this->preamble()); - // Label all objects so printer knows about them since the start. - file.write(label_all_objects(config().gcode_label_objects, config().gcode_flavor, print)); - print.throw_if_canceled(); // Collect custom seam data from all objects. From 387376fb254fce66ce978bc266be58869a391664 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 6 Sep 2023 16:54:18 +0200 Subject: [PATCH 06/11] LabelObjects: Fixed a typo --- src/libslic3r/PrintConfig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 5e79c97d3e..a53a2d7286 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1503,7 +1503,7 @@ void PrintConfigDef::init_fff_params() def = this->add("gcode_label_objects", coEnum); def->label = L("Label objects"); def->tooltip = L("Selects whether labels should be exported at object boundaries and in what format.\n" - " Octoprint = comments to be consumed by Octoprint CancelObject plugin.\n" + " OctoPrint = comments to be consumed by OctoPrint CancelObject plugin.\n" " Firmware = firmware specific G-code (it will be chosen based on firmware flavor and it can end up to be empty).\n\n" "This settings is NOT compatible with Single Extruder Multi Material setup and Wipe into Object / Wipe into Infill."); From fe3cf27394d3fd07557d2411502ea1b49e7adf1c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 12 Sep 2023 17:23:27 +0200 Subject: [PATCH 07/11] Label objects: refactoring + fix of object/instance indexing when instances are rotated/out-of-bed --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/GCode.cpp | 75 ++-------------- src/libslic3r/GCode.hpp | 2 + src/libslic3r/GCode/LabelObjects.cpp | 122 +++++++++++++++++++++++++++ src/libslic3r/GCode/LabelObjects.hpp | 49 +++++++++++ src/libslic3r/PrintConfig.cpp | 14 +-- src/libslic3r/PrintConfig.hpp | 6 +- 7 files changed, 192 insertions(+), 78 deletions(-) create mode 100644 src/libslic3r/GCode/LabelObjects.cpp create mode 100644 src/libslic3r/GCode/LabelObjects.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 72f741ae4d..609c666505 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -158,6 +158,8 @@ set(SLIC3R_SOURCES GCode/ExtrusionProcessor.hpp GCode/FindReplace.cpp GCode/FindReplace.hpp + GCode/LabelObjects.cpp + GCode/LabelObjects.hpp GCode/GCodeWriter.cpp GCode/GCodeWriter.hpp GCode/PostProcessor.cpp diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 6bd7a06cf8..f2b4e8480b 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -28,6 +28,7 @@ #include "Exception.hpp" #include "ExtrusionEntity.hpp" #include "Geometry/ConvexHull.hpp" +#include "GCode/LabelObjects.hpp" #include "GCode/PrintExtents.hpp" #include "GCode/Thumbnails.hpp" #include "GCode/WipeTower.hpp" @@ -102,69 +103,6 @@ namespace Slic3r { gcode += '\n'; } - - // Accepts vector of PrintObjectPtrs and an object and instance ids. Returns starting tag for label object function. - static std::string label_object_start(LabelObjects label_object_style, GCodeFlavor flavor, const SpanOfConstPtrs& objects, int object_id, int instance_id) - { - int unique_id = 0; - for (size_t idx = 0; idx < size_t(object_id); ++idx) - unique_id += int(objects[idx]->model_object()->instances.size()); - unique_id += instance_id; - - std::string name = objects[object_id]->model_object()->name; - if (label_object_style == LabelObjects::Firmware && objects[object_id]->model_object()->instances.size() > 1u) - name += " (copy " + std::to_string(instance_id) + ")"; - - std::string out; - if (label_object_style == LabelObjects::Octoprint) - out += std::string("; printing object ") + name + " id:" + std::to_string(object_id) + " copy " + std::to_string(instance_id) + "\n"; - else if (label_object_style == LabelObjects::Firmware) { - if (flavor == GCodeFlavor::gcfMarlinFirmware || flavor == GCodeFlavor::gcfMarlinLegacy || flavor == GCodeFlavor::gcfRepRapFirmware) { - out += std::string("M486 S") + std::to_string(unique_id) + "\n"; - out += std::string("M486 A"); - out += (flavor == GCodeFlavor::gcfRepRapFirmware ? (std::string("\"") + name + "\"") : name) + "\n"; - } else { - // Not supported by / implemented for the other firmware flavors. - } - } - return out; - } - - - static std::string label_object_stop(LabelObjects label_object_style, GCodeFlavor flavor, int object_id, int instance_id, const std::string& name) - { - std::string out; - if (label_object_style == LabelObjects::Octoprint) - out += std::string("; stop printing object ") + name + " id:" + std::to_string(object_id) + " copy " + std::to_string(instance_id) + "\n"; - else if (label_object_style == LabelObjects::Firmware) - if (flavor == GCodeFlavor::gcfMarlinFirmware || flavor == GCodeFlavor::gcfMarlinLegacy || flavor == GCodeFlavor::gcfRepRapFirmware) - out += std::string("M486 S-1\n"); - else { - // Not supported by / implemented for the other firmware flavors. - } - return out; - } - - - static std::string label_all_objects(LabelObjects label_objects_style, GCodeFlavor flavor, const Print& print) - { - std::string out; - - if (label_objects_style != LabelObjects::Disabled) { - out += "\n"; - for (size_t object_idx = 0; object_idx < print.objects().size(); ++object_idx) { - for (size_t inst_idx = 0; inst_idx < print.objects()[object_idx]->model_object()->instances.size(); ++inst_idx) { - out += label_object_start(label_objects_style, flavor, print.objects(), object_idx, inst_idx); - out += label_object_stop(label_objects_style, flavor, object_idx, inst_idx, print.objects()[object_idx]->model_object()->name); - } - } - out += "\n"; - } - return out; - } - - - // Return true if tch_prefix is found in custom_gcode static bool custom_gcode_changes_tool(const std::string& custom_gcode, const std::string& tch_prefix, unsigned next_extruder) { @@ -1207,7 +1145,8 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail this->print_machine_envelope(file, print); // Label all objects so printer knows about them since the start. - file.write(label_all_objects(config().gcode_label_objects, config().gcode_flavor, print)); + m_label_objects.init(print); + file.write(m_label_objects.all_objects_header()); // Update output variables after the extruders were initialized. m_placeholder_parser_integration.init(m_writer); @@ -2419,13 +2358,13 @@ void GCodeGenerator::process_layer_single_object( m_avoid_crossing_perimeters.use_external_mp_once(); m_last_obj_copy = this_object_copy; this->set_origin(unscale(offset)); - if (this->config().gcode_label_objects != LabelObjects::Disabled) { + if (this->config().gcode_label_objects != LabelObjectsStyle::Disabled) { for (const PrintObject* po : print_object.print()->objects()) { if (po == &print_object) break; ++object_id; } - gcode += label_object_start(config().gcode_label_objects, config().gcode_flavor, print_object.print()->objects(), object_id, print_instance.instance_id); + gcode += m_label_objects.start_object(print_instance.print_object.instances()[print_instance.instance_id], GCode::LabelObjects::IncludeName::No); } } }; @@ -2603,8 +2542,8 @@ void GCodeGenerator::process_layer_single_object( } } } - if (! first && config().gcode_label_objects != LabelObjects::Disabled) - gcode += label_object_stop(config().gcode_label_objects, config().gcode_flavor, object_id, print_instance.instance_id, print_object.model_object()->name); + if (! first) + gcode += m_label_objects.stop_object(print_instance.print_object.instances()[print_instance.instance_id]); } void GCodeGenerator::apply_print_config(const PrintConfig &print_config) diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index ae1cc333b1..d25113b0d2 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -28,6 +28,7 @@ #include "GCode/CoolingBuffer.hpp" #include "GCode/FindReplace.hpp" #include "GCode/GCodeWriter.hpp" +#include "GCode/LabelObjects.hpp" #include "GCode/PressureEqualizer.hpp" #include "GCode/RetractWhenCrossingPerimeters.hpp" #include "GCode/SmoothPath.hpp" @@ -353,6 +354,7 @@ private: OozePrevention m_ooze_prevention; GCode::Wipe m_wipe; + GCode::LabelObjects m_label_objects; AvoidCrossingPerimeters m_avoid_crossing_perimeters; JPSPathFinder m_avoid_crossing_curled_overhangs; RetractWhenCrossingPerimeters m_retract_when_crossing_perimeters; diff --git a/src/libslic3r/GCode/LabelObjects.cpp b/src/libslic3r/GCode/LabelObjects.cpp new file mode 100644 index 0000000000..dd4378d4fa --- /dev/null +++ b/src/libslic3r/GCode/LabelObjects.cpp @@ -0,0 +1,122 @@ +#include "LabelObjects.hpp" + + +namespace Slic3r::GCode { + + + +void LabelObjects::init(const Print& print) +{ + m_label_objects_style = print.config().gcode_label_objects; + m_flavor = print.config().gcode_flavor; + + if (m_label_objects_style == LabelObjectsStyle::Disabled) + return; + + std::map> model_object_to_print_instances; + + // Iterate over all PrintObjects and their PrintInstances, collect PrintInstances which + // belong to the same ModelObject. + for (const PrintObject* po : print.objects()) + for (const PrintInstance& pi : po->instances()) + model_object_to_print_instances[pi.model_instance->get_object()].emplace_back(&pi); + + // Now go through the map, assign a unique_id to each of the PrintInstances and get the indices of the + // respective ModelObject and ModelInstance so we can use them in the tags. This will maintain + // indices even in case that some instances are rotated (those end up in different PrintObjects) + // or when some are out of bed (these ModelInstances have no corresponding PrintInstances). + int unique_id = 0; + for (const auto& [model_object, print_instances] : model_object_to_print_instances) { + const ModelObjectPtrs& model_objects = model_object->get_model()->objects; + int object_id = int(std::find(model_objects.begin(), model_objects.end(), model_object) - model_objects.begin()); + for (const PrintInstance* const pi : print_instances) { + int instance_id = int(std::find(model_object->instances.begin(), model_object->instances.end(), pi->model_instance) - model_object->instances.begin()); + if (m_label_objects_style != LabelObjectsStyle::Octoprint) { + // OctoPrint comments have always indexed instances from 0, let's keep it that way. + // In the other cases, we will use one-based indexing so the indices match with the one in PrusaSlicer. + ++instance_id; + } + m_label_data.emplace(pi, LabelData{model_object->name, unique_id, object_id, instance_id, print_instances.size() > 1}); + ++unique_id; + } + } +} + + + +std::string LabelObjects::all_objects_header() const +{ + if (m_label_objects_style == LabelObjectsStyle::Disabled) + return std::string(); + + std::string out; + + // Let's sort the values according to unique_id so they are in the same order in which they were added. + std::vector> label_data_sorted; + for (const auto& pi_and_label : m_label_data) + label_data_sorted.emplace_back(pi_and_label); + std::sort(label_data_sorted.begin(), label_data_sorted.end(), [](const auto& ld1, const auto& ld2) { return ld1.second.unique_id < ld2.second.unique_id; }); + + out += "\n"; + for (const auto& [print_instance, label] : label_data_sorted) { + out += start_object(*print_instance, IncludeName::Yes); + out += stop_object(*print_instance); + } + out += "\n"; + return out; +} + + + +std::string LabelObjects::start_object(const PrintInstance& print_instance, IncludeName include_name) const +{ + if (m_label_objects_style == LabelObjectsStyle::Disabled) + return std::string(); + + const LabelData& label = m_label_data.at(&print_instance); + + std::string name = label.name; + if (m_label_objects_style == LabelObjectsStyle::Firmware && label.object_has_more_instances) + name += " (copy " + std::to_string(label.instance_id) + ")"; + + std::string out; + if (m_label_objects_style == LabelObjectsStyle::Octoprint) + out += std::string("; printing object ") + name + " id:" + std::to_string(label.object_id) + " copy " + std::to_string(label.instance_id) + "\n"; + else if (m_label_objects_style == LabelObjectsStyle::Firmware) { + if (m_flavor == GCodeFlavor::gcfMarlinFirmware || m_flavor == GCodeFlavor::gcfMarlinLegacy || m_flavor == GCodeFlavor::gcfRepRapFirmware) { + out += std::string("M486 S") + std::to_string(label.unique_id) + "\n"; + if (include_name == IncludeName::Yes) { + out += std::string("M486 A"); + out += (m_flavor == GCodeFlavor::gcfRepRapFirmware ? (std::string("\"") + name + "\"") : name) + "\n"; + } + } else { + // Not supported by / implemented for the other firmware flavors. + } + } + return out; +} + + + +std::string LabelObjects::stop_object(const PrintInstance& print_instance) const +{ + if (m_label_objects_style == LabelObjectsStyle::Disabled) + return std::string(); + + const LabelData& label = m_label_data.at(&print_instance); + + std::string out; + if (m_label_objects_style == LabelObjectsStyle::Octoprint) + out += std::string("; stop printing object ") + label.name + " id:" + std::to_string(label.object_id) + " copy " + std::to_string(label.instance_id) + "\n"; + else if (m_label_objects_style == LabelObjectsStyle::Firmware) + if (m_flavor == GCodeFlavor::gcfMarlinFirmware || m_flavor == GCodeFlavor::gcfMarlinLegacy || m_flavor == GCodeFlavor::gcfRepRapFirmware) + out += std::string("M486 S-1\n"); + else { + // Not supported by / implemented for the other firmware flavors. + } + return out; +} + + + +} // namespace Slic3r::GCode diff --git a/src/libslic3r/GCode/LabelObjects.hpp b/src/libslic3r/GCode/LabelObjects.hpp new file mode 100644 index 0000000000..b19b46de96 --- /dev/null +++ b/src/libslic3r/GCode/LabelObjects.hpp @@ -0,0 +1,49 @@ +#ifndef slic3r_GCode_LabelObjects_hpp_ +#define slic3r_GCode_LabelObjects_hpp_ + +#include "Print.hpp" + +namespace Slic3r { + +enum GCodeFlavor : unsigned char; +enum class LabelObjectsStyle; + + +namespace GCode { + + //std::string label_object_start(LabelObjectsStyle label_object_style, GCodeFlavor flavor, const SpanOfConstPtrs& objects, int object_id, int instance_id); + //std::string label_object_stop(LabelObjectsStyle label_object_style, GCodeFlavor flavor, int object_id, int instance_id, const std::string& name); + //std::string label_all_objects(LabelObjectsStyle label_objects_style, GCodeFlavor flavor, const Print& print); + + +class LabelObjects { +public: + enum class IncludeName { + No, + Yes + }; + void init(const Print& print); + std::string all_objects_header() const; + std::string start_object(const PrintInstance& print_instance, IncludeName include_name) const; + std::string stop_object(const PrintInstance& print_instance) const; + +private: + struct LabelData { + std::string name; + int unique_id; + int object_id; + int instance_id; + bool object_has_more_instances; + }; + + LabelObjectsStyle m_label_objects_style; + GCodeFlavor m_flavor; + std::unordered_map m_label_data; + +}; + + +} // namespace GCode +} // namespace Slic3r + +#endif // slic3r_GCode_LabelObjects_hpp_ diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index a53a2d7286..38767ea62e 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -229,12 +229,12 @@ static const t_config_enum_values s_keys_map_DraftShield = { }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(DraftShield) -static const t_config_enum_values s_keys_map_LabelObjects = { - { "disabled", int(LabelObjects::Disabled) }, - { "octoprint", int(LabelObjects::Octoprint) }, - { "firmware", int(LabelObjects::Firmware) } +static const t_config_enum_values s_keys_map_LabelObjectsStyle = { + { "disabled", int(LabelObjectsStyle::Disabled) }, + { "octoprint", int(LabelObjectsStyle::Octoprint) }, + { "firmware", int(LabelObjectsStyle::Firmware) } }; -CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(LabelObjects) +CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(LabelObjectsStyle) static const t_config_enum_values s_keys_map_GCodeThumbnailsFormat = { { "PNG", int(GCodeThumbnailsFormat::PNG) }, @@ -1507,13 +1507,13 @@ void PrintConfigDef::init_fff_params() " Firmware = firmware specific G-code (it will be chosen based on firmware flavor and it can end up to be empty).\n\n" "This settings is NOT compatible with Single Extruder Multi Material setup and Wipe into Object / Wipe into Infill."); - def->set_enum({ + def->set_enum({ { "disabled", L("Disabled") }, { "octoprint", L("OctoPrint comments") }, { "firmware", L("Firmware-specific") } }); def->mode = comAdvanced; - def->set_default_value(new ConfigOptionEnum(LabelObjects::Disabled)); + def->set_default_value(new ConfigOptionEnum(LabelObjectsStyle::Disabled)); def = this->add("gcode_substitutions", coStrings); def->label = L("G-code substitutions"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index d3fd0ff429..63ffcde0e3 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -147,7 +147,7 @@ enum DraftShield { dsDisabled, dsLimited, dsEnabled }; -enum class LabelObjects { +enum class LabelObjectsStyle { Disabled, Octoprint, Firmware }; @@ -187,7 +187,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLAPillarConnectionMode) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLASupportTreeType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BrimType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(DraftShield) -CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(LabelObjects) +CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(LabelObjectsStyle) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeThumbnailsFormat) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ForwardCompatibilitySubstitutionRule) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PerimeterGeneratorType) @@ -734,7 +734,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloats, filament_multitool_ramming_flow)) ((ConfigOptionBool, gcode_comments)) ((ConfigOptionEnum, gcode_flavor)) - ((ConfigOptionEnum, gcode_label_objects)) + ((ConfigOptionEnum, gcode_label_objects)) // Triples of strings: "search pattern", "replace with pattern", "attribs" // where "attribs" are one of: // r - regular expression From 6016c6b021af08c1901bd153128af90d0d86b280 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 13 Sep 2023 12:32:36 +0200 Subject: [PATCH 08/11] Label objects: more refactoring (name composed only once) --- src/libslic3r/GCode/LabelObjects.cpp | 36 +++++++++++++++++----------- src/libslic3r/GCode/LabelObjects.hpp | 7 ------ 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/GCode/LabelObjects.cpp b/src/libslic3r/GCode/LabelObjects.cpp index dd4378d4fa..ee57ce9a21 100644 --- a/src/libslic3r/GCode/LabelObjects.cpp +++ b/src/libslic3r/GCode/LabelObjects.cpp @@ -30,14 +30,26 @@ void LabelObjects::init(const Print& print) const ModelObjectPtrs& model_objects = model_object->get_model()->objects; int object_id = int(std::find(model_objects.begin(), model_objects.end(), model_object) - model_objects.begin()); for (const PrintInstance* const pi : print_instances) { + bool object_has_more_instances = print_instances.size() > 1u; int instance_id = int(std::find(model_object->instances.begin(), model_object->instances.end(), pi->model_instance) - model_object->instances.begin()); - if (m_label_objects_style != LabelObjectsStyle::Octoprint) { - // OctoPrint comments have always indexed instances from 0, let's keep it that way. - // In the other cases, we will use one-based indexing so the indices match with the one in PrusaSlicer. - ++instance_id; + + // Now compose the name of the object and define whether indexing is 0 or 1-based. + std::string name = model_object->name; + if (m_label_objects_style == LabelObjectsStyle::Octoprint) { + // use zero-based indexing for objects and instances, as we always have done + name += " id:" + std::to_string(object_id) + " copy " + std::to_string(instance_id); } - m_label_data.emplace(pi, LabelData{model_object->name, unique_id, object_id, instance_id, print_instances.size() > 1}); - ++unique_id; + else if (m_label_objects_style == LabelObjectsStyle::Firmware) { + // use one-based indexing for objects and instances so indices match what we see in PrusaSlicer. + ++object_id; + ++instance_id; + + if (object_has_more_instances) + name += " (Instance " + std::to_string(instance_id) + ")"; + } + + m_label_data.emplace(pi, LabelData{name, unique_id}); + ++unique_id; } } } @@ -63,7 +75,7 @@ std::string LabelObjects::all_objects_header() const out += stop_object(*print_instance); } out += "\n"; - return out; + return out; } @@ -75,19 +87,15 @@ std::string LabelObjects::start_object(const PrintInstance& print_instance, Incl const LabelData& label = m_label_data.at(&print_instance); - std::string name = label.name; - if (m_label_objects_style == LabelObjectsStyle::Firmware && label.object_has_more_instances) - name += " (copy " + std::to_string(label.instance_id) + ")"; - std::string out; if (m_label_objects_style == LabelObjectsStyle::Octoprint) - out += std::string("; printing object ") + name + " id:" + std::to_string(label.object_id) + " copy " + std::to_string(label.instance_id) + "\n"; + out += std::string("; printing object ") + label.name + "\n"; else if (m_label_objects_style == LabelObjectsStyle::Firmware) { if (m_flavor == GCodeFlavor::gcfMarlinFirmware || m_flavor == GCodeFlavor::gcfMarlinLegacy || m_flavor == GCodeFlavor::gcfRepRapFirmware) { out += std::string("M486 S") + std::to_string(label.unique_id) + "\n"; if (include_name == IncludeName::Yes) { out += std::string("M486 A"); - out += (m_flavor == GCodeFlavor::gcfRepRapFirmware ? (std::string("\"") + name + "\"") : name) + "\n"; + out += (m_flavor == GCodeFlavor::gcfRepRapFirmware ? (std::string("\"") + label.name + "\"") : label.name) + "\n"; } } else { // Not supported by / implemented for the other firmware flavors. @@ -107,7 +115,7 @@ std::string LabelObjects::stop_object(const PrintInstance& print_instance) const std::string out; if (m_label_objects_style == LabelObjectsStyle::Octoprint) - out += std::string("; stop printing object ") + label.name + " id:" + std::to_string(label.object_id) + " copy " + std::to_string(label.instance_id) + "\n"; + out += std::string("; stop printing object ") + label.name + "\n"; else if (m_label_objects_style == LabelObjectsStyle::Firmware) if (m_flavor == GCodeFlavor::gcfMarlinFirmware || m_flavor == GCodeFlavor::gcfMarlinLegacy || m_flavor == GCodeFlavor::gcfRepRapFirmware) out += std::string("M486 S-1\n"); diff --git a/src/libslic3r/GCode/LabelObjects.hpp b/src/libslic3r/GCode/LabelObjects.hpp index b19b46de96..a62090530d 100644 --- a/src/libslic3r/GCode/LabelObjects.hpp +++ b/src/libslic3r/GCode/LabelObjects.hpp @@ -11,10 +11,6 @@ enum class LabelObjectsStyle; namespace GCode { - //std::string label_object_start(LabelObjectsStyle label_object_style, GCodeFlavor flavor, const SpanOfConstPtrs& objects, int object_id, int instance_id); - //std::string label_object_stop(LabelObjectsStyle label_object_style, GCodeFlavor flavor, int object_id, int instance_id, const std::string& name); - //std::string label_all_objects(LabelObjectsStyle label_objects_style, GCodeFlavor flavor, const Print& print); - class LabelObjects { public: @@ -31,9 +27,6 @@ private: struct LabelData { std::string name; int unique_id; - int object_id; - int instance_id; - bool object_has_more_instances; }; LabelObjectsStyle m_label_objects_style; From e659e96b0447a07ce3f0c851ddff024cc5129585 Mon Sep 17 00:00:00 2001 From: Justin Schuh Date: Fri, 19 May 2023 08:56:48 -0700 Subject: [PATCH 09/11] Support Klipper exclude_objects feature (PR #10618): comment by @lukasmatena: The functions are not called anywhere because of how I resolved conflicts. I will integrate the functions into current PrusaSlicer in the next commit. --- src/libslic3r/GCode.cpp | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index f2b4e8480b..bea09c5833 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -130,6 +130,43 @@ namespace Slic3r { return ok; } + inline std::string make_klipper_exclude_object_name(const std::string &name, int object_id, int instance_id) + { + constexpr char banned_chars[] = "-. \r\n\v\t\f"; + std::string cleaned_name = name; + const char *next = cleaned_name.c_str(); + while (next = std::strpbrk(next, banned_chars)) + cleaned_name[next - cleaned_name.c_str()] = '_'; + return cleaned_name + "_id_" + std::to_string(object_id) + "_copy_" + std::to_string(instance_id); + } + + // Label excluded objects for Klipper + std::string make_klipper_exclude_object_header(const Print &print) { + std::string output; + int object_id = 0; + for (auto object : print.objects()) { + int instance_id = 0; + for (auto instance : object->instances()) { + char buffer[64]; + output += "EXCLUDE_OBJECT_DEFINE NAME="; + output += make_klipper_exclude_object_name(object->model_object()->name, object_id, instance_id++); + Polygon outline = object->model_object()->convex_hull_2d(instance.model_instance->get_matrix()); + outline.douglas_peucker(50000.f); + auto center = outline.centroid(); + std::snprintf(buffer, sizeof(buffer) - 1, " CENTER=%.3f,%.3f", unscale(center[0]), unscale(center[1])); + output += buffer + std::string(" POLYGON=["); + for (auto point : outline) { + std::snprintf(buffer, sizeof(buffer) - 1, "[%.3f,%.3f],", unscale(point[0]), unscale(point[1])); + output += buffer; + } + output.pop_back(); + output += "]\n"; + } + ++object_id; + } + return output; + } + std::string OozePrevention::pre_toolchange(GCodeGenerator &gcodegen) { std::string gcode; From 501cfb64f9319ce9a728a679c124f67422874b98 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 13 Sep 2023 13:43:46 +0200 Subject: [PATCH 10/11] Label objects: integrating the recently merged PR: - code from @jschuh's functions moved into respective functions in LabelObjects class - name no longer contains object_id, instance_id is newly 1-based - replacing banned characters using std::replace_if --- src/libslic3r/GCode.cpp | 37 ---------------------------- src/libslic3r/GCode/LabelObjects.cpp | 31 ++++++++++++++++++++--- 2 files changed, 28 insertions(+), 40 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index bea09c5833..f2b4e8480b 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -130,43 +130,6 @@ namespace Slic3r { return ok; } - inline std::string make_klipper_exclude_object_name(const std::string &name, int object_id, int instance_id) - { - constexpr char banned_chars[] = "-. \r\n\v\t\f"; - std::string cleaned_name = name; - const char *next = cleaned_name.c_str(); - while (next = std::strpbrk(next, banned_chars)) - cleaned_name[next - cleaned_name.c_str()] = '_'; - return cleaned_name + "_id_" + std::to_string(object_id) + "_copy_" + std::to_string(instance_id); - } - - // Label excluded objects for Klipper - std::string make_klipper_exclude_object_header(const Print &print) { - std::string output; - int object_id = 0; - for (auto object : print.objects()) { - int instance_id = 0; - for (auto instance : object->instances()) { - char buffer[64]; - output += "EXCLUDE_OBJECT_DEFINE NAME="; - output += make_klipper_exclude_object_name(object->model_object()->name, object_id, instance_id++); - Polygon outline = object->model_object()->convex_hull_2d(instance.model_instance->get_matrix()); - outline.douglas_peucker(50000.f); - auto center = outline.centroid(); - std::snprintf(buffer, sizeof(buffer) - 1, " CENTER=%.3f,%.3f", unscale(center[0]), unscale(center[1])); - output += buffer + std::string(" POLYGON=["); - for (auto point : outline) { - std::snprintf(buffer, sizeof(buffer) - 1, "[%.3f,%.3f],", unscale(point[0]), unscale(point[1])); - output += buffer; - } - output.pop_back(); - output += "]\n"; - } - ++object_id; - } - return output; - } - std::string OozePrevention::pre_toolchange(GCodeGenerator &gcodegen) { std::string gcode; diff --git a/src/libslic3r/GCode/LabelObjects.cpp b/src/libslic3r/GCode/LabelObjects.cpp index ee57ce9a21..2104f77e52 100644 --- a/src/libslic3r/GCode/LabelObjects.cpp +++ b/src/libslic3r/GCode/LabelObjects.cpp @@ -46,6 +46,10 @@ void LabelObjects::init(const Print& print) if (object_has_more_instances) name += " (Instance " + std::to_string(instance_id) + ")"; + if (m_flavor == gcfKlipper) { + const std::string banned = "-. \r\n\v\t\f"; + std::replace_if(name.begin(), name.end(), [&banned](char c) { return banned.find(c) != std::string::npos; }, '_'); + } } m_label_data.emplace(pi, LabelData{name, unique_id}); @@ -71,8 +75,25 @@ std::string LabelObjects::all_objects_header() const out += "\n"; for (const auto& [print_instance, label] : label_data_sorted) { - out += start_object(*print_instance, IncludeName::Yes); - out += stop_object(*print_instance); + if (m_flavor == gcfKlipper) { + char buffer[64]; + out += "EXCLUDE_OBJECT_DEFINE NAME=" + label.name; + Polygon outline = print_instance->model_instance->get_object()->convex_hull_2d(print_instance->model_instance->get_matrix()); + assert(! outline.empty()); + outline.douglas_peucker(50000.f); + Point center = outline.centroid(); + std::snprintf(buffer, sizeof(buffer) - 1, " CENTER=%.3f,%.3f", unscale(center[0]), unscale(center[1])); + out += buffer + std::string(" POLYGON=["); + for (const Point& point : outline) { + std::snprintf(buffer, sizeof(buffer) - 1, "[%.3f,%.3f],", unscale(point[0]), unscale(point[1])); + out += buffer; + } + out.pop_back(); + out += "]\n"; + } else { + out += start_object(*print_instance, IncludeName::Yes); + out += stop_object(*print_instance); + } } out += "\n"; return out; @@ -97,7 +118,9 @@ std::string LabelObjects::start_object(const PrintInstance& print_instance, Incl out += std::string("M486 A"); out += (m_flavor == GCodeFlavor::gcfRepRapFirmware ? (std::string("\"") + label.name + "\"") : label.name) + "\n"; } - } else { + } else if (m_flavor == gcfKlipper) + out += "EXCLUDE_OBJECT_START NAME=" + label.name + "\n"; + else { // Not supported by / implemented for the other firmware flavors. } } @@ -119,6 +142,8 @@ std::string LabelObjects::stop_object(const PrintInstance& print_instance) const else if (m_label_objects_style == LabelObjectsStyle::Firmware) if (m_flavor == GCodeFlavor::gcfMarlinFirmware || m_flavor == GCodeFlavor::gcfMarlinLegacy || m_flavor == GCodeFlavor::gcfRepRapFirmware) out += std::string("M486 S-1\n"); + else if (m_flavor ==gcfKlipper) + out += "EXCLUDE_OBJECT_END NAME=" + label.name + "\n"; else { // Not supported by / implemented for the other firmware flavors. } From f599ddca8f3407460b2c2b6ccf1030c2a2ba9e22 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 13 Sep 2023 16:11:13 +0200 Subject: [PATCH 11/11] Label objects: use non-convex projection if it only contains one (Ex)Polygon, remove unused code, tidy includes --- src/libslic3r/GCode.cpp | 12 ++------- src/libslic3r/GCode/LabelObjects.cpp | 39 ++++++++++++++++++++++++++-- src/libslic3r/GCode/LabelObjects.hpp | 4 +-- 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index f2b4e8480b..dd62029769 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2340,9 +2340,8 @@ void GCodeGenerator::process_layer_single_object( const bool print_wipe_extrusions) { bool first = true; - int object_id = 0; // Delay layer initialization as many layers may not print with all extruders. - auto init_layer_delayed = [this, &print_instance, &layer_to_print, &first, &object_id, &gcode]() { + auto init_layer_delayed = [this, &print_instance, &layer_to_print, &first, &gcode]() { if (first) { first = false; const PrintObject &print_object = print_instance.print_object; @@ -2358,14 +2357,7 @@ void GCodeGenerator::process_layer_single_object( m_avoid_crossing_perimeters.use_external_mp_once(); m_last_obj_copy = this_object_copy; this->set_origin(unscale(offset)); - if (this->config().gcode_label_objects != LabelObjectsStyle::Disabled) { - for (const PrintObject* po : print_object.print()->objects()) { - if (po == &print_object) - break; - ++object_id; - } - gcode += m_label_objects.start_object(print_instance.print_object.instances()[print_instance.instance_id], GCode::LabelObjects::IncludeName::No); - } + gcode += m_label_objects.start_object(print_instance.print_object.instances()[print_instance.instance_id], GCode::LabelObjects::IncludeName::No); } }; diff --git a/src/libslic3r/GCode/LabelObjects.cpp b/src/libslic3r/GCode/LabelObjects.cpp index 2104f77e52..4c32122ad2 100644 --- a/src/libslic3r/GCode/LabelObjects.cpp +++ b/src/libslic3r/GCode/LabelObjects.cpp @@ -1,9 +1,43 @@ #include "LabelObjects.hpp" +#include "ClipperUtils.hpp" +#include "Model.hpp" +#include "Print.hpp" +#include "TriangleMeshSlicer.hpp" + namespace Slic3r::GCode { +namespace { + +Polygon instance_outline(const PrintInstance* pi) +{ + ExPolygons outline; + const ModelObject* mo = pi->model_instance->get_object(); + const ModelInstance* mi = pi->model_instance; + for (const ModelVolume *v : mo->volumes) { + Polygons vol_outline; + vol_outline = project_mesh(v->mesh().its, + mi->get_matrix() * v->get_matrix(), + [] {}); + switch (v->type()) { + case ModelVolumeType::MODEL_PART: outline = union_ex(outline, vol_outline); break; + case ModelVolumeType::NEGATIVE_VOLUME: outline = diff_ex(outline, vol_outline); break; + default:; + } + } + + // The projection may contain multiple polygons, which is not supported by Klipper. + // When that happens, calculate and use a 2d convex hull instead. + if (outline.size() == 1u) + return outline.front().contour; + else + return pi->model_instance->get_object()->convex_hull_2d(pi->model_instance->get_matrix()); +} + +}; // anonymous namespace + void LabelObjects::init(const Print& print) { @@ -78,7 +112,7 @@ std::string LabelObjects::all_objects_header() const if (m_flavor == gcfKlipper) { char buffer[64]; out += "EXCLUDE_OBJECT_DEFINE NAME=" + label.name; - Polygon outline = print_instance->model_instance->get_object()->convex_hull_2d(print_instance->model_instance->get_matrix()); + Polygon outline = instance_outline(print_instance); assert(! outline.empty()); outline.douglas_peucker(50000.f); Point center = outline.centroid(); @@ -139,7 +173,7 @@ std::string LabelObjects::stop_object(const PrintInstance& print_instance) const std::string out; if (m_label_objects_style == LabelObjectsStyle::Octoprint) out += std::string("; stop printing object ") + label.name + "\n"; - else if (m_label_objects_style == LabelObjectsStyle::Firmware) + else if (m_label_objects_style == LabelObjectsStyle::Firmware) { if (m_flavor == GCodeFlavor::gcfMarlinFirmware || m_flavor == GCodeFlavor::gcfMarlinLegacy || m_flavor == GCodeFlavor::gcfRepRapFirmware) out += std::string("M486 S-1\n"); else if (m_flavor ==gcfKlipper) @@ -147,6 +181,7 @@ std::string LabelObjects::stop_object(const PrintInstance& print_instance) const else { // Not supported by / implemented for the other firmware flavors. } + } return out; } diff --git a/src/libslic3r/GCode/LabelObjects.hpp b/src/libslic3r/GCode/LabelObjects.hpp index a62090530d..78add73009 100644 --- a/src/libslic3r/GCode/LabelObjects.hpp +++ b/src/libslic3r/GCode/LabelObjects.hpp @@ -1,12 +1,12 @@ #ifndef slic3r_GCode_LabelObjects_hpp_ #define slic3r_GCode_LabelObjects_hpp_ -#include "Print.hpp" - namespace Slic3r { enum GCodeFlavor : unsigned char; enum class LabelObjectsStyle; +struct PrintInstance; +class Print; namespace GCode {