From c03564f2189319549e489bc9f7fc921fbceba7e5 Mon Sep 17 00:00:00 2001 From: supermerill Date: Tue, 2 Apr 2019 19:18:01 +0200 Subject: [PATCH] restore most loop orientation to ccw. add test_model and test_print tests. Work is needed for them --- doc/How to build - Windows.md | 2 +- src/libslic3r/ExtrusionEntity.hpp | 5 +- src/libslic3r/GCode.cpp | 11 +++ src/libslic3r/Model.cpp | 20 +++-- src/libslic3r/Model.hpp | 8 +- src/libslic3r/PerimeterGenerator.cpp | 4 + src/libslic3r/Print.cpp | 2 + src/libslic3r/Technologies.hpp | 1 + src/libslic3r/TriangleMesh.cpp | 16 ++++ src/libslic3r/TriangleMesh.hpp | 3 + src/test/CMakeLists.txt | 2 + src/test/libslic3r/test_flow.cpp | 4 +- src/test/libslic3r/test_model.cpp | 50 +++++++------ src/test/libslic3r/test_print.cpp | 108 +++++++++++++++------------ 14 files changed, 152 insertions(+), 84 deletions(-) diff --git a/doc/How to build - Windows.md b/doc/How to build - Windows.md index 80fc946be..b37adbd1f 100644 --- a/doc/How to build - Windows.md +++ b/doc/How to build - Windows.md @@ -89,7 +89,7 @@ Then `cd` into the `deps` directory and use these commands to build: You can also use the Visual Studio GUI or other generators as mentioned above. The `DESTDIR` option is the location where the bundle will be installed. -This may be customized. If you leave it empty, the `DESTDIR` will be places inside the same `build` directory. +This may be customized. If you leave it empty, the `DESTDIR` will be places inside the same `build` directory. For using your newly compile deps, use -DCMAKE_INSTALL_PREFIX= in your cmake command for slic3r. Warning: If the `build` directory is nested too deep inside other folders, various file paths during the build become too long and the build might fail due to file writing errors. For this reason, it is recommended to diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 0db875e47..1cdee2de9 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -473,7 +473,10 @@ inline void extrusion_entities_append_loops(ExtrusionEntitiesPtr &dst, Polygons ExtrusionPath path(role, mm3_per_mm, width, height); path.polyline.points = std::move(poly.points); path.polyline.points.push_back(path.polyline.points.front()); - dst.emplace_back(new ExtrusionLoop(std::move(path))); + ExtrusionLoop *loop = new ExtrusionLoop(std::move(path)); + //default to ccw + loop->make_counter_clockwise(); + dst.emplace_back(loop); } } loops.clear(); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index a6550aac2..72e8616e5 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2022,6 +2022,17 @@ std::vector polygon_angles_at_vertices(const Polygon &polygon, const std: std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::string &description, double speed, std::unique_ptr *lower_layer_edge_grid) { +#if DEBUG_EXTRUSION_OUTPUT + std::cout << "extrude loop_" << (original_loop.polygon().is_counter_clockwise() ? "ccw" : "clw") << ": "; + for (const ExtrusionPath &path : original_loop.paths) { + std::cout << ", path{ "; + for (const Point &pt : path.polyline.points) { + std::cout << ", " << floor(100 * unscale(pt.x())) / 100.0 << ":" << floor(100 * unscale(pt.y())) / 100.0; + } + std::cout << "}"; + } + std::cout << "\n"; +#endif // get a copy; don't modify the orientation of the original loop object otherwise // next copies (if any) would not detect the correct orientation ExtrusionLoop loop = original_loop; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index f9db9fea0..0528236f4 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -670,45 +670,49 @@ void ModelObject::assign_new_unique_ids_recursive() // return new ModelObject(parent, *this, true); //} -ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh) +ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh, bool centered) { ModelVolume* v = new ModelVolume(this, mesh); this->volumes.push_back(v); #if ENABLE_VOLUMES_CENTERING_FIXES - v->center_geometry(); + if(centered) + v->center_geometry(); #endif // ENABLE_VOLUMES_CENTERING_FIXES this->invalidate_bounding_box(); return v; } -ModelVolume* ModelObject::add_volume(TriangleMesh &&mesh) +ModelVolume* ModelObject::add_volume(TriangleMesh &&mesh, bool centered) { ModelVolume* v = new ModelVolume(this, std::move(mesh)); this->volumes.push_back(v); #if ENABLE_VOLUMES_CENTERING_FIXES - v->center_geometry(); + if (centered) + v->center_geometry(); #endif // ENABLE_VOLUMES_CENTERING_FIXES this->invalidate_bounding_box(); return v; } -ModelVolume* ModelObject::add_volume(const ModelVolume &other) +ModelVolume* ModelObject::add_volume(const ModelVolume &other, bool centered) { ModelVolume* v = new ModelVolume(this, other); this->volumes.push_back(v); #if ENABLE_VOLUMES_CENTERING_FIXES - v->center_geometry(); + if (centered) + v->center_geometry(); #endif // ENABLE_VOLUMES_CENTERING_FIXES this->invalidate_bounding_box(); return v; } -ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&mesh) +ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&mesh, bool centered) { ModelVolume* v = new ModelVolume(this, other, std::move(mesh)); this->volumes.push_back(v); #if ENABLE_VOLUMES_CENTERING_FIXES - v->center_geometry(); + if (centered) + v->center_geometry(); #endif // ENABLE_VOLUMES_CENTERING_FIXES this->invalidate_bounding_box(); return v; diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 8a48f8ee9..f05939276 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -193,10 +193,10 @@ public: Model* get_model() { return m_model; }; const Model* get_model() const { return m_model; }; - ModelVolume* add_volume(const TriangleMesh &mesh); - ModelVolume* add_volume(TriangleMesh &&mesh); - ModelVolume* add_volume(const ModelVolume &volume); - ModelVolume* add_volume(const ModelVolume &volume, TriangleMesh &&mesh); + ModelVolume* add_volume(const TriangleMesh &mesh, bool centered = true); + ModelVolume* add_volume(TriangleMesh &&mesh, bool centered = true); + ModelVolume* add_volume(const ModelVolume &volume, bool centered = true); + ModelVolume* add_volume(const ModelVolume &volume, TriangleMesh &&mesh, bool centered = true); void delete_volume(size_t idx); void clear_volumes(); bool is_multiparts() const { return volumes.size() > 1; } diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 537bb5e6a..0f55b26e4 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -744,6 +744,10 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops( // let's get it from the sorted collection as it might have been reversed size_t i = idx - sorted_coll.orig_indices.begin(); entities.append(*sorted_coll.entities[i]); + //if thin extrusion is a loop, make it ccw like a normal contour. + if (ExtrusionLoop* loop = dynamic_cast(entities.entities.back())) { + loop->make_counter_clockwise(); + } } else { const PerimeterGeneratorLoop &loop = loops[*idx]; ExtrusionLoop eloop = *dynamic_cast(coll.entities[*idx]); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 275490ec2..aa9afe9e7 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1720,6 +1720,8 @@ void Print::_make_skirt(const PrintObjectPtrs &objects, ExtrusionEntityCollectio first_layer_height // this will be overridden at G-code export time ))); eloop.paths.back().polyline = loop.split_at_first_point(); + //we make it clowkwise, but as it will be reversed, it will be ccw + eloop.make_clockwise(); out.append(eloop); if (m_config.min_skirt_length.value > 0) { // The skirt length is limited. Sum the total amount of filament length extruded, in mm. diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index ea3d87888..bb6fd4d58 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -57,4 +57,5 @@ #define ENABLE_SVG_ICONS (1 && ENABLE_1_42_0_ALPHA8 && ENABLE_TEXTURES_FROM_SVG) +#define DEBUG_EXTRUSION_OUTPUT 0 #endif // _technologies_h_ diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 2b54131f9..766601a40 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -1826,6 +1826,22 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) stl_get_size(&lower->stl); } +Pointf3s TriangleMesh::vertices() +{ + Pointf3s tmp{}; + if (this->repaired) { + if (this->stl.v_shared == nullptr) + stl_generate_shared_vertices(&stl); // build the list of vertices + for (auto i = 0; i < this->stl.stats.shared_vertices; i++) { + const auto& v = this->stl.v_shared[i]; + tmp.emplace_back(Vec3d(v.x(), v.y(), v.z())); + } + } else { + BOOST_LOG_TRIVIAL(warning) << "TriangleMesh", "vertices() requires repair()"; + } + return tmp; +} + // Generate the vertex list for a cube solid of arbitrary size in X/Y/Z. TriangleMesh make_cube(double x, double y, double z) { Vec3d pv[8] = { diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 8d2293360..340513adb 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -77,6 +77,9 @@ public: stl_file stl; bool repaired; + +/// --- for tests ----- /// + Pointf3s vertices(); private: void require_shared_vertices(); diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index a97c2d2f3..37ffdc3f4 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -15,6 +15,8 @@ set(SLIC3R_TEST_SOURCES libslic3r/test_flow.cpp libslic3r/test_gcodewriter.cpp libslic3r/test_geometry.cpp + libslic3r/test_model.cpp + libslic3r/test_print.cpp ) if (NOT TARGET Catch) diff --git a/src/test/libslic3r/test_flow.cpp b/src/test/libslic3r/test_flow.cpp index 65f0287aa..cab1d4a12 100644 --- a/src/test/libslic3r/test_flow.cpp +++ b/src/test/libslic3r/test_flow.cpp @@ -42,8 +42,8 @@ SCENARIO("Extrusion width specifics", "[!mayfail]") { std::string gcode_filepath(""); Slic3r::Test::gcode(gcode_filepath, print); GCodeReader parser {Slic3r::GCodeReader()}; - const auto layer_height { config->opt_float("layer_height") }; - std::string gcode_from_file{ read_to_string(gcode_filepath) }; + const double layer_height = config->opt_float("layer_height"); + std::string gcode_from_file= read_to_string(gcode_filepath); parser.parse_buffer(gcode_from_file, [&E_per_mm_bottom, layer_height] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line) { if (self.z() == Approx(layer_height).margin(0.01)) { // only consider first layer diff --git a/src/test/libslic3r/test_model.cpp b/src/test/libslic3r/test_model.cpp index 009c5255f..f93bfac0b 100644 --- a/src/test/libslic3r/test_model.cpp +++ b/src/test/libslic3r/test_model.cpp @@ -1,47 +1,55 @@ #include -#include "Model.hpp" -#include "test_data.hpp" // get access to init_print, etc +#include "../../libslic3r/Config.hpp" +#include "../../libslic3r/Model.hpp" +#include "../test_data.hpp" // get access to init_print, etc +using namespace Slic3r; using namespace Slic3r::Test; SCENARIO("Model construction") { GIVEN("A Slic3r Model") { - auto model {Slic3r::Model()}; - auto sample_mesh {Slic3r::TriangleMesh::make_cube(20,20,20)}; + Model model; + TriangleMesh sample_mesh = make_cube(20,20,20); sample_mesh.repair(); - auto config {Slic3r::Config::new_from_defaults()}; - std::shared_ptr print = std::make_shared(); - print->apply_config(config); + DynamicPrintConfig *config = Slic3r::DynamicPrintConfig::new_from_defaults(); + Slic3r::Print print; + print.apply_config(*config); + //Slic3r::Test::init_print(print, { sample_mesh }, model, config); WHEN("Model object is added") { - ModelObject* mo {model.add_object()}; + ModelObject* mo = model.add_object(); + mo->name = "cube20"; THEN("Model object list == 1") { REQUIRE(model.objects.size() == 1); } - mo->add_volume(sample_mesh); + mo->add_volume(sample_mesh, false); THEN("Model volume list == 1") { REQUIRE(mo->volumes.size() == 1); } THEN("Model volume modifier is false") { - REQUIRE(mo->volumes.front()->modifier == false); + REQUIRE(mo->volumes.front()->is_modifier() == false); } THEN("Mesh is equivalent to input mesh.") { REQUIRE(sample_mesh.vertices() == mo->volumes.front()->mesh.vertices()); } - ModelInstance* inst {mo->add_instance()}; - inst->rotation = 0; - inst->scaling_factor = 1.0; - model.arrange_objects(print->config.min_object_distance()); - model.center_instances_around_point(Slic3r::Pointf(100,100)); - print->auto_assign_extruders(mo); - print->add_model_object(mo); + ModelInstance* inst = mo->add_instance(); + inst->set_rotation(Vec3d(0,0,0)); + inst->set_scaling_factor(Vec3d(1, 1, 1)); + model.arrange_objects(print.config().min_object_distance()); + model.center_instances_around_point(Slic3r::Vec2d(100,100)); + print.auto_assign_extruders(mo); + //print.add_model_object(mo); + print.apply(model, *config); + print.validate(); THEN("Print works?") { - print->process(); - auto gcode {std::stringstream("")}; - print->export_gcode(gcode, true); - REQUIRE(gcode.str().size() > 0); + std::string gcode_filepath(""); + Slic3r::Test::gcode(gcode_filepath, print); + std::cout << "gcode generation done\n"; + std::string gcode_from_file = read_to_string(gcode_filepath); + REQUIRE(gcode_from_file.size() > 0); + clean_file(gcode_filepath, "gcode"); } } diff --git a/src/test/libslic3r/test_print.cpp b/src/test/libslic3r/test_print.cpp index 14981cfaa..0eea78322 100644 --- a/src/test/libslic3r/test_print.cpp +++ b/src/test/libslic3r/test_print.cpp @@ -1,60 +1,70 @@ #include #include -#include "test_data.hpp" -#include "libslic3r.h" +#include "../test_data.hpp" +#include "../../libslic3r/libslic3r.h" using namespace Slic3r::Test; +using namespace Slic3r; using namespace std::literals; SCENARIO("PrintObject: Perimeter generation") { GIVEN("20mm cube and default config") { - auto config {Slic3r::Config::new_from_defaults()}; - TestMesh m { TestMesh::cube_20x20x20 }; - Slic3r::Model model; - auto event_counter {0U}; + DynamicPrintConfig *config = Slic3r::DynamicPrintConfig::new_from_defaults(); + TestMesh m = TestMesh::cube_20x20x20; + Model model; + unsigned int event_counter = 0U; std::string stage; - int value {0}; + int value = 0; auto callback {[&event_counter, &stage, &value] (int a, const char* b) { stage = std::string(b); event_counter++; value = a; }}; - config->set("fill_density", 0); + config->set_key_value("fill_density", new ConfigOptionPercent(0)); WHEN("make_perimeters() is called") { - auto print {Slic3r::Test::init_print({m}, model, config)}; - const auto& object = *(print->objects.at(0)); - print->objects[0]->make_perimeters(); + Print print; + Slic3r::Test::init_print(print, { m }, model, config); + PrintObject& object = *(print.objects().at(0)); + print.process(); + // there are 66.66666.... layers for 0.3mm in 20mm + //slic3r is rounded (slice at half-layer), slic3rPE is less? + //TODO: check the slic32r why it's not cut at half-layer THEN("67 layers exist in the model") { - REQUIRE(object.layers.size() == 67); + REQUIRE(object.layers().size() == 67); } THEN("Every layer in region 0 has 1 island of perimeters") { - for(auto* layer : object.layers) { - REQUIRE(layer->regions[0]->perimeters.size() == 1); + for(Layer* layer : object.layers()) { + REQUIRE(layer->regions()[0]->perimeters.entities.size() == 1); } } - THEN("Every layer in region 0 has 3 paths in its perimeters list.") { - for(auto* layer : object.layers) { - REQUIRE(layer->regions[0]->perimeters.items_count() == 3); + THEN("Every layer (but top) in region 0 has 3 paths in its perimeters list.") { + LayerPtrs layers = object.layers(); + for (auto layer = layers.begin(); layer != layers.end() - 1; ++layer) { + REQUIRE((*layer)->regions()[0]->perimeters.items_count() == 3); } } + THEN("Top layer in region 0 has 1 path in its perimeters list (only 1 perimeter on top).") { + REQUIRE(object.layers().back()->regions()[0]->perimeters.items_count() == 1); + } } } } SCENARIO("Print: Skirt generation") { GIVEN("20mm cube and default config") { - auto config {Slic3r::Config::new_from_defaults()}; - TestMesh m { TestMesh::cube_20x20x20 }; + DynamicPrintConfig *config = Slic3r::DynamicPrintConfig::new_from_defaults(); + TestMesh m = TestMesh::cube_20x20x20; Slic3r::Model model; - auto event_counter {0U}; + unsigned int event_counter = 0U; std::string stage; - int value {0}; - config->set("skirt_height", 1); - config->set("skirt_distance", 1); + int value = 0; + config->set_key_value("skirt_height", new ConfigOptionInt(1)); + config->set_key_value("skirt_distance", new ConfigOptionFloat(1)); WHEN("Skirts is set to 2 loops") { - config->set("skirts", 2); - auto print {Slic3r::Test::init_print({m}, model, config)}; - print->make_skirt(); + config->set_key_value("skirts", new ConfigOptionInt(2)); + Print print; + Slic3r::Test::init_print(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); + REQUIRE(print.skirt().items_count() == 2); + REQUIRE(print.skirt().flatten().entities.size() == 2); } } } @@ -62,36 +72,40 @@ SCENARIO("Print: Skirt generation") { SCENARIO("Print: Brim generation") { GIVEN("20mm cube and default config, 1mm first layer width") { - auto config {Slic3r::Config::new_from_defaults()}; - TestMesh m { TestMesh::cube_20x20x20 }; + DynamicPrintConfig *config = Slic3r::DynamicPrintConfig::new_from_defaults(); + TestMesh m = TestMesh::cube_20x20x20; Slic3r::Model model; - auto event_counter {0U}; + unsigned int event_counter = 0U; std::string stage; - int value {0}; - config->set("first_layer_extrusion_width", 1); + int value = 0; + config->set_key_value("first_layer_extrusion_width", new ConfigOptionFloatOrPercent(1, false)); WHEN("Brim is set to 3mm") { - config->set("brim_width", 3); - auto print {Slic3r::Test::init_print({m}, model, config)}; - print->make_brim(); + config->set_key_value("brim_width", new ConfigOptionFloat(3)); + Print print; + Slic3r::Test::init_print(print, { m }, model, config); + print.process(); 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") { - config->set("brim_width", 6); - auto print {Slic3r::Test::init_print({m}, model, config)}; - print->make_brim(); + config->set_key_value("brim_width", new ConfigOptionFloat(6)); + Print print; + Slic3r::Test::init_print(print, { m }, model, config); + print.process(); 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") { - config->set("brim_width", 6); - config->set("first_layer_extrusion_width", 0.5); - auto print {Slic3r::Test::init_print({m}, model, config)}; - print->make_brim(); - THEN("Brim Extrusion collection has 12 loops in it") { - REQUIRE(print->brim.items_count() == 12); + config->set_key_value("brim_width", new ConfigOptionFloat(6)); + config->set_key_value("first_layer_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false)); + Print print; + Slic3r::Test::init_print(print, { m }, model, config); + print.process(); + double nbLoops = 6.0 / print.brim_flow().spacing(); + THEN("Brim Extrusion collection has " + std::to_string(nbLoops) + " loops in it (flow="+ std::to_string(print.brim_flow().spacing())+")") { + REQUIRE(print.brim().items_count() == floor(nbLoops)); } } }