diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 2b38fa0a51..6797f34821 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2222,6 +2222,22 @@ void GCode::PlaceholderParserIntegration::validate_output_vector_variables() // Collect pairs of object_layer + support_layer sorted by print_z. // object_layer & support_layer are considered to be on the same print_z, if they are not further than EPSILON. +// Belt printers: whether an object layer writes anything, its own extrusions or a belt +// brim band riding on it. Shared by collect_layers_to_print() (which drops the layers +// that do not) and the layer count. +static bool belt_object_layer_prints_something(const PrintObject &object, const Layer &layer) +{ + if (layer.has_extrusions()) + return true; + if (object.has_belt_brim()) { + const auto &by_layer = object.belt_brim_by_layer(); + const size_t id = layer.id(); + if (id < by_layer.size() && ! by_layer[id].empty()) + return true; + } + return false; +} + std::vector GCode::collect_layers_to_print(const PrintObject& object, bool skip_empty_first_layer) { std::vector layers_to_print; @@ -2378,6 +2394,17 @@ std::vector GCode::collect_layers_to_print(const PrintObjec PrintStateBase::WarningLevel::CRITICAL, warning, PrintStateBase::SlicingEmptyGcodeLayers); } + // Belt printers: drop the layers that print nothing (see the by-layer overload), so + // the by-object export writes the same layer changes as the by-layer one. + if (object.print()->config().belt_printer.value) + layers_to_print.erase( + std::remove_if(layers_to_print.begin(), layers_to_print.end(), [&object](const LayerToPrint <p) { + return ! ((ltp.object_layer != nullptr && belt_object_layer_prints_something(object, *ltp.object_layer)) || + (ltp.support_layer != nullptr && ltp.support_layer->has_extrusions()) || + (ltp.belt_brim_band != nullptr && ! ltp.belt_brim_band->fills.empty())); + }), + layers_to_print.end()); + return layers_to_print; } @@ -2448,18 +2475,13 @@ std::vector>> GCode::collec // moves it sees, so a gap folds every later layer into the one before it. if (print.config().belt_printer.value) { auto prints_something = [](const LayerToPrint <p) { - if (ltp.object_layer != nullptr && ltp.object_layer->has_extrusions()) + if (ltp.object_layer != nullptr && ltp.original_object != nullptr && + belt_object_layer_prints_something(*ltp.original_object, *ltp.object_layer)) return true; if (ltp.support_layer != nullptr && ltp.support_layer->has_extrusions()) return true; if (ltp.belt_brim_band != nullptr && ! ltp.belt_brim_band->fills.empty()) return true; - if (ltp.object_layer != nullptr && ltp.original_object != nullptr && ltp.original_object->has_belt_brim()) { - const auto &by_layer = ltp.original_object->belt_brim_by_layer(); - const size_t id = ltp.object_layer->id(); - if (id < by_layer.size() && ! by_layer[id].empty()) - return true; - } return false; }; layers_to_print.erase( @@ -3204,15 +3226,21 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // How many times will be change_layer() called? // change_layer() in turn increments the progress bar status. m_layer_count = 0; + // On a belt, collect_layers_to_print() drops the layers that print nothing (an + // object's empty lead-in), so they must not be counted here either or the layer + // count in the file disagrees with its layer changes. + const bool belt = print.config().belt_printer.value; if (print.config().print_sequence == PrintSequence::ByObject) { // Add each of the object's layers separately. for (auto object : print.objects()) { std::vector zs; zs.reserve(object->layers().size() + object->support_layers().size()); for (auto layer : object->layers()) - zs.push_back(layer->print_z); + if (! belt || belt_object_layer_prints_something(*object, *layer)) + zs.push_back(layer->print_z); for (auto layer : object->support_layers()) - zs.push_back(layer->print_z); + if (! belt || layer->has_extrusions()) + zs.push_back(layer->print_z); // Belt brim apron bands each get their own change_layer() call. for (const BeltBrimBand &band : object->belt_brim_prologue()) zs.push_back(band.print_z); @@ -3232,9 +3260,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato for (auto object : print.objects()) { zs.reserve(zs.size() + object->layers().size() + object->support_layers().size()); for (auto layer : object->layers()) - zs.push_back(layer->print_z); + if (! belt || belt_object_layer_prints_something(*object, *layer)) + zs.push_back(layer->print_z); for (auto layer : object->support_layers()) - zs.push_back(layer->print_z); + if (! belt || layer->has_extrusions()) + zs.push_back(layer->print_z); // See the ByObject branch: apron bands are real printed layers. for (const BeltBrimBand &band : object->belt_brim_prologue()) zs.push_back(band.print_z); diff --git a/tests/fff_print/test_print.cpp b/tests/fff_print/test_print.cpp index 546f3667d5..a84bc3b451 100644 --- a/tests/fff_print/test_print.cpp +++ b/tests/fff_print/test_print.cpp @@ -1362,6 +1362,9 @@ TEST_CASE("Belt G-code has no layer that prints nothing", "[Print][belt][GCode][ { "machine_start_gcode", "T[initial_tool]\n" }, { "layer_change_gcode", "G92 E0\n" }, }); + // Both export paths drop the empty layers and count the layers the same way. + SECTION("by layer") { config.set_deserialize_strict({{ "print_sequence", "by layer" }}); } + SECTION("by object") { config.set_deserialize_strict({{ "print_sequence", "by object" }}); } Print print; Model model; TriangleMesh cube_a(its_make_cube(20., 20., 20.)); @@ -1376,7 +1379,7 @@ TEST_CASE("Belt G-code has no layer that prints nothing", "[Print][belt][GCode][ const std::string gc = gcode(print); REQUIRE(! gc.empty()); - size_t layers = 0, empty = 0, total_header = 0; + size_t layers = 0, empty = 0, total_header = 0, total_count = 0; bool extruded = true; // before the first layer change std::istringstream in(gc); std::string line; @@ -1387,17 +1390,24 @@ TEST_CASE("Belt G-code has no layer that prints nothing", "[Print][belt][GCode][ ++ layers; extruded = false; } else if (line.rfind("; total layer number: ", 0) == 0) { + // Counted by the G-code processor from the layer changes it saw. total_header = size_t(std::atoi(line.c_str() + 22)); + } else if (line.rfind("; total layers count = ", 0) == 0) { + // GCode::m_layer_count, counted up front from the objects' layers; it also + // drives the M73 progress and the total_layer_count placeholder. + total_count = size_t(std::atoi(line.c_str() + 23)); } else if (! extruded && line.rfind("G1 ", 0) == 0 && line.find('E') != std::string::npos && (line.find('X') != std::string::npos || line.find('Y') != std::string::npos)) { extruded = true; } } close_layer(); - INFO("layers " << layers << ", header " << total_header << ", layers without extrusion " << empty); + INFO("layers " << layers << ", header " << total_header << ", count " << total_count + << ", layers without extrusion " << empty); CHECK(layers > 150); // both cubes, 141 layers each, overlapping along the belt CHECK(empty == 0); CHECK(total_header == layers); + CHECK(total_count == layers); } // A part with an overhang on its LEADING side (the end that prints first) needs