From fe3cf27394d3fd07557d2411502ea1b49e7adf1c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 12 Sep 2023 17:23:27 +0200 Subject: [PATCH] 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