diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 1ac7448cee..645d8f416d 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -2235,9 +2235,11 @@ StringObjectException Print::validate(std::vector *warnin //FIXME It is quite expensive to generate object layers just to get the print height! auto layers = generate_object_layers(print_object.slicing_parameters(), layer_height_profile(print_object_idx), print_object.config().precise_z_height.value); if (!layers.empty()) { - effective_max_z = layers.back(); + // The object layers start above the raft. + const double raft_z = print_object.slicing_parameters().object_print_z_min; + effective_max_z = raft_z + layers.back(); last_layer_below_max = layers.size() >= 2 && - 0.5 * (layers[layers.size() - 2] + layers.back()) <= this->config().printable_height + EPSILON; + raft_z + 0.5 * (layers[layers.size() - 2] + layers.back()) <= this->config().printable_height + EPSILON; have_height = true; } } @@ -2245,7 +2247,11 @@ StringObjectException Print::validate(std::vector *warnin if (!have_height) continue; - if (shrinkage_compensation_z != 1. && effective_max_z > (this->config().printable_height / shrinkage_compensation_z + EPSILON)) { + // The layers of a regular printer already carry the Z shrinkage compensation, the belt height does not. + const double compensated_max_z = belt_printer ? effective_max_z * shrinkage_compensation_z : effective_max_z; + const double uncompensated_max_z = belt_printer ? effective_max_z : print_object.slicing_parameters().object_print_z_uncompensated_max; + if (shrinkage_compensation_z != 1. && compensated_max_z > this->config().printable_height + EPSILON && + uncompensated_max_z <= this->config().printable_height + EPSILON) { // The object exceeds the maximum build volume height because of shrinkage compensation. return StringObjectException{ Slic3r::format(_u8L("While the object %1% itself fits the build volume, it exceeds the maximum build volume height because of material shrinkage compensation."), print_object.model_object()->name), diff --git a/tests/fff_print/test_print.cpp b/tests/fff_print/test_print.cpp index 4b76de4b55..1b81eea0ea 100644 --- a/tests/fff_print/test_print.cpp +++ b/tests/fff_print/test_print.cpp @@ -26,6 +26,7 @@ #include #include "libslic3r/Print.hpp" +#include "libslic3r/Slicing.hpp" #include "libslic3r/Layer.hpp" #include "libslic3r/BuildVolume.hpp" #include "libslic3r/Support/TreeModelVolumes.hpp" @@ -1596,3 +1597,58 @@ TEST_CASE("Belt supports reach the belt under a leading overhang", "[Print][belt CHECK(lowest->print_z - floor_under_lowest > -0.2 - EPSILON); } } + +TEST_CASE("The build height check counts the Z shrinkage compensation once", "[Print][validate][Regression]") +{ + // printable_height is 100 mm and Z shrinks to 95 %, so the object is sliced 100 / 95 times taller. + // 92 mm become about 96.8 mm and fit; 97 mm become about 102.1 mm and only the compensation makes them too tall; 101 mm are too tall on their own. + const auto [height, expected] = GENERATE(table({ + { 92., "" }, + { 97., "because of material shrinkage compensation" }, + { 101., "exceeds the maximum build volume height." }, + })); + DynamicPrintConfig config = DynamicPrintConfig::full_print_config(); + config.set_deserialize_strict({ + { "printable_height", 100. }, + { "filament_shrinkage_compensation_z", "95%" }, + { "layer_height", 0.2 }, + { "initial_layer_print_height", 0.2 }, + { "layer_change_gcode", "G92 E0\n" }, // validate() relative-E reset + }); + + Print print; + Model model; + init_print({ make_cube(20., 20., height) }, print, model, config); + const std::string error = print.validate().string; + INFO(error); + if (expected.empty()) + CHECK(error.empty()); + else + CHECK(error.find(expected) != std::string::npos); +} + +TEST_CASE("The build height check counts the raft", "[Print][validate][Regression]") +{ + // printable_height is 100 mm; three raft layers lift the object by 0.9 mm. + const auto [height, fits] = GENERATE(table({ { 98.8, true }, { 99.8, false } })); + DynamicPrintConfig config = DynamicPrintConfig::full_print_config(); + config.set_deserialize_strict({ + { "printable_height", 100. }, + { "raft_layers", 3 }, + { "layer_height", 0.2 }, + { "initial_layer_print_height", 0.2 }, + { "layer_change_gcode", "G92 E0\n" }, // validate() relative-E reset + }); + + Print print; + Model model; + init_print({ make_cube(20., 20., height) }, print, model, config); + const SlicingParameters &slicing_params = print.objects().front()->slicing_parameters(); + REQUIRE_THAT(slicing_params.object_print_z_min, Catch::Matchers::WithinAbs(0.9, EPSILON)); + const std::string error = print.validate().string; + INFO(error); + if (fits) + CHECK(error.empty()); + else + CHECK(error.find("maximum build volume height") != std::string::npos); +}