Ported test_support_material from upstream Slic3r.

Reworked the FFF testing framework & ConfigBase::set_deserialize()
for more compact tests: set_deserialize() now accepts list
of key / value pairs.

Fixed an incorrect assert in LayerRegion.
This commit is contained in:
bubnikv 2019-10-17 17:09:15 +02:00
parent 0ee78543a4
commit c228a49fe0
13 changed files with 769 additions and 536 deletions

View File

@ -438,6 +438,14 @@ bool ConfigBase::set_deserialize(const t_config_option_key &opt_key_src, const s
return this->set_deserialize_raw(opt_key, value, append); return this->set_deserialize_raw(opt_key, value, append);
} }
bool ConfigBase::set_deserialize(std::initializer_list<SetDeserializeItem> items)
{
bool deserialized = true;
for (const SetDeserializeItem &item : items)
deserialized &= this->set_deserialize(item.opt_key, item.opt_value, item.append);
return deserialized;
}
bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, const std::string &value, bool append) bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, const std::string &value, bool append)
{ {
t_config_option_key opt_key = opt_key_src; t_config_option_key opt_key = opt_key_src;

View File

@ -1554,6 +1554,8 @@ public:
// Set a configuration value from a string, it will call an overridable handle_legacy() // Set a configuration value from a string, it will call an overridable handle_legacy()
// to resolve renamed and removed configuration keys. // to resolve renamed and removed configuration keys.
bool set_deserialize(const t_config_option_key &opt_key, const std::string &str, bool append = false); bool set_deserialize(const t_config_option_key &opt_key, const std::string &str, bool append = false);
struct SetDeserializeItem { std::string opt_key; std::string opt_value; bool append = false; };
bool set_deserialize(std::initializer_list<SetDeserializeItem> items);
double get_abs_value(const t_config_option_key &opt_key) const; double get_abs_value(const t_config_option_key &opt_key) const;
double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const; double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const;

View File

@ -70,7 +70,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
fill_surfaces fill_surfaces
); );
if (this->layer()->lower_layer != NULL) if (this->layer()->lower_layer != nullptr)
// Cummulative sum of polygons over all the regions. // Cummulative sum of polygons over all the regions.
g.lower_slices = &this->layer()->lower_layer->slices; g.lower_slices = &this->layer()->lower_layer->slices;
@ -130,7 +130,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
bridges.emplace_back(surface); bridges.emplace_back(surface);
} }
if (surface.is_internal()) { if (surface.is_internal()) {
assert(surface.surface_type == stInternal); assert(surface.surface_type == stInternal || surface.surface_type == stInternalSolid);
if (! has_infill && lower_layer != nullptr) if (! has_infill && lower_layer != nullptr)
polygons_append(voids, surface.expolygon); polygons_append(voids, surface.expolygon);
internal.emplace_back(std::move(surface)); internal.emplace_back(std::move(surface));

View File

@ -11,6 +11,7 @@ add_executable(${_TEST_NAME}_tests
test_printgcode.cpp test_printgcode.cpp
test_printobject.cpp test_printobject.cpp
test_skirt_brim.cpp test_skirt_brim.cpp
test_support_material.cpp
test_trianglemesh.cpp test_trianglemesh.cpp
) )
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r) target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)

File diff suppressed because one or more lines are too long

View File

@ -1,12 +1,12 @@
#ifndef SLIC3R_TEST_DATA_HPP #ifndef SLIC3R_TEST_DATA_HPP
#define SLIC3R_TEST_DATA_HPP #define SLIC3R_TEST_DATA_HPP
#include "libslic3r/Point.hpp" #include "libslic3r/Config.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/Geometry.hpp" #include "libslic3r/Geometry.hpp"
#include "libslic3r/Model.hpp" #include "libslic3r/Model.hpp"
#include "libslic3r/Point.hpp"
#include "libslic3r/Print.hpp" #include "libslic3r/Print.hpp"
#include "libslic3r/Config.hpp" #include "libslic3r/TriangleMesh.hpp"
#include <unordered_map> #include <unordered_map>
@ -61,15 +61,24 @@ bool _equiv(const T& a, const T& b) { return std::abs(a - b) < EPSILON; }
template <typename T> template <typename T>
bool _equiv(const T& a, const T& b, double epsilon) { return abs(a - b) < epsilon; } bool _equiv(const T& a, const T& b, double epsilon) { return abs(a - b) < epsilon; }
//Slic3r::Model model(const std::string& model_name, TestMesh m, Vec3d translate = Vec3d(0,0,0), Vec3d scale = Vec3d(1.0,1.0,1.0));
//Slic3r::Model model(const std::string& model_name, TestMesh m, Vec3d translate = Vec3d(0,0,0), double scale = 1.0);
Slic3r::Model model(const std::string& model_name, TriangleMesh&& _mesh); Slic3r::Model model(const std::string& model_name, TriangleMesh&& _mesh);
void init_print(std::vector<TriangleMesh> &&meshes, Slic3r::Print &print, Slic3r::Model& model, const DynamicPrintConfig &config_in, bool comments = false);
void init_print(std::initializer_list<TestMesh> meshes, Slic3r::Print &print, Slic3r::Model& model, const Slic3r::DynamicPrintConfig &config_in = Slic3r::DynamicPrintConfig::full_print_config(), bool comments = false);
void init_print(std::initializer_list<TriangleMesh> meshes, Slic3r::Print &print, Slic3r::Model& model, const Slic3r::DynamicPrintConfig &config_in = Slic3r::DynamicPrintConfig::full_print_config(), bool comments = false);
void init_print(std::initializer_list<TestMesh> meshes, Slic3r::Print &print, Slic3r::Model& model, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items, bool comments = false);
void init_print(std::initializer_list<TriangleMesh> meshes, Slic3r::Print &print, Slic3r::Model& model, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items, bool comments = false);
std::shared_ptr<Print> init_print(std::initializer_list<TestMesh> meshes, Slic3r::Model& model, const Slic3r::DynamicPrintConfig &config_in = Slic3r::DynamicPrintConfig::full_print_config(), bool comments = false); void init_and_process_print(std::initializer_list<TestMesh> meshes, Slic3r::Print &print, const DynamicPrintConfig& config, bool comments = false);
std::shared_ptr<Print> init_print(std::initializer_list<TriangleMesh> meshes, Slic3r::Model& model, const Slic3r::DynamicPrintConfig &config_in = Slic3r::DynamicPrintConfig::full_print_config(), bool comments = false); void init_and_process_print(std::initializer_list<TriangleMesh> meshes, Slic3r::Print &print, const DynamicPrintConfig& config, bool comments = false);
void init_and_process_print(std::initializer_list<TestMesh> meshes, Slic3r::Print &print, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items, bool comments = false);
void init_and_process_print(std::initializer_list<TriangleMesh> meshes, Slic3r::Print &print, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items, bool comments = false);
std::string gcode(std::shared_ptr<Print> print); std::string gcode(Print& print);
std::string slice(std::initializer_list<TestMesh> meshes, const DynamicPrintConfig &config, bool comments = false);
std::string slice(std::initializer_list<TriangleMesh> meshes, const DynamicPrintConfig &config, bool comments = false);
std::string slice(std::initializer_list<TestMesh> meshes, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items, bool comments = false);
std::string slice(std::initializer_list<TriangleMesh> meshes, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items, bool comments = false);
} } // namespace Slic3r::Test } } // namespace Slic3r::Test

View File

@ -19,16 +19,19 @@ SCENARIO("Extrusion width specifics", "[!mayfail]") {
GIVEN("A config with a skirt, brim, some fill density, 3 perimeters, and 1 bottom solid layer and a 20mm cube mesh") { GIVEN("A config with a skirt, brim, some fill density, 3 perimeters, and 1 bottom solid layer and a 20mm cube mesh") {
// this is a sharedptr // this is a sharedptr
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config(); DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
config.opt_int("skirts") = 1; config.set_deserialize({
config.opt_float("brim_width") = 2.; { "brim_width", "2" },
config.opt_int("perimeters") = 3; { "skirts", "1" },
config.set_deserialize("fill_density", "40%"); { "perimeters", "3" },
config.set_deserialize("first_layer_height", "100%"); { "fill_density", "40%" },
{ "first_layer_height", "100%" }
});
WHEN("first layer width set to 2mm") { WHEN("first layer width set to 2mm") {
Slic3r::Model model; Slic3r::Model model;
config.set_deserialize("first_layer_extrusion_width", "2"); config.set_deserialize("first_layer_extrusion_width", "2");
std::shared_ptr<Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config); Slic3r::Print print;
Slic3r::Test::init_print({TestMesh::cube_20x20x20}, print, model, config);
std::vector<double> E_per_mm_bottom; std::vector<double> E_per_mm_bottom;
std::string gcode = Test::gcode(print); std::string gcode = Test::gcode(print);

View File

@ -10,98 +10,80 @@ using namespace Slic3r::Test;
SCENARIO("PrintObject: Perimeter generation", "[PrintObject]") { SCENARIO("PrintObject: Perimeter generation", "[PrintObject]") {
GIVEN("20mm cube and default config") { GIVEN("20mm cube and default config") {
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
TestMesh m = TestMesh::cube_20x20x20;
Slic3r::Model model;
size_t event_counter = 0;
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") { WHEN("make_perimeters() is called") {
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config); Slic3r::Print print;
print->process(); Slic3r::Test::init_and_process_print({TestMesh::cube_20x20x20}, print, { { "fill_density", "0" } });
const PrintObject& object = *(print->objects().at(0)); const PrintObject &object = *print.objects().front();
THEN("67 layers exist in the model") { THEN("67 layers exist in the model") {
REQUIRE(object.layers().size() == 66); REQUIRE(object.layers().size() == 66);
} }
THEN("Every layer in region 0 has 1 island of perimeters") { THEN("Every layer in region 0 has 1 island of perimeters") {
for (const Layer *layer : object.layers()) { for (const Layer *layer : object.layers())
REQUIRE(layer->regions().front()->perimeters.entities.size() == 1); REQUIRE(layer->regions().front()->perimeters.entities.size() == 1);
} }
}
THEN("Every layer in region 0 has 3 paths in its perimeters list.") { THEN("Every layer in region 0 has 3 paths in its perimeters list.") {
for (const Layer *layer : object.layers()) { for (const Layer *layer : object.layers())
REQUIRE(layer->regions().front()->perimeters.items_count() == 3); REQUIRE(layer->regions().front()->perimeters.items_count() == 3);
} }
} }
} }
} }
}
SCENARIO("Print: Skirt generation", "[Print]") { SCENARIO("Print: Skirt generation", "[Print]") {
GIVEN("20mm cube and default config") { GIVEN("20mm cube and default config") {
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
TestMesh m = TestMesh::cube_20x20x20;
Slic3r::Model model;
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") { WHEN("Skirts is set to 2 loops") {
config.opt_int("skirts") = 2; Slic3r::Print print;
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config); Slic3r::Test::init_and_process_print({TestMesh::cube_20x20x20}, print, {
print->process(); { "skirt_height", "1" },
{ "skirt_distance", "1" },
{ "skirts", "2"}
});
THEN("Skirt Extrusion collection has 2 loops in it") { THEN("Skirt Extrusion collection has 2 loops in it") {
REQUIRE(print->skirt().items_count() == 2); REQUIRE(print.skirt().items_count() == 2);
REQUIRE(print->skirt().flatten().entities.size() == 2); REQUIRE(print.skirt().flatten().entities.size() == 2);
} }
} }
} }
} }
void test_is_solid_infill(std::shared_ptr<Slic3r::Print> 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]") { 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") { GIVEN("sliced 20mm cube and config with top_solid_surfaces = 2 and bottom_solid_surfaces = 1") {
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config(); Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
TestMesh m = TestMesh::cube_20x20x20; config.set_deserialize({
config.opt_int("top_solid_layers") = 2; { "top_solid_layers", "2" },
config.opt_int("bottom_solid_layers") = 1; { "bottom_solid_layers", "1" },
config.opt_float("layer_height") = 0.5; // get a known number of layers { "layer_height", "0.5" }, // get a known number of layers
config.set_deserialize("first_layer_height", "0.5"); { "first_layer_height", "0.5" }
});
Slic3r::Print print;
Slic3r::Model model; Slic3r::Model model;
std::string stage; Slic3r::Test::init_print({TestMesh::cube_20x20x20}, print, model, config);
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
print->process();
// Precondition: Ensure that the model has 2 solid top layers (39, 38) // Precondition: Ensure that the model has 2 solid top layers (39, 38)
// and one solid bottom layer (0). // and one solid bottom layer (0).
test_is_solid_infill(print, 0, 0); // should be solid auto test_is_solid_infill = [&print](size_t obj_id, size_t layer_id) {
test_is_solid_infill(print, 0, 39); // should be solid const Layer &layer = *(print.objects().at(obj_id)->get_layer((int)layer_id));
test_is_solid_infill(print, 0, 38); // should be solid // iterate over all of the regions in the layer
for (const LayerRegion *region : layer.regions()) {
// for each region, iterate over the fill surfaces
for (const Surface &surface : region->fill_surfaces.surfaces)
CHECK(surface.is_solid());
}
};
print.process();
test_is_solid_infill(0, 0); // should be solid
test_is_solid_infill(0, 39); // should be solid
test_is_solid_infill(0, 38); // should be solid
WHEN("Model is re-sliced with top_solid_layers == 3") { WHEN("Model is re-sliced with top_solid_layers == 3") {
config.opt_int("top_solid_layers") = 3; config.opt_int("top_solid_layers") = 3;
print->apply(model, config); print.apply(model, config);
print->process(); print.process();
THEN("Print object does not have 0 solid bottom layers.") { THEN("Print object does not have 0 solid bottom layers.") {
test_is_solid_infill(print, 0, 0); test_is_solid_infill(0, 0);
} }
AND_THEN("Print object has 3 top solid layers") { AND_THEN("Print object has 3 top solid layers") {
test_is_solid_infill(print, 0, 39); test_is_solid_infill(0, 39);
test_is_solid_infill(print, 0, 38); test_is_solid_infill(0, 38);
test_is_solid_infill(print, 0, 37); test_is_solid_infill(0, 37);
} }
} }
} }
@ -109,35 +91,36 @@ SCENARIO("Print: Changing number of solid surfaces does not cause all surfaces t
SCENARIO("Print: Brim generation", "[Print]") { SCENARIO("Print: Brim generation", "[Print]") {
GIVEN("20mm cube and default config, 1mm first layer width") { 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;
std::string stage;
int value = 0;
config.set_deserialize("first_layer_extrusion_width", "1");
WHEN("Brim is set to 3mm") { WHEN("Brim is set to 3mm") {
config.opt_float("brim_width") = 3; Slic3r::Print print;
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config); Slic3r::Test::init_and_process_print({TestMesh::cube_20x20x20}, print, {
print->process(); { "first_layer_extrusion_width", "1" },
{ "brim_width", "3" }
});
THEN("Brim Extrusion collection has 3 loops in it") { THEN("Brim Extrusion collection has 3 loops in it") {
REQUIRE(print->brim().items_count() == 3); REQUIRE(print.brim().items_count() == 3);
} }
} }
WHEN("Brim is set to 6mm") { WHEN("Brim is set to 6mm") {
config.opt_float("brim_width") = 6; Slic3r::Print print;
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config); Slic3r::Test::init_and_process_print({TestMesh::cube_20x20x20}, print, {
print->process(); { "first_layer_extrusion_width", "1" },
{ "brim_width", "6" }
});
THEN("Brim Extrusion collection has 6 loops in it") { THEN("Brim Extrusion collection has 6 loops in it") {
REQUIRE(print->brim().items_count() == 6); REQUIRE(print.brim().items_count() == 6);
} }
} }
WHEN("Brim is set to 6mm, extrusion width 0.5mm") { WHEN("Brim is set to 6mm, extrusion width 0.5mm") {
config.opt_float("brim_width") = 6; Slic3r::Print print;
config.set_deserialize("first_layer_extrusion_width", "0.5"); Slic3r::Test::init_and_process_print({TestMesh::cube_20x20x20}, print, {
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config); { "first_layer_extrusion_width", "1" },
print->process(); { "brim_width", "6" },
{ "first_layer_extrusion_width", "0.5" }
});
print.process();
THEN("Brim Extrusion collection has 12 loops in it") { THEN("Brim Extrusion collection has 12 loops in it") {
REQUIRE(print->brim().items_count() == 14); REQUIRE(print.brim().items_count() == 14);
} }
} }
} }

View File

@ -17,16 +17,16 @@ std::regex skirt_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; skirt");
SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") { SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
GIVEN("A default configuration and a print test object") { GIVEN("A default configuration and a print test object") {
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
WHEN("the output is executed with no support material") { WHEN("the output is executed with no support material") {
config.set_deserialize("layer_height", "0.2"); Slic3r::Print print;
config.set_deserialize("first_layer_height", "0.2");
config.set_deserialize("first_layer_extrusion_width", "0");
config.set_deserialize("gcode_comments", "1");
config.set_deserialize("start_gcode", "");
Slic3r::Model model; Slic3r::Model model;
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config); Slic3r::Test::init_print({TestMesh::cube_20x20x20}, print, model, {
{ "layer_height", "0.2" },
{ "first_layer_height", "0.2" },
{ "first_layer_extrusion_width", "0" },
{ "gcode_comments", "1" },
{ "start_gcode", "" }
});
std::string gcode = Slic3r::Test::gcode(print); std::string gcode = Slic3r::Test::gcode(print);
THEN("Some text output is generated.") { THEN("Some text output is generated.") {
REQUIRE(gcode.size() > 0); REQUIRE(gcode.size() > 0);
@ -75,7 +75,7 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
THEN("final Z height is 20mm") { THEN("final Z height is 20mm") {
double final_z = 0.0; double final_z = 0.0;
GCodeReader reader; GCodeReader reader;
reader.apply_config(print->config()); reader.apply_config(print.config());
reader.parse_buffer(gcode, [&final_z] (GCodeReader& self, const GCodeReader::GCodeLine& line) { reader.parse_buffer(gcode, [&final_z] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
final_z = std::max<double>(final_z, static_cast<double>(self.z())); // record the highest Z point we reach final_z = std::max<double>(final_z, static_cast<double>(self.z())); // record the highest Z point we reach
}); });
@ -83,16 +83,18 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
} }
} }
WHEN("output is executed with complete objects and two differently-sized meshes") { WHEN("output is executed with complete objects and two differently-sized meshes") {
Slic3r::Print print;
Slic3r::Model model; Slic3r::Model model;
config.set_deserialize("first_layer_extrusion_width", "0"); Slic3r::Test::init_print({TestMesh::cube_20x20x20,TestMesh::cube_20x20x20}, print, model, {
config.set_deserialize("first_layer_height", "0.3"); { "first_layer_extrusion_width", "0" },
config.set_deserialize("layer_height", "0.2"); { "first_layer_height", "0.3" },
config.set_deserialize("support_material", "0"); { "layer_height", "0.2" },
config.set_deserialize("raft_layers", "0"); { "support_material", "0" },
config.set_deserialize("complete_objects", "1"); { "raft_layers", "0" },
config.set_deserialize("gcode_comments", "1"); { "complete_objects", "1" },
config.set_deserialize("between_objects_gcode", "; between-object-gcode"); { "gcode_comments", "1" },
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20,TestMesh::cube_20x20x20}, model, config); { "between_objects_gcode", "; between-object-gcode" }
});
std::string gcode = Slic3r::Test::gcode(print); std::string gcode = Slic3r::Test::gcode(print);
THEN("Some text output is generated.") { THEN("Some text output is generated.") {
REQUIRE(gcode.size() > 0); REQUIRE(gcode.size() > 0);
@ -115,7 +117,7 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
THEN("final Z height is 20.1mm") { THEN("final Z height is 20.1mm") {
double final_z = 0.0; double final_z = 0.0;
GCodeReader reader; GCodeReader reader;
reader.apply_config(print->config()); reader.apply_config(print.config());
reader.parse_buffer(gcode, [&final_z] (GCodeReader& self, const GCodeReader::GCodeLine& line) { reader.parse_buffer(gcode, [&final_z] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
final_z = std::max(final_z, static_cast<double>(self.z())); // record the highest Z point we reach final_z = std::max(final_z, static_cast<double>(self.z())); // record the highest Z point we reach
}); });
@ -125,7 +127,7 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
double final_z = 0.0; double final_z = 0.0;
bool reset = false; bool reset = false;
GCodeReader reader; GCodeReader reader;
reader.apply_config(print->config()); reader.apply_config(print.config());
reader.parse_buffer(gcode, [&final_z, &reset] (GCodeReader& self, const GCodeReader::GCodeLine& line) { reader.parse_buffer(gcode, [&final_z, &reset] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
if (final_z > 0 && std::abs(self.z() - 0.3) < 0.01 ) { // saw higher Z before this, now it's lower if (final_z > 0 && std::abs(self.z() - 0.3) < 0.01 ) { // saw higher Z before this, now it's lower
reset = true; reset = true;
@ -139,7 +141,7 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
double final_z = 0.0; double final_z = 0.0;
bool reset = false; bool reset = false;
GCodeReader reader; GCodeReader reader;
reader.apply_config(print->config()); reader.apply_config(print.config());
reader.parse_buffer(gcode, [&final_z, &reset] (GCodeReader& self, const GCodeReader::GCodeLine& line) { reader.parse_buffer(gcode, [&final_z, &reset] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
if (final_z > 0 && std::abs(self.z() - 0.3) < 0.01 ) { if (final_z > 0 && std::abs(self.z() - 0.3) < 0.01 ) {
reset = (final_z > 20.0); reset = (final_z > 20.0);
@ -151,13 +153,12 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
} }
} }
WHEN("the output is executed with support material") { WHEN("the output is executed with support material") {
Slic3r::Model model; std::string gcode = ::Test::slice({TestMesh::cube_20x20x20}, {
config.set_deserialize("first_layer_extrusion_width", "0"); { "first_layer_extrusion_width", "0" },
config.set_deserialize("support_material", "1"); { "support_material", "1" },
config.set_deserialize("raft_layers", "3"); { "raft_layers", "3" },
config.set_deserialize("gcode_comments", "1"); { "gcode_comments", "1" }
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config); });
std::string gcode = Slic3r::Test::gcode(print);
THEN("Some text output is generated.") { THEN("Some text output is generated.") {
REQUIRE(gcode.size() > 0); REQUIRE(gcode.size() > 0);
} }
@ -175,10 +176,9 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
} }
} }
WHEN("the output is executed with a separate first layer extrusion width") { WHEN("the output is executed with a separate first layer extrusion width") {
Slic3r::Model model; std::string gcode = ::Test::slice({ TestMesh::cube_20x20x20 }, {
config.set_deserialize("first_layer_extrusion_width", "0.5"); { "first_layer_extrusion_width", "0.5" }
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config); });
std::string gcode = Slic3r::Test::gcode(print);
THEN("Some text output is generated.") { THEN("Some text output is generated.") {
REQUIRE(gcode.size() > 0); REQUIRE(gcode.size() > 0);
} }
@ -193,48 +193,46 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
} }
} }
WHEN("Cooling is enabled and the fan is disabled.") { WHEN("Cooling is enabled and the fan is disabled.") {
config.set_deserialize("cooling", "1"); std::string gcode = ::Test::slice({ TestMesh::cube_20x20x20 }, {
config.set_deserialize("disable_fan_first_layers", "5"); { "cooling", "1" },
Slic3r::Model model; { "disable_fan_first_layers", "5" }
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config); });
std::string gcode = Slic3r::Test::gcode(print);
THEN("GCode to disable fan is emitted."){ THEN("GCode to disable fan is emitted."){
REQUIRE(gcode.find("M107") != std::string::npos); REQUIRE(gcode.find("M107") != std::string::npos);
} }
} }
WHEN("end_gcode exists with layer_num and layer_z") { WHEN("end_gcode exists with layer_num and layer_z") {
config.set_deserialize("end_gcode", "; Layer_num [layer_num]\n; Layer_z [layer_z]"); std::string gcode = ::Test::slice({ TestMesh::cube_20x20x20 }, {
config.set_deserialize("layer_height", "0.1"); { "end_gcode", "; Layer_num [layer_num]\n; Layer_z [layer_z]" },
config.set_deserialize("first_layer_height", "0.1"); { "layer_height", "0.1" },
{ "first_layer_height", "0.1" }
Slic3r::Model model; });
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
std::string gcode = Slic3r::Test::gcode(print);
THEN("layer_num and layer_z are processed in the end gcode") { THEN("layer_num and layer_z are processed in the end gcode") {
REQUIRE(gcode.find("; Layer_num 199") != std::string::npos); REQUIRE(gcode.find("; Layer_num 199") != std::string::npos);
REQUIRE(gcode.find("; Layer_z 20") != std::string::npos); REQUIRE(gcode.find("; Layer_z 20") != std::string::npos);
} }
} }
WHEN("current_extruder exists in start_gcode") { WHEN("current_extruder exists in start_gcode") {
config.set_deserialize("start_gcode", "; Extruder [current_extruder]");
{ {
Slic3r::Model model; std::string gcode = ::Test::slice({ TestMesh::cube_20x20x20 }, {
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config); { "start_gcode", "; Extruder [current_extruder]" }
std::string gcode = Slic3r::Test::gcode(print); });
THEN("current_extruder is processed in the start gcode and set for first extruder") { THEN("current_extruder is processed in the start gcode and set for first extruder") {
REQUIRE(gcode.find("; Extruder 0") != std::string::npos); REQUIRE(gcode.find("; Extruder 0") != std::string::npos);
} }
} }
config.set_num_extruders(4);
config.set_deserialize("infill_extruder", "2");
config.set_deserialize("solid_infill_extruder", "2");
config.set_deserialize("perimeter_extruder", "2");
config.set_deserialize("support_material_extruder", "2");
config.set_deserialize("support_material_interface_extruder", "2");
{ {
Slic3r::Model model; DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config); config.set_num_extruders(4);
std::string gcode = Slic3r::Test::gcode(print); config.set_deserialize({
{ "start_gcode", "; Extruder [current_extruder]" },
{ "infill_extruder", "2" },
{ "solid_infill_extruder", "2" },
{ "perimeter_extruder", "2" },
{ "support_material_extruder", "2" },
{ "support_material_interface_extruder", "2" }
});
std::string gcode = Slic3r::Test::slice({TestMesh::cube_20x20x20}, config);
THEN("current_extruder is processed in the start gcode and set for second extruder") { THEN("current_extruder is processed in the start gcode and set for second extruder") {
REQUIRE(gcode.find("; Extruder 1") != std::string::npos); REQUIRE(gcode.find("; Extruder 1") != std::string::npos);
} }
@ -242,15 +240,13 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
} }
WHEN("layer_num represents the layer's index from z=0") { WHEN("layer_num represents the layer's index from z=0") {
config.set_deserialize("complete_objects", "1"); std::string gcode = ::Test::slice({ TestMesh::cube_20x20x20, TestMesh::cube_20x20x20 }, {
config.set_deserialize("gcode_comments", "1"); { "complete_objects", "1" },
config.set_deserialize("layer_gcode", ";Layer:[layer_num] ([layer_z] mm)"); { "gcode_comments", "1" },
config.set_deserialize("layer_height", "1.0"); { "layer_gcode", ";Layer:[layer_num] ([layer_z] mm)" },
config.set_deserialize("first_layer_height", "1.0"); { "layer_height", "1.0" },
{ "first_layer_height", "1.0" }
Slic3r::Model model; });
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20,TestMesh::cube_20x20x20}, model, config);
std::string gcode = Slic3r::Test::gcode(print);
// End of the 1st object. // End of the 1st object.
size_t pos = gcode.find(";Layer:19 "); size_t pos = gcode.find(";Layer:19 ");
THEN("First and second object last layer is emitted") { THEN("First and second object last layer is emitted") {

View File

@ -19,9 +19,10 @@ SCENARIO("PrintObject: object layer heights", "[PrintObject]") {
WHEN("generate_object_layers() is called for 2mm layer heights and nozzle diameter of 3mm") { 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("nozzle_diameter", 0) = 3;
config.opt_float("layer_height") = 2.0; config.opt_float("layer_height") = 2.0;
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config); Slic3r::Print print;
print->process(); Slic3r::Test::init_print({m}, print, model, config);
const std::vector<Slic3r::Layer*> &layers = print->objects().front()->layers(); print.process();
const std::vector<Slic3r::Layer*> &layers = print.objects().front()->layers();
THEN("The output vector has 10 entries") { THEN("The output vector has 10 entries") {
REQUIRE(layers.size() == 10); REQUIRE(layers.size() == 10);
} }
@ -36,9 +37,10 @@ SCENARIO("PrintObject: object layer heights", "[PrintObject]") {
WHEN("generate_object_layers() is called for 10mm layer heights and nozzle diameter of 11mm") { 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("nozzle_diameter", 0) = 11;
config.opt_float("layer_height") = 10; config.opt_float("layer_height") = 10;
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config); Slic3r::Print print;
print->process(); Slic3r::Test::init_print({m}, print, model, config);
const std::vector<Slic3r::Layer*> &layers = print->objects().front()->layers(); print.process();
const std::vector<Slic3r::Layer*> &layers = print.objects().front()->layers();
THEN("The output vector has 3 entries") { THEN("The output vector has 3 entries") {
REQUIRE(layers.size() == 3); REQUIRE(layers.size() == 3);
} }
@ -52,9 +54,10 @@ SCENARIO("PrintObject: object layer heights", "[PrintObject]") {
WHEN("generate_object_layers() is called for 15mm layer heights and nozzle diameter of 16mm") { 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("nozzle_diameter", 0) = 16;
config.opt_float("layer_height") = 15.0; config.opt_float("layer_height") = 15.0;
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config); Slic3r::Print print;
print->process(); Slic3r::Test::init_print({m}, print, model, config);
const std::vector<Slic3r::Layer*> &layers = print->objects().front()->layers(); print.process();
const std::vector<Slic3r::Layer*> &layers = print.objects().front()->layers();
THEN("The output vector has 2 entries") { THEN("The output vector has 2 entries") {
REQUIRE(layers.size() == 2); REQUIRE(layers.size() == 2);
} }
@ -69,9 +72,10 @@ SCENARIO("PrintObject: object layer heights", "[PrintObject]") {
WHEN("generate_object_layers() is called for 15mm layer heights and nozzle diameter of 5mm") { 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("nozzle_diameter", 0) = 5;
config.opt_float("layer_height") = 15.0; config.opt_float("layer_height") = 15.0;
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config); Slic3r::Print print;
print->process(); Slic3r::Test::init_print({m}, print, model, config);
const std::vector<Slic3r::Layer*> &layers = print->objects().front()->layers(); print.process();
const std::vector<Slic3r::Layer*> &layers = print.objects().front()->layers();
THEN("The layer height is limited to 5mm.") { THEN("The layer height is limited to 5mm.") {
CHECK(layers.size() == 5); CHECK(layers.size() == 5);
coordf_t last = 2.0; coordf_t last = 2.0;

View File

@ -12,10 +12,11 @@ using namespace Slic3r::Test;
using namespace Slic3r; using namespace Slic3r;
/// Helper method to find the tool used for the brim (always the first extrusion) /// Helper method to find the tool used for the brim (always the first extrusion)
int get_brim_tool(std::string &gcode, Slic3r::GCodeReader& parser) { static int get_brim_tool(const std::string &gcode)
{
int brim_tool = -1; int brim_tool = -1;
int tool = -1; int tool = -1;
GCodeReader parser;
parser.parse_buffer(gcode, [&tool, &brim_tool] (Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line) parser.parse_buffer(gcode, [&tool, &brim_tool] (Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line)
{ {
// if the command is a T command, set the the current tool // if the command is a T command, set the the current tool
@ -25,75 +26,68 @@ int get_brim_tool(std::string &gcode, Slic3r::GCodeReader& parser) {
brim_tool = tool; brim_tool = tool;
} }
}); });
return brim_tool; return brim_tool;
} }
TEST_CASE("Skirt height is honored") { TEST_CASE("Skirt height is honored") {
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config(); DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
config.opt_int("skirts") = 1; config.set_deserialize({
config.opt_int("skirt_height") = 5; { "skirts", "1" },
config.opt_int("perimeters") = 0; { "skirt_height", "5" },
config.opt_float("support_material_speed") = 99; { "perimeters", "0" },
{ "support_material_speed", "99" },
// avoid altering speeds unexpectedly // avoid altering speeds unexpectedly
config.set_deserialize("cooling", "0"); { "cooling", "0" },
config.set_deserialize("first_layer_speed", "100%"); { "first_layer_speed", "100%" }
double support_speed = config.opt<Slic3r::ConfigOptionFloat>("support_material_speed")->value * MM_PER_MIN; });
std::string gcode;
SECTION("printing a single object") {
gcode = Slic3r::Test::slice({TestMesh::cube_20x20x20}, config);
}
SECTION("printing multiple objects") {
gcode = Slic3r::Test::slice({TestMesh::cube_20x20x20, TestMesh::cube_20x20x20}, config);
}
std::map<double, bool> layers_with_skirt; std::map<double, bool> layers_with_skirt;
std::string gcode; double support_speed = config.opt<Slic3r::ConfigOptionFloat>("support_material_speed")->value * MM_PER_MIN;
GCodeReader parser; GCodeReader parser;
Slic3r::Model model; parser.parse_buffer(gcode, [&layers_with_skirt, &support_speed] (Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line) {
SECTION("printing a single object") {
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
gcode = Slic3r::Test::gcode(print);
}
SECTION("printing multiple objects") {
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20, TestMesh::cube_20x20x20}, model, config);
gcode = Slic3r::Test::gcode(print);
}
parser.parse_buffer(gcode, [&layers_with_skirt, &support_speed] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
{
if (line.extruding(self) && self.f() == Approx(support_speed)) { if (line.extruding(self) && self.f() == Approx(support_speed)) {
layers_with_skirt[self.z()] = 1; layers_with_skirt[self.z()] = 1;
} }
}); });
REQUIRE(layers_with_skirt.size() == (size_t)config.opt_int("skirt_height")); REQUIRE(layers_with_skirt.size() == (size_t)config.opt_int("skirt_height"));
} }
SCENARIO("Original Slic3r Skirt/Brim tests", "[!mayfail]") { SCENARIO("Original Slic3r Skirt/Brim tests", "[!mayfail]") {
Slic3r::GCodeReader parser;
Slic3r::Model model;
std::string gcode;
GIVEN("A default configuration") { GIVEN("A default configuration") {
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config(); DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
config.set_num_extruders(4); config.set_num_extruders(4);
config.opt_float("support_material_speed") = 99; config.set_deserialize({
config.set_deserialize("first_layer_height", "0.3"); { "support_material_speed", "99" },
config.set_deserialize("gcode_comments", "1"); { "first_layer_height", "0.3" },
{ "gcode_comments", "1" },
// avoid altering speeds unexpectedly // avoid altering speeds unexpectedly
config.set_deserialize("cooling", "0"); { "cooling", "0" },
config.set_deserialize("first_layer_speed", "100%"); { "first_layer_speed", "100%" },
// remove noise from top/solid layers // remove noise from top/solid layers
config.opt_int("top_solid_layers") = 0; { "top_solid_layers", "0" },
config.opt_int("bottom_solid_layers") = 1; { "bottom_solid_layers", "1" }
});
WHEN("Brim width is set to 5") { WHEN("Brim width is set to 5") {
config.opt_int("perimeters") = 0; config.set_deserialize({
config.opt_int("skirts") = 0; { "perimeters", "0" },
config.opt_float("brim_width") = 5; { "skirts", "0" },
{ "brim_width", "5" }
});
THEN("Brim is generated") { THEN("Brim is generated") {
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config); std::string gcode = Slic3r::Test::slice({TestMesh::cube_20x20x20}, config);
gcode = Slic3r::Test::gcode(print);
bool brim_generated = false; bool brim_generated = false;
double support_speed = config.opt<Slic3r::ConfigOptionFloat>("support_material_speed")->value * MM_PER_MIN; double support_speed = config.opt<Slic3r::ConfigOptionFloat>("support_material_speed")->value * MM_PER_MIN;
parser.parse_buffer(gcode, [&brim_generated, support_speed] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line) Slic3r::GCodeReader parser;
{ parser.parse_buffer(gcode, [&brim_generated, support_speed] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line) {
if (self.z() == Approx(0.3) || line.new_Z(self) == Approx(0.3)) { if (self.z() == Approx(0.3) || line.new_Z(self) == Approx(0.3)) {
if (line.extruding(self) && self.f() == Approx(support_speed)) { if (line.extruding(self) && self.f() == Approx(support_speed)) {
brim_generated = true; brim_generated = true;
@ -105,125 +99,125 @@ SCENARIO("Original Slic3r Skirt/Brim tests", "[!mayfail]") {
} }
WHEN("Skirt area is smaller than the brim") { WHEN("Skirt area is smaller than the brim") {
config.opt_int("skirts") = 1; config.set_deserialize({
config.opt_float("brim_width") = 10; { "skirts", "1" },
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config); { "brim_width", "10"}
});
THEN("Gcode generates") { THEN("Gcode generates") {
REQUIRE(! Slic3r::Test::gcode(print).empty()); REQUIRE(! Slic3r::Test::slice({TestMesh::cube_20x20x20}, config).empty());
} }
} }
WHEN("Skirt height is 0 and skirts > 0") { WHEN("Skirt height is 0 and skirts > 0") {
config.opt_int("skirts") = 2; config.set_deserialize({
config.opt_int("skirt_height") = 0; { "skirts", "2" },
{ "skirt_height", "0"}
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config); });
THEN("Gcode generates") { THEN("Gcode generates") {
REQUIRE(! Slic3r::Test::gcode(print).empty()); REQUIRE(! Slic3r::Test::slice({TestMesh::cube_20x20x20}, config).empty());
} }
} }
WHEN("Perimeter extruder = 2 and support extruders = 3") { WHEN("Perimeter extruder = 2 and support extruders = 3") {
config.opt_int("skirts") = 0;
config.opt_float("brim_width") = 5;
config.opt_int("perimeter_extruder") = 2;
config.opt_int("support_material_extruder") = 3;
THEN("Brim is printed with the extruder used for the perimeters of first object") { THEN("Brim is printed with the extruder used for the perimeters of first object") {
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config); std::string gcode = Slic3r::Test::slice({TestMesh::cube_20x20x20}, {
gcode = Slic3r::Test::gcode(print); { "skirts", "0" },
int tool = get_brim_tool(gcode, parser); { "brim_width", "5" },
{ "perimeter_extruder", "2" },
{ "support_material_extruder", "3" }
});
int tool = get_brim_tool(gcode);
REQUIRE(tool == config.opt_int("perimeter_extruder") - 1); REQUIRE(tool == config.opt_int("perimeter_extruder") - 1);
} }
} }
WHEN("Perimeter extruder = 2, support extruders = 3, raft is enabled") { WHEN("Perimeter extruder = 2, support extruders = 3, raft is enabled") {
config.opt_int("skirts") = 0;
config.opt_float("brim_width") = 5;
config.opt_int("perimeter_extruder") = 2;
config.opt_int("support_material_extruder") = 3;
config.opt_int("raft_layers") = 1;
THEN("brim is printed with same extruder as skirt") { THEN("brim is printed with same extruder as skirt") {
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config); std::string gcode = Slic3r::Test::slice({TestMesh::cube_20x20x20}, {
gcode = Slic3r::Test::gcode(print); { "skirts", "0" },
int tool = get_brim_tool(gcode, parser); { "brim_width", "5" },
{ "perimeter_extruder", "2" },
{ "support_material_extruder", "3" },
{ "raft_layers", "1" }
});
int tool = get_brim_tool(gcode);
REQUIRE(tool == config.opt_int("support_material_extruder") - 1); REQUIRE(tool == config.opt_int("support_material_extruder") - 1);
} }
} }
WHEN("brim width to 1 with layer_width of 0.5") { WHEN("brim width to 1 with layer_width of 0.5") {
config.opt_int("skirts") = 0; config.set_deserialize({
config.set_deserialize("first_layer_extrusion_width", "0.5"); { "skirts", "0" },
config.opt_float("brim_width") = 1; { "first_layer_extrusion_width", "0.5" },
{ "brim_width", "1" }
});
THEN("2 brim lines") { THEN("2 brim lines") {
Slic3r::Model model; Slic3r::Print print;
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config); Slic3r::Test::init_and_process_print({TestMesh::cube_20x20x20}, print, config);
print->process(); REQUIRE(print.brim().entities.size() == 2);
REQUIRE(print->brim().entities.size() == 2);
} }
} }
#if 0 #if 0
WHEN("brim ears on a square") { WHEN("brim ears on a square") {
config.opt_int("skirts") = 0); config.set_deserialize({
config.set_deserialize("first_layer_extrusion_width", "0.5"); { "skirts", "0" },
config.opt_float("brim_width") = 1; { "first_layer_extrusion_width", "0.5" },
config.set("brim_ears", true); { "brim_width", "1" },
config.set("brim_ears_max_angle", 91); { "brim_ears", "1" },
{ "brim_ears_max_angle", "91" }
Slic3r::Model model; });
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config); Slic3r::Print print;
print->process(); Slic3r::Test::init_and_process_print({TestMesh::cube_20x20x20}, print, config);
THEN("Four brim ears") { THEN("Four brim ears") {
REQUIRE(print->brim.size() == 4); REQUIRE(print.brim().entities.size() == 4);
} }
} }
WHEN("brim ears on a square but with a too small max angle") { WHEN("brim ears on a square but with a too small max angle") {
config.set("skirts", 0); config.set_deserialize({
config.set("first_layer_extrusion_width", 0.5); { "skirts", "0" },
config.set("brim_width", 1); { "first_layer_extrusion_width", "0.5" },
config.set("brim_ears", true); { "brim_width", "1" },
config.set("brim_ears_max_angle", 89); { "brim_ears", "1" },
{ "brim_ears_max_angle", "89" }
});
THEN("no brim") { THEN("no brim") {
Slic3r::Model model; Slic3r::Print print;
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config); Slic3r::Test::init_and_process_print({ TestMesh::cube_20x20x20 }, print, config);
print->process(); REQUIRE(print.brim().entities.size() == 0);
REQUIRE(print->brim.size() == 0);
} }
} }
#endif #endif
WHEN("Object is plated with overhang support and a brim") { WHEN("Object is plated with overhang support and a brim") {
config.opt_float("layer_height") = 0.4; config.set_deserialize({
config.set_deserialize("first_layer_height", "0.4"); { "layer_height", "0.4" },
config.opt_int("skirts") = 1; { "first_layer_height", "0.4" },
config.opt_float("skirt_distance") = 0; { "skirts", "1" },
config.opt_float("support_material_speed") = 99; { "skirt_distance", "0" },
config.opt_int("perimeter_extruder") = 1; { "support_material_speed", "99" },
config.opt_int("support_material_extruder") = 2; { "perimeter_extruder", "1" },
config.opt_int("infill_extruder") = 3; // ensure that a tool command gets emitted. { "support_material_extruder", "2" },
config.set_deserialize("cooling", "0"); // to prevent speeds to be altered { "infill_extruder", "3" }, // ensure that a tool command gets emitted.
config.set_deserialize("first_layer_speed", "100%"); // to prevent speeds to be altered { "cooling", "0" }, // to prevent speeds to be altered
{ "first_layer_speed", "100%" }, // to prevent speeds to be altered
});
Slic3r::Model model; THEN("overhang generates?") {
auto print = Slic3r::Test::init_print({TestMesh::overhang}, model, config); //FIXME does it make sense?
print->process(); REQUIRE(! Slic3r::Test::slice({TestMesh::overhang}, config).empty());
}
// config.set("support_material", true); // to prevent speeds to be altered // config.set("support_material", true); // to prevent speeds to be altered
THEN("skirt length is large enough to contain object with support") { THEN("skirt length is large enough to contain object with support") {
CHECK(config.opt_bool("support_material")); // test is not valid if support material is off CHECK(config.opt_bool("support_material")); // test is not valid if support material is off
std::string gcode = Slic3r::Test::slice({TestMesh::cube_20x20x20}, config);
double support_speed = config.opt<ConfigOptionFloat>("support_material_speed")->value * MM_PER_MIN;
double skirt_length = 0.0; double skirt_length = 0.0;
Points extrusion_points; Points extrusion_points;
int tool = -1; int tool = -1;
GCodeReader parser;
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config); parser.parse_buffer(gcode, [config, &extrusion_points, &tool, &skirt_length, support_speed] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line) {
std::string gcode = Slic3r::Test::gcode(print);
double support_speed = config.opt<ConfigOptionFloat>("support_material_speed")->value * MM_PER_MIN;
parser.parse_buffer(gcode, [config, &extrusion_points, &tool, &skirt_length, support_speed] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
{
// std::cerr << line.cmd() << "\n"; // std::cerr << line.cmd() << "\n";
if (boost::starts_with(line.cmd(), "T")) { if (boost::starts_with(line.cmd(), "T")) {
tool = atoi(line.cmd().data() + 1); tool = atoi(line.cmd().data() + 1);
@ -235,12 +229,10 @@ SCENARIO("Original Slic3r Skirt/Brim tests", "[!mayfail]") {
if (speed == Approx(support_speed) && tool == config.opt_int("perimeter_extruder") - 1) { if (speed == Approx(support_speed) && tool == config.opt_int("perimeter_extruder") - 1) {
// Skirt uses first material extruder, support material speed. // Skirt uses first material extruder, support material speed.
skirt_length += line.dist_XY(self); skirt_length += line.dist_XY(self);
} else { } else
extrusion_points.push_back(Slic3r::Point::new_scale(line.new_X(self), line.new_Y(self))); extrusion_points.push_back(Slic3r::Point::new_scale(line.new_X(self), line.new_Y(self)));
} }
} }
}
if (self.z() == Approx(0.3) || line.new_Z(self) == Approx(0.3)) { if (self.z() == Approx(0.3) || line.new_Z(self) == Approx(0.3)) {
if (line.extruding(self) && self.f() == Approx(support_speed)) { if (line.extruding(self) && self.f() == Approx(support_speed)) {
} }
@ -253,10 +245,8 @@ SCENARIO("Original Slic3r Skirt/Brim tests", "[!mayfail]") {
} }
WHEN("Large minimum skirt length is used.") { WHEN("Large minimum skirt length is used.") {
config.opt_float("min_skirt_length") = 20; config.opt_float("min_skirt_length") = 20;
Slic3r::Model model;
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
THEN("Gcode generation doesn't crash") { THEN("Gcode generation doesn't crash") {
REQUIRE(! Slic3r::Test::gcode(print).empty()); REQUIRE(! Slic3r::Test::slice({TestMesh::cube_20x20x20}, config).empty());
} }
} }
} }

View File

@ -0,0 +1,233 @@
#include <catch2/catch.hpp>
#include "libslic3r/GCodeReader.hpp"
#include "test_data.hpp" // get access to init_print, etc
using namespace Slic3r::Test;
using namespace Slic3r;
TEST_CASE("SupportMaterial: Three raft layers created", "[SupportMaterial]")
{
Slic3r::Print print;
Slic3r::Test::init_and_process_print({ TestMesh::cube_20x20x20 }, print, {
{ "support_material", "1" },
{ "raft_layers", "3" }
});
REQUIRE(print.objects().front()->support_layers().size() == 3);
}
SCENARIO("SupportMaterial: support_layers_z and contact_distance", "[SupportMaterial]")
{
// Box h = 20mm, hole bottom at 5mm, hole height 10mm (top edge at 15mm).
TriangleMesh mesh = Slic3r::Test::mesh(Slic3r::Test::TestMesh::cube_with_hole);
mesh.rotate_x(float(M_PI / 2));
auto check = [](Slic3r::Print &print, bool &first_support_layer_height_ok, bool &layer_height_minimum_ok, bool &layer_height_maximum_ok, bool &top_spacing_ok)
{
const std::vector<Slic3r::SupportLayer*> &support_layers = print.objects().front()->support_layers();
first_support_layer_height_ok = support_layers.front()->print_z == print.default_object_config().first_layer_height.value;
layer_height_minimum_ok = true;
layer_height_maximum_ok = true;
double min_layer_height = print.config().min_layer_height.values.front();
double max_layer_height = print.config().nozzle_diameter.values.front();
if (print.config().max_layer_height.values.front() > EPSILON)
max_layer_height = std::min(max_layer_height, print.config().max_layer_height.values.front());
for (size_t i = 1; i < support_layers.size(); ++ i) {
if (support_layers[i]->print_z - support_layers[i - 1]->print_z < min_layer_height - EPSILON)
layer_height_minimum_ok = false;
if (support_layers[i]->print_z - support_layers[i - 1]->print_z > max_layer_height + EPSILON)
layer_height_maximum_ok = false;
}
#if 0
double expected_top_spacing = print.default_object_config().layer_height + print.config().nozzle_diameter.get_at(0);
bool wrong_top_spacing = 0;
std::vector<coordf_t> top_z { 1.1 };
for (coordf_t top_z_el : top_z) {
// find layer index of this top surface.
size_t layer_id = -1;
for (size_t i = 0; i < support_z.size(); ++ i) {
if (abs(support_z[i] - top_z_el) < EPSILON) {
layer_id = i;
i = static_cast<int>(support_z.size());
}
}
// check that first support layer above this top surface (or the next one) is spaced with nozzle diameter
if (abs(support_z[layer_id + 1] - support_z[layer_id] - expected_top_spacing) > EPSILON &&
abs(support_z[layer_id + 2] - support_z[layer_id] - expected_top_spacing) > EPSILON) {
wrong_top_spacing = 1;
}
}
d = ! wrong_top_spacing;
#else
top_spacing_ok = true;
#endif
};
GIVEN("A print object having one modelObject") {
WHEN("First layer height = 0.4") {
Slic3r::Print print;
Slic3r::Test::init_and_process_print({ mesh }, print, {
{ "support_material", "1" },
{ "layer_height", "0.2" },
{ "first_layer_height", "0.4" },
});
bool a, b, c, d;
check(print, a, b, c, d);
THEN("First layer height is honored") { REQUIRE(a == true); }
THEN("No null or negative support layers") { REQUIRE(b == true); }
THEN("No layers thicker than nozzle diameter") { REQUIRE(c == true); }
// THEN("Layers above top surfaces are spaced correctly") { REQUIRE(d == true); }
}
WHEN("Layer height = 0.2 and, first layer height = 0.3") {
Slic3r::Print print;
Slic3r::Test::init_and_process_print({ mesh }, print, {
{ "support_material", "1" },
{ "layer_height", "0.2" },
{ "first_layer_height", "0.3" },
});
bool a, b, c, d;
check(print, a, b, c, d);
THEN("First layer height is honored") { REQUIRE(a == true); }
THEN("No null or negative support layers") { REQUIRE(b == true); }
THEN("No layers thicker than nozzle diameter") { REQUIRE(c == true); }
// THEN("Layers above top surfaces are spaced correctly") { REQUIRE(d == true); }
}
WHEN("Layer height = nozzle_diameter[0]") {
Slic3r::Print print;
Slic3r::Test::init_and_process_print({ mesh }, print, {
{ "support_material", "1" },
{ "layer_height", "0.2" },
{ "first_layer_height", "0.3" },
});
bool a, b, c, d;
check(print, a, b, c, d);
THEN("First layer height is honored") { REQUIRE(a == true); }
THEN("No null or negative support layers") { REQUIRE(b == true); }
THEN("No layers thicker than nozzle diameter") { REQUIRE(c == true); }
// THEN("Layers above top surfaces are spaced correctly") { REQUIRE(d == true); }
}
}
}
#if 0
// Test 8.
TEST_CASE("SupportMaterial: forced support is generated", "[SupportMaterial]")
{
// Create a mesh & modelObject.
TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20);
Model model = Model();
ModelObject *object = model.add_object();
object->add_volume(mesh);
model.add_default_instances();
model.align_instances_to_origin();
Print print = Print();
std::vector<coordf_t> contact_z = {1.9};
std::vector<coordf_t> top_z = {1.1};
print.default_object_config.support_material_enforce_layers = 100;
print.default_object_config.support_material = 0;
print.default_object_config.layer_height = 0.2;
print.default_object_config.set_deserialize("first_layer_height", "0.3");
print.add_model_object(model.objects[0]);
print.objects.front()->_slice();
SupportMaterial *support = print.objects.front()->_support_material();
auto support_z = support->support_layers_z(contact_z, top_z, print.default_object_config.layer_height);
bool check = true;
for (size_t i = 1; i < support_z.size(); i++) {
if (support_z[i] - support_z[i - 1] <= 0)
check = false;
}
REQUIRE(check == true);
}
// TODO
bool test_6_checks(Print& print)
{
bool has_bridge_speed = true;
// Pre-Processing.
PrintObject* print_object = print.objects.front();
print_object->infill();
SupportMaterial* support_material = print.objects.front()->_support_material();
support_material->generate(print_object);
// TODO but not needed in test 6 (make brims and make skirts).
// Exporting gcode.
// TODO validation found in Simple.pm
return has_bridge_speed;
}
// Test 6.
SCENARIO("SupportMaterial: Checking bridge speed", "[SupportMaterial]")
{
GIVEN("Print object") {
// Create a mesh & modelObject.
TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20);
Model model = Model();
ModelObject *object = model.add_object();
object->add_volume(mesh);
model.add_default_instances();
model.align_instances_to_origin();
Print print = Print();
print.config.brim_width = 0;
print.config.skirts = 0;
print.config.skirts = 0;
print.default_object_config.support_material = 1;
print.default_region_config.top_solid_layers = 0; // so that we don't have the internal bridge over infill.
print.default_region_config.bridge_speed = 99;
print.config.cooling = 0;
print.config.set_deserialize("first_layer_speed", "100%");
WHEN("support_material_contact_distance = 0.2") {
print.default_object_config.support_material_contact_distance = 0.2;
print.add_model_object(model.objects[0]);
bool check = test_6_checks(print);
REQUIRE(check == true); // bridge speed is used.
}
WHEN("support_material_contact_distance = 0") {
print.default_object_config.support_material_contact_distance = 0;
print.add_model_object(model.objects[0]);
bool check = test_6_checks(print);
REQUIRE(check == true); // bridge speed is not used.
}
WHEN("support_material_contact_distance = 0.2 & raft_layers = 5") {
print.default_object_config.support_material_contact_distance = 0.2;
print.default_object_config.raft_layers = 5;
print.add_model_object(model.objects[0]);
bool check = test_6_checks(print);
REQUIRE(check == true); // bridge speed is used.
}
WHEN("support_material_contact_distance = 0 & raft_layers = 5") {
print.default_object_config.support_material_contact_distance = 0;
print.default_object_config.raft_layers = 5;
print.add_model_object(model.objects[0]);
bool check = test_6_checks(print);
REQUIRE(check == true); // bridge speed is not used.
}
}
}
#endif

View File

@ -392,12 +392,13 @@ TEST_CASE("Regression test for issue #4486 - files take forever to slice") {
config.set("first_layer_height", 250); config.set("first_layer_height", 250);
config.set("nozzle_diameter", 500); config.set("nozzle_diameter", 500);
Slic3r::Print print;
Slic3r::Model model; Slic3r::Model model;
auto print = Slic3r::Test::init_print({mesh}, model, config); Slic3r::Test::init_print({mesh}, print, model, config);
print->status_cb = [] (int ln, const std::string& msg) { Slic3r::Log::info("Print") << ln << " " << msg << "\n";}; print.status_cb = [] (int ln, const std::string& msg) { Slic3r::Log::info("Print") << ln << " " << msg << "\n";};
std::future<void> fut = std::async([&print] () { print->process(); }); std::future<void> fut = std::async([&print] () { print.process(); });
std::chrono::milliseconds span {120000}; std::chrono::milliseconds span {120000};
bool timedout {false}; bool timedout {false};
if(fut.wait_for(span) == std::future_status::timeout) { if(fut.wait_for(span) == std::future_status::timeout) {
@ -420,12 +421,13 @@ TEST_CASE("Profile test for issue #4486 - files take forever to slice") {
config.set("nozzle_diameter", 500); config.set("nozzle_diameter", 500);
config.set("fill_density", "5%"); config.set("fill_density", "5%");
Slic3r::Print print;
Slic3r::Model model; Slic3r::Model model;
auto print = Slic3r::Test::init_print({mesh}, model, config); Slic3r::Test::init_print({mesh}, print, model, config);
print->status_cb = [] (int ln, const std::string& msg) { Slic3r::Log::info("Print") << ln << " " << msg << "\n";}; print.status_cb = [] (int ln, const std::string& msg) { Slic3r::Log::info("Print") << ln << " " << msg << "\n";};
print->process(); print.process();
REQUIRE(true); REQUIRE(true);