Browse Source

Ported test_data.cpp/hpp & test_flow.cpp from upstream slic3r,
thanks @lordofhyphens

bubnikv 5 years ago
parent
commit
67e1eba8e6

+ 6 - 1
tests/fff_print/CMakeLists.txt

@@ -1,5 +1,10 @@
 get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
-add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests.cpp)
+add_executable(${_TEST_NAME}_tests 
+	${_TEST_NAME}_tests.cpp
+	test_data.cpp
+	test_data.hpp
+	test_flow.cpp
+	)
 target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
 set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
 

+ 0 - 11
tests/fff_print/fff_print_tests.cpp

@@ -2,14 +2,3 @@
 #include <catch2/catch.hpp>
 
 #include "libslic3r/libslic3r.h"
-
-namespace {
-
-TEST_CASE("sort_remove_duplicates", "[utils]") {
-	std::vector<int> data_src = { 3, 0, 2, 1, 15, 3, 5, 6, 3, 1, 0 };
-	std::vector<int> data_dst = { 0, 1, 2, 3, 5, 6, 15 };
-	Slic3r::sort_remove_duplicates(data_src);
-    REQUIRE(data_src == data_dst);
-}
-
-}

File diff suppressed because it is too large
+ 78 - 0
tests/fff_print/test_data.cpp


+ 77 - 0
tests/fff_print/test_data.hpp

@@ -0,0 +1,77 @@
+#ifndef SLIC3R_TEST_DATA_HPP
+#define SLIC3R_TEST_DATA_HPP
+
+#include "libslic3r/Point.hpp"
+#include "libslic3r/TriangleMesh.hpp"
+#include "libslic3r/Geometry.hpp"
+#include "libslic3r/Model.hpp"
+#include "libslic3r/Print.hpp"
+#include "libslic3r/Config.hpp"
+
+#include <unordered_map>
+
+namespace Slic3r { namespace Test {
+
+constexpr double MM_PER_MIN = 60.0;
+
+/// Enumeration of test meshes
+enum class TestMesh {
+    A,
+    L,
+    V,
+    _40x10,
+    cube_20x20x20,
+    sphere_50mm,
+    bridge,
+    bridge_with_hole,
+    cube_with_concave_hole,
+    cube_with_hole,
+    gt2_teeth,
+    ipadstand,
+    overhang,
+    pyramid,
+    sloping_hole,
+    slopy_cube,
+    small_dorito,
+    step,
+    two_hollow_squares
+};
+
+// Neccessary for <c++17
+struct TestMeshHash {
+    std::size_t operator()(TestMesh tm) const {
+        return static_cast<std::size_t>(tm);
+    }
+};
+
+/// Mesh enumeration to name mapping
+extern const std::unordered_map<TestMesh, const char*, TestMeshHash> mesh_names;
+
+/// Port of Slic3r::Test::mesh
+/// Basic cubes/boxes should call TriangleMesh::make_cube() directly and rescale/translate it
+TriangleMesh mesh(TestMesh m);
+
+TriangleMesh mesh(TestMesh m, Vec3d translate, Vec3d scale = Vec3d(1.0, 1.0, 1.0));
+TriangleMesh mesh(TestMesh m, Vec3d translate, double scale = 1.0);
+
+/// Templated function to see if two values are equivalent (+/- epsilon)
+template <typename T>
+bool _equiv(const T& a, const T& b) { return abs(a - b) < Slic3r::Geometry::epsilon; }
+
+template <typename T>
+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);
+
+std::shared_ptr<Print> init_print(std::initializer_list<TestMesh> meshes, Slic3r::Model& model, std::shared_ptr<Slic3r::DynamicPrintConfig> _config = std::shared_ptr<Slic3r::DynamicPrintConfig>(Slic3r::DynamicPrintConfig::new_from_defaults()), bool comments = false);
+std::shared_ptr<Print> init_print(std::initializer_list<TriangleMesh> meshes, Slic3r::Model& model, std::shared_ptr<Slic3r::DynamicPrintConfig> _config = std::shared_ptr<Slic3r::DynamicPrintConfig>(Slic3r::DynamicPrintConfig::new_from_defaults()), bool comments = false);
+
+std::string gcode(std::shared_ptr<Print> print);
+
+} } // namespace Slic3r::Test
+
+
+#endif // SLIC3R_TEST_DATA_HPP

+ 203 - 0
tests/fff_print/test_flow.cpp

@@ -0,0 +1,203 @@
+#include <catch2/catch.hpp>
+
+#include <numeric>
+#include <sstream>
+
+#include "test_data.hpp" // get access to init_print, etc
+
+#include "libslic3r/Config.hpp"
+#include "libslic3r/Model.hpp"
+#include "libslic3r/Config.hpp"
+#include "libslic3r/GCodeReader.hpp"
+#include "libslic3r/Flow.hpp"
+#include "libslic3r/libslic3r.h"
+
+using namespace Slic3r::Test;
+using namespace Slic3r;
+
+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") {
+        // this is a sharedptr
+        std::shared_ptr<DynamicPrintConfig> config(Slic3r::DynamicPrintConfig::new_from_defaults());
+        config->opt_int("skirts") = 1;
+        config->opt_float("brim_width") = 2.;
+        config->opt_int("perimeters") = 3;
+        config->set_deserialize("fill_density", "40%");
+        config->set_deserialize("first_layer_height", "100%");
+
+        WHEN("first layer width set to 2mm") {
+            Slic3r::Model model;
+            config->set_deserialize("first_layer_extrusion_width", "2");
+            auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)};
+
+            std::vector<double> E_per_mm_bottom;
+            std::string gcode = ::Test::gcode(print);
+            auto parser {Slic3r::GCodeReader()};
+            const auto layer_height { config->opt_float("layer_height") };
+            parser.parse_buffer(gcode, [&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
+                    if (line.extruding(self) && line.dist_XY(self) > 0) {
+                        E_per_mm_bottom.emplace_back(line.dist_E(self) / line.dist_XY(self));
+                    }
+                }
+            });
+            THEN(" First layer width applies to everything on first layer.") {
+                bool pass = false;
+                auto avg_E {std::accumulate(E_per_mm_bottom.cbegin(), E_per_mm_bottom.cend(), 0.0) / static_cast<double>(E_per_mm_bottom.size())};
+
+                pass = (std::count_if(E_per_mm_bottom.cbegin(), E_per_mm_bottom.cend(), [avg_E] (const double& v) { return v == Approx(avg_E); }) == 0);
+                REQUIRE(pass == true);
+                REQUIRE(E_per_mm_bottom.size() > 0); // make sure it actually passed because of extrusion
+            }
+            THEN(" First layer width does not apply to upper layer.") {
+            }
+        }
+    }
+}
+// needs gcode export
+SCENARIO(" Bridge flow specifics.", "[!mayfail]") {
+    GIVEN("A default config with no cooling and a fixed bridge speed, flow ratio and an overhang mesh.") {
+        WHEN("bridge_flow_ratio is set to 1.0") {
+            THEN("Output flow is as expected.") {
+            }
+        }
+        WHEN("bridge_flow_ratio is set to 0.5") {
+            THEN("Output flow is as expected.") {
+            }
+        }
+        WHEN("bridge_flow_ratio is set to 2.0") {
+            THEN("Output flow is as expected.") {
+            }
+        }
+    }
+    GIVEN("A default config with no cooling and a fixed bridge speed, flow ratio, fixed extrusion width of 0.4mm and an overhang mesh.") {
+        WHEN("bridge_flow_ratio is set to 1.0") {
+            THEN("Output flow is as expected.") {
+            }
+        }
+        WHEN("bridge_flow_ratio is set to 0.5") {
+            THEN("Output flow is as expected.") {
+            }
+        }
+        WHEN("bridge_flow_ratio is set to 2.0") {
+            THEN("Output flow is as expected.") {
+            }
+        }
+    }
+}
+
+/// Test the expected behavior for auto-width, 
+/// spacing, etc
+SCENARIO("Flow: Flow math for non-bridges", "[!mayfail]") {
+    GIVEN("Nozzle Diameter of 0.4, a desired width of 1mm and layer height of 0.5") {
+        auto width {ConfigOptionFloatOrPercent(1.0, false)};
+        float spacing {0.4};
+        float nozzle_diameter {0.4};
+        float bridge_flow {1.0};
+        float layer_height {0.5};
+
+        // Spacing for non-bridges is has some overlap
+        THEN("External perimeter flow has spacing fixed to 1.1*nozzle_diameter") {
+            auto flow {Flow::new_from_config_width(frExternalPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, 0.0f)};
+            REQUIRE(flow.spacing() == Approx((1.1*nozzle_diameter) - layer_height * (1.0 - PI / 4.0)));
+        }
+
+        THEN("Internal perimeter flow has spacing of 1.05 (minimum)") {
+            auto flow {Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, 0.0f)};
+            REQUIRE(flow.spacing() == Approx((1.05*nozzle_diameter) - layer_height * (1.0 - PI / 4.0)));
+        }
+        THEN("Spacing for supplied width is 0.8927f") {
+            auto flow {Flow::new_from_config_width(frExternalPerimeter, width, nozzle_diameter, layer_height, 0.0f)};
+            REQUIRE(flow.spacing() == Approx(width.value - layer_height * (1.0 - PI / 4.0)));
+            flow = Flow::new_from_config_width(frPerimeter, width, nozzle_diameter, layer_height, 0.0f);
+            REQUIRE(flow.spacing() == Approx(width.value - layer_height * (1.0 - PI / 4.0)));
+        }
+    }
+    /// Check the min/max
+    GIVEN("Nozzle Diameter of 0.25") {
+        float spacing {0.4};
+        float nozzle_diameter {0.25};
+        float bridge_flow {0.0};
+        float layer_height {0.5};
+        WHEN("layer height is set to 0.2") {
+            layer_height = 0.15f;
+            THEN("Max width is set.") {
+                auto flow {Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, 0.0f)};
+                REQUIRE(flow.width == Approx(1.4*nozzle_diameter));
+            }
+        }
+        WHEN("Layer height is set to 0.2") {
+            layer_height = 0.3f;
+            THEN("Min width is set.") {
+                auto flow {Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, 0.0f)};
+                REQUIRE(flow.width == Approx(1.05*nozzle_diameter));
+            }
+        }
+    }
+
+
+#if 0
+    /// Check for an edge case in the maths where the spacing could be 0; original
+    /// math is 0.99. Slic3r issue #4654
+    GIVEN("Input spacing of 0.414159 and a total width of 2") {
+        double in_spacing = 0.414159;
+        double total_width = 2.0;
+        auto flow {Flow::new_from_spacing(1.0, 0.4, 0.3, false)};
+        WHEN("solid_spacing() is called") {
+            double result = flow.solid_spacing(total_width, in_spacing);
+            THEN("Yielded spacing is greater than 0") {
+                REQUIRE(result > 0);
+            }
+        }
+    }
+#endif    
+
+}
+
+/// Spacing, width calculation for bridge extrusions
+SCENARIO("Flow: Flow math for bridges", "[!mayfail]") {
+    GIVEN("Nozzle Diameter of 0.4, a desired width of 1mm and layer height of 0.5") {
+        auto width {ConfigOptionFloatOrPercent(1.0, false)};
+        auto spacing {0.4};
+        auto nozzle_diameter {0.4};
+        auto bridge_flow {1.0};
+        auto layer_height {0.5};
+        WHEN("Flow role is frExternalPerimeter") {
+            auto flow {Flow::new_from_config_width(frExternalPerimeter, width, nozzle_diameter, layer_height, bridge_flow)};
+            THEN("Bridge width is same as nozzle diameter") {
+                REQUIRE(flow.width == Approx(nozzle_diameter));
+            }
+            THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") {
+                REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING));
+            }
+        }
+        WHEN("Flow role is frInfill") {
+            auto flow {Flow::new_from_config_width(frInfill, width, nozzle_diameter, layer_height, bridge_flow)};
+            THEN("Bridge width is same as nozzle diameter") {
+                REQUIRE(flow.width == Approx(nozzle_diameter));
+            }
+            THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") {
+                REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING));
+            }
+        }
+        WHEN("Flow role is frPerimeter") {
+            auto flow {Flow::new_from_config_width(frPerimeter, width, nozzle_diameter, layer_height, bridge_flow)};
+            THEN("Bridge width is same as nozzle diameter") {
+                REQUIRE(flow.width == Approx(nozzle_diameter));
+            }
+            THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") {
+                REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING));
+            }
+        }
+        WHEN("Flow role is frSupportMaterial") {
+            auto flow {Flow::new_from_config_width(frSupportMaterial, width, nozzle_diameter, layer_height, bridge_flow)};
+            THEN("Bridge width is same as nozzle diameter") {
+                REQUIRE(flow.width == Approx(nozzle_diameter));
+            }
+            THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") {
+                REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING));
+            }
+        }
+    }
+}

Some files were not shown because too many files changed in this diff