#include #include "libslic3r/libslic3r.h" #include "libslic3r/Print.hpp" #include "libslic3r/Layer.hpp" #include "libslic3r/GCodeReader.hpp" #include "test_helpers.hpp" #include #include using namespace Slic3r; using namespace Slic3r::Test; SCENARIO("Object layer heights", "[PrintObject]") { GIVEN("A 20mm cube") { WHEN("sliced with a 2mm layer height and a 3mm nozzle") { Slic3r::Print print; Slic3r::Test::init_and_process_print({cube(20)}, print, { { "initial_layer_print_height", 2 }, { "layer_height", 2 }, { "nozzle_diameter", 3 } }); ConstLayerPtrsAdaptor layers = print.objects().front()->layers(); THEN("The output vector has 10 entries") { REQUIRE(layers.size() == 10); } AND_THEN("Each layer is approximately 2mm above the previous Z") { coordf_t last = 0.0; for (size_t i = 0; i < layers.size(); ++ i) { REQUIRE_THAT(layers[i]->print_z - last, Catch::Matchers::WithinAbs(2.0, 1e-4)); last = layers[i]->print_z; } } } WHEN("sliced with a 10mm layer height and an 11mm nozzle") { Slic3r::Print print; Slic3r::Test::init_and_process_print({cube(20)}, print, { { "initial_layer_print_height", 2 }, { "layer_height", 10 }, { "nozzle_diameter", 11 } }); ConstLayerPtrsAdaptor layers = print.objects().front()->layers(); THEN("The output vector has 3 entries") { REQUIRE(layers.size() == 3); } AND_THEN("Layer 0 is at 2mm") { REQUIRE_THAT(layers.front()->print_z, Catch::Matchers::WithinAbs(2.0, 1e-4)); } AND_THEN("Layer 1 is at 12mm") { REQUIRE_THAT(layers[1]->print_z, Catch::Matchers::WithinAbs(12.0, 1e-4)); } } WHEN("sliced with a 15mm layer height and a 16mm nozzle") { Slic3r::Print print; Slic3r::Test::init_and_process_print({cube(20)}, print, { { "initial_layer_print_height", 2 }, { "layer_height", 15 }, { "nozzle_diameter", 16 } }); ConstLayerPtrsAdaptor layers = print.objects().front()->layers(); THEN("The output vector has 2 entries") { REQUIRE(layers.size() == 2); } AND_THEN("Layer 0 is at 2mm") { REQUIRE_THAT(layers[0]->print_z, Catch::Matchers::WithinAbs(2.0, 1e-4)); } AND_THEN("Layer 1 is at 17mm") { REQUIRE_THAT(layers[1]->print_z, Catch::Matchers::WithinAbs(17.0, 1e-4)); } } WHEN("layer height exceeds the nozzle diameter") { // Orca does not clamp an over-large layer height to the nozzle; it // rejects the slice during flow computation. Pin that behavior. THEN("Slicing is rejected") { Slic3r::Print print; REQUIRE_THROWS(Slic3r::Test::init_and_process_print({cube(20)}, print, { { "initial_layer_print_height", 0.3 }, { "layer_height", 0.5 }, { "nozzle_diameter", 0.4 } })); } } } } SCENARIO("Perimeter generation", "[PrintObject]") { GIVEN("20mm cube and default config") { WHEN("make_perimeters() is called") { Slic3r::Print print; Slic3r::Test::init_and_process_print({cube(20)}, print, { { "sparse_infill_density", 0 } }); const PrintObject &object = *print.objects().front(); THEN("Every layer in region 0 has 1 island of perimeters") { for (const Layer *layer : object.layers()) REQUIRE(layer->regions().front()->perimeters.entities.size() == 1); } } WHEN("wall_loops is set to 3") { Slic3r::Print print; Slic3r::Test::init_and_process_print({cube(20)}, print, { { "sparse_infill_density", 0 }, { "wall_loops", 3 } }); const PrintObject &object = *print.objects().front(); THEN("Every layer in region 0 has 3 perimeter loops") { for (const Layer *layer : object.layers()) REQUIRE(layer->regions().front()->perimeters.items_count() == 3); } } } } TEST_CASE("Initial layer height is honored", "[PrintObject]") { const std::string gcode = Slic3r::Test::slice({cube(20)}, { { "initial_layer_print_height", 0.3 }, { "layer_height", 0.2 }, { "z_hop", 0 } // keep recorded Z equal to the printed layer height }); std::set layer_zs; GCodeReader reader; reader.parse_buffer(gcode, [&layer_zs] (GCodeReader& self, const GCodeReader::GCodeLine& line) { if (line.extruding(self) && line.dist_XY(self) > 0) layer_zs.insert(self.z()); }); REQUIRE(layer_zs.size() > 1); REQUIRE_THAT(*layer_zs.begin(), Catch::Matchers::WithinAbs(0.3, 1e-4)); REQUIRE_THAT(*std::next(layer_zs.begin()), Catch::Matchers::WithinAbs(0.5, 1e-4)); }