diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ee7f6cabc6..dd17cc4fdd 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -4201,6 +4201,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.write_format("; EXECUTABLE_BLOCK_START\n"); + // Set for every printer, as the object label comments use these ids too. + assign_object_label_ids(print); // SoftFever if( m_enable_exclude_object) file.write(set_object_info(&print)); @@ -11055,15 +11057,30 @@ inline std::string polygon_to_string(const Polygon &polygon, Print *print, bool gcode << "]"; return gcode.str(); } -// this function iterator PrintObject and assign a seqential id to each object. -// this id is used to generate unique object id for each object. +// Numbers the objects, and their instances both within each object (id) and across the print +// (unique_id). The "; printing object" comments and the exclude-object commands label objects with these ids. +void GCode::assign_object_label_ids(Print &print) +{ + m_instance_names.clear(); + size_t object_id = 0; + size_t unique_id = 0; + for (PrintObject *object : print.objects()) { + object->set_id(object_id++); + size_t inst_id = 0; + for (PrintInstance &inst : object->instances()) { + inst.id = inst_id++; + inst.unique_id = unique_id++; + } + } +} + +// Writes the exclude-object definitions for the ids assign_object_label_ids() set. std::string GCode::set_object_info(Print *print) { const auto gflavor = print->config().gcode_flavor.value; if (print->is_BBL_printer() || (gflavor != gcfKlipper && gflavor != gcfMarlinLegacy && gflavor != gcfMarlinFirmware && gflavor != gcfRepRapFirmware)) return ""; std::ostringstream gcode; - size_t object_id = 0; // Orca: check if we are in pa calib mode if (print->calib_mode() == CalibMode::Calib_PA_Pattern) { BoundingBoxf bbox_bed(print->config().printable_area.values); @@ -11079,14 +11096,8 @@ std::string GCode::set_object_info(Print *print) { } else if (print->calib_mode() == CalibMode::Calib_PA_Line) { // PA_Line has only one object, no EXCLUDE_OBJECT_DEFINE needed } else { - size_t unique_id = 0; - m_instance_names.clear(); for (PrintObject* object : print->objects()) { - object->set_id(object_id++); - size_t inst_id = 0; for (PrintInstance& inst : object->instances()) { - inst.unique_id = unique_id++; - inst.id = inst_id++; // Outlines are in plate coordinates. On a belt printer that is the frame after // the slicing rotation has been undone and before the G-code axis remap and // machine-frame shear: where the object stands on the belt, which is what an diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index c89b646ae2..08894574ed 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -319,6 +319,7 @@ public: bool is_BBL_Printer(); WipeTowerType wipe_tower_type(); + void assign_object_label_ids(Print &print); // SoftFever std::string set_object_info(Print* print); @@ -794,7 +795,7 @@ protected: bool m_enable_exclude_object; std::vector m_label_objects_ids; - // Object label names by instance, built on first use from the ids set_object_info() assigns. + // Object label names by instance, built on first use from the ids assign_object_label_ids() assigns. std::unordered_map m_instance_names; const std::string& instance_name(const PrintInstance &instance); std::string _encode_label_ids_to_base64(std::vector ids); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 070e4f4ddc..11de1d80e4 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -249,9 +249,9 @@ struct PrintInstance // SoftFever // // instance id - size_t id; + size_t id{0}; // Orca: unique id used by marlin/rrf cancel object feature - size_t unique_id; + size_t unique_id{0}; //BBS: instance_shift is too large because of multi-plate, apply without plate offset. Point shift_without_plate_offset() const; @@ -738,7 +738,7 @@ private: // SoftFever // // object id - size_t m_id; + size_t m_id{0}; void apply_conical_overhang(); public: diff --git a/tests/fff_print/test_gcode.cpp b/tests/fff_print/test_gcode.cpp index f63e30a07f..71fe6e16bd 100644 --- a/tests/fff_print/test_gcode.cpp +++ b/tests/fff_print/test_gcode.cpp @@ -50,6 +50,43 @@ TEST_CASE("Klipper object labels name each copy without the characters Klipper c } } +TEST_CASE("Object label comments number each object and copy with or without exclude object", "[GCode]") +{ + // repetier has no exclude-object commands, so set_object_info() writes nothing for it. + // BBL printers write M624/M625 skip blocks whatever exclude_object is set to. + const auto [flavor, exclude_object, bbl] = GENERATE(table({ + {"marlin2", "0", false}, + {"klipper", "0", false}, + {"klipper", "1", false}, + {"repetier", "1", false}, + {"marlin2", "0", true}, + })); + DynamicPrintConfig config = DynamicPrintConfig::full_print_config(); + config.set_deserialize_strict({{"gcode_flavor", flavor}, {"exclude_object", exclude_object}, {"gcode_label_objects", "1"}}); + Print print; + print.is_BBL_printer() = bbl; + Model model; + Test::init_print(std::vector{Test::cube(20.), Test::cube(20.)}, print, model, config, nullptr, false, 2); + arrange_objects(model, BoundingBox{Point::new_scale(0., 0.), Point::new_scale(500., 500.)}, + ArrangeParams{scaled(min_object_distance(config))}); + print.apply(model, config); + + const std::string gcode = Test::gcode(print); + INFO(flavor << ", exclude_object " << exclude_object << ", BBL " << bbl); + for (const char *label : {"id:0 copy 0", "id:0 copy 1", "id:1 copy 0", "id:1 copy 1"}) { + INFO(label); + CHECK(gcode.find("; printing object object.stl " + std::string(label) + "\n") != std::string::npos); + } + if (bbl) + for (const PrintObject *object : print.objects()) + for (const PrintInstance &instance : object->instances()) { + const std::string label_id = std::to_string(instance.model_instance->get_labeled_id()); + INFO("label id " << label_id); + CHECK(gcode.find("; start printing object, unique label id: " + label_id + "\nM624 ") != std::string::npos); + CHECK(gcode.find("; stop printing object, unique label id: " + label_id + "\nM625\n") != std::string::npos); + } +} + TEST_CASE("An exported G-code's settings load back as the settings it was sliced with", "[GCode]") { // Two filaments, so the config block carries the flush matrix already multiplied by flush_multiplier. diff --git a/tests/fff_print/test_slicing_pipeline_hook.cpp b/tests/fff_print/test_slicing_pipeline_hook.cpp index 128f05d81e..500cace206 100644 --- a/tests/fff_print/test_slicing_pipeline_hook.cpp +++ b/tests/fff_print/test_slicing_pipeline_hook.cpp @@ -87,8 +87,8 @@ static std::string strip_nondeterministic_gcode_lines(const std::string& gcode) while (std::getline(in, line)) { if (line.compare(0, 15, "; generated by ") == 0) continue; // wall-clock timestamp if (line.compare(0, 18, "; model label id: ") == 0) continue; // ObjectID-derived - // "; [stop] printing object id:N copy M" / "... unique label id: N" (ObjectID-derived): - if (line.find("printing object") != std::string::npos && line.find(" id:") != std::string::npos) continue; + // "; start/stop printing object, unique label id: N" (ObjectID-derived): + if (line.find("printing object, unique label id: ") != std::string::npos) continue; if (line.find("slicing_pipeline_plugin") != std::string::npos) continue; // config-dump plugin name out += line; out += '\n'; }