diff --git a/tests/data/test_3mf/Geräte/Büchse.3mf b/tests/data/test_3mf/Geräte/Büchse.3mf new file mode 100644 index 000000000..3b5a8350d Binary files /dev/null and b/tests/data/test_3mf/Geräte/Büchse.3mf differ diff --git a/tests/fff_print/CMakeLists.txt b/tests/fff_print/CMakeLists.txt index ac4e2fd3e..c70715678 100644 --- a/tests/fff_print/CMakeLists.txt +++ b/tests/fff_print/CMakeLists.txt @@ -6,6 +6,8 @@ add_executable(${_TEST_NAME}_tests test_flow.cpp test_gcodewriter.cpp test_model.cpp + test_print.cpp + test_printobject.cpp test_skirt_brim.cpp test_trianglemesh.cpp ) diff --git a/tests/fff_print/test_print.cpp b/tests/fff_print/test_print.cpp new file mode 100644 index 000000000..cc23d2fef --- /dev/null +++ b/tests/fff_print/test_print.cpp @@ -0,0 +1,147 @@ +#include + +#include "libslic3r/libslic3r.h" +#include "libslic3r/Print.hpp" + +#include "test_data.hpp" + +using namespace Slic3r; +using namespace Slic3r::Test; + +SCENARIO("PrintObject: Perimeter generation", "[PrintObject]") { + GIVEN("20mm cube and default config") { + Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config(); + TestMesh m = TestMesh::cube_20x20x20; + Slic3r::Model model; + auto event_counter {0U}; + std::string stage; + int value {0}; + auto callback {[&event_counter, &stage, &value] (int a, const char* b) { stage = std::string(b); event_counter++; value = a; }}; + config.set_deserialize("fill_density", "0"); + + WHEN("make_perimeters() is called") { + std::shared_ptr print = Slic3r::Test::init_print({m}, model, config); + print->process(); + const PrintObject& object = *(print->objects().at(0)); + THEN("67 layers exist in the model") { + REQUIRE(object.layers().size() == 66); + } + 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); + } + } + THEN("Every layer in region 0 has 3 paths in its perimeters list.") { + for (const Layer *layer : object.layers()) { + REQUIRE(layer->regions().front()->perimeters.items_count() == 3); + } + } + } + } +} + +SCENARIO("Print: Skirt generation", "[Print]") { + GIVEN("20mm cube and default config") { + Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config(); + TestMesh m { TestMesh::cube_20x20x20 }; + Slic3r::Model model; + auto event_counter {0U}; + std::string stage; + int value {0}; + config.opt_int("skirt_height") = 1; + config.opt_float("skirt_distance") = 1.f; + WHEN("Skirts is set to 2 loops") { + config.opt_int("skirts") = 2; + std::shared_ptr print = Slic3r::Test::init_print({m}, model, config); + print->process(); + THEN("Skirt Extrusion collection has 2 loops in it") { + REQUIRE(print->skirt().items_count() == 2); + REQUIRE(print->skirt().flatten().entities.size() == 2); + } + } + } +} + +void test_is_solid_infill(std::shared_ptr p, size_t obj_id, size_t layer_id ) { + const PrintObject &obj = *(p->objects().at(obj_id)); + const Layer &layer = *(obj.get_layer((int)layer_id)); + + // iterate over all of the regions in the layer + for (const LayerRegion *reg : layer.regions()) { + // for each region, iterate over the fill surfaces + for (const Surface& s : reg->fill_surfaces.surfaces) { + CHECK(s.is_solid()); + } + } +} + +SCENARIO("Print: Changing number of solid surfaces does not cause all surfaces to become internal.", "[Print]") { + GIVEN("sliced 20mm cube and config with top_solid_surfaces = 2 and bottom_solid_surfaces = 1") { + Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config(); + TestMesh m { TestMesh::cube_20x20x20 }; + config.opt_int("top_solid_layers") = 2; + config.opt_int("bottom_solid_layers") = 1; + config.opt_float("layer_height") = 0.5; // get a known number of layers + config.set_deserialize("first_layer_height", "0.5"); + Slic3r::Model model; + auto event_counter {0U}; + std::string stage; + std::shared_ptr print = Slic3r::Test::init_print({m}, model, config); + print->process(); + // Precondition: Ensure that the model has 2 solid top layers (39, 38) + // and one solid bottom layer (0). + test_is_solid_infill(print, 0, 0); // should be solid + test_is_solid_infill(print, 0, 39); // should be solid + test_is_solid_infill(print, 0, 38); // should be solid + WHEN("Model is re-sliced with top_solid_layers == 3") { + config.opt_int("top_solid_layers") = 3; + print->apply(model, config); + print->process(); + THEN("Print object does not have 0 solid bottom layers.") { + test_is_solid_infill(print, 0, 0); + } + AND_THEN("Print object has 3 top solid layers") { + test_is_solid_infill(print, 0, 39); + test_is_solid_infill(print, 0, 38); + test_is_solid_infill(print, 0, 37); + } + } + } +} + +SCENARIO("Print: Brim generation", "[Print]") { + GIVEN("20mm cube and default config, 1mm first layer width") { + Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config(); + TestMesh m { TestMesh::cube_20x20x20 }; + Slic3r::Model model; + auto event_counter {0U}; + std::string stage; + int value {0}; + config.set_deserialize("first_layer_extrusion_width", "1"); + WHEN("Brim is set to 3mm") { + config.opt_float("brim_width") = 3; + std::shared_ptr print = Slic3r::Test::init_print({m}, model, config); + print->process(); + THEN("Brim Extrusion collection has 3 loops in it") { + REQUIRE(print->brim().items_count() == 3); + } + } + WHEN("Brim is set to 6mm") { + config.opt_float("brim_width") = 6; + std::shared_ptr print = Slic3r::Test::init_print({m}, model, config); + print->process(); + THEN("Brim Extrusion collection has 6 loops in it") { + REQUIRE(print->brim().items_count() == 6); + } + } + WHEN("Brim is set to 6mm, extrusion width 0.5mm") { + config.opt_float("brim_width") = 6; + config.set_deserialize("first_layer_extrusion_width", "0.5"); + std::shared_ptr print = Slic3r::Test::init_print({m}, model, config); + print->process(); + THEN("Brim Extrusion collection has 12 loops in it") { + REQUIRE(print->brim().items_count() == 14); + } + } + } +} diff --git a/tests/fff_print/test_printobject.cpp b/tests/fff_print/test_printobject.cpp new file mode 100644 index 000000000..27f3aff54 --- /dev/null +++ b/tests/fff_print/test_printobject.cpp @@ -0,0 +1,86 @@ +#include + +#include "libslic3r/libslic3r.h" +#include "libslic3r/Print.hpp" + +#include "test_data.hpp" + +using namespace Slic3r; +using namespace Slic3r::Test; + +SCENARIO("PrintObject: object layer heights", "[PrintObject]") { + GIVEN("20mm cube and default initial config, initial layer height of 2mm") { + Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config(); + TestMesh m = TestMesh::cube_20x20x20; + Slic3r::Model model; + + config.set_deserialize("first_layer_height", "2"); + + WHEN("generate_object_layers() is called for 2mm layer heights and nozzle diameter of 3mm") { + config.opt_float("nozzle_diameter", 0) = 3; + config.opt_float("layer_height") = 2.0; + std::shared_ptr print = Slic3r::Test::init_print({m}, model, config); + print->process(); + const std::vector &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((layers[i]->print_z - last) == Approx(2.0)); + last = layers[i]->print_z; + } + } + } + WHEN("generate_object_layers() is called for 10mm layer heights and nozzle diameter of 11mm") { + config.opt_float("nozzle_diameter", 0) = 11; + config.opt_float("layer_height") = 10; + std::shared_ptr print = Slic3r::Test::init_print({m}, model, config); + print->process(); + const std::vector &layers = print->objects().front()->layers(); + THEN("The output vector has 3 entries") { + REQUIRE(layers.size() == 3); + } + AND_THEN("Layer 0 is at 2mm") { + REQUIRE(layers.front()->print_z == Approx(2.0)); + } + AND_THEN("Layer 1 is at 12mm") { + REQUIRE(layers[1]->print_z == Approx(12.0)); + } + } + WHEN("generate_object_layers() is called for 15mm layer heights and nozzle diameter of 16mm") { + config.opt_float("nozzle_diameter", 0) = 16; + config.opt_float("layer_height") = 15.0; + std::shared_ptr print = Slic3r::Test::init_print({m}, model, config); + print->process(); + const std::vector &layers = print->objects().front()->layers(); + THEN("The output vector has 2 entries") { + REQUIRE(layers.size() == 2); + } + AND_THEN("Layer 0 is at 2mm") { + REQUIRE(layers[0]->print_z == Approx(2.0)); + } + AND_THEN("Layer 1 is at 17mm") { + REQUIRE(layers[1]->print_z == Approx(17.0)); + } + } +#if 0 + WHEN("generate_object_layers() is called for 15mm layer heights and nozzle diameter of 5mm") { + config.opt_float("nozzle_diameter", 0) = 5; + config.opt_float("layer_height") = 15.0; + std::shared_ptr print = Slic3r::Test::init_print({m}, model, config); + print->process(); + const std::vector &layers = print->objects().front()->layers(); + THEN("The layer height is limited to 5mm.") { + CHECK(layers.size() == 5); + coordf_t last = 2.0; + for (size_t i = 1; i < layers.size(); i++) { + REQUIRE((layers[i]->print_z - last) == Approx(5.0)); + last = layers[i]->print_z; + } + } + } +#endif + } +} diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index cfc7ea833..61d4667b7 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -1,6 +1,7 @@ get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests.cpp + test_3mf.cpp test_geometry.cpp test_polygon.cpp ) diff --git a/tests/libslic3r/test_3mf.cpp b/tests/libslic3r/test_3mf.cpp new file mode 100644 index 000000000..0cde0b0dc --- /dev/null +++ b/tests/libslic3r/test_3mf.cpp @@ -0,0 +1,20 @@ +#include + +#include "libslic3r/Model.hpp" +#include "libslic3r/Format/3mf.hpp" + +using namespace Slic3r; + +SCENARIO("Reading 3mf file") { + GIVEN("umlauts in the path of the file") { + Slic3r::Model model; + WHEN("3mf model is read") { + std::string path = std::string(TEST_DATA_DIR) + "/test_3mf/Geräte/Büchse.3mf"; + DynamicPrintConfig config; + bool ret = Slic3r::load_3mf(path.c_str(), &config, &model, false); + THEN("load should succeed") { + REQUIRE(ret); + } + } + } +}