1 #include <catch2/catch.hpp> 2 3 #include <numeric> 4 #include <sstream> 5 6 #include "test_data.hpp" // get access to init_print, etc 7 8 #include "libslic3r/Config.hpp" 9 #include "libslic3r/Model.hpp" 10 #include "libslic3r/Config.hpp" 11 #include "libslic3r/GCodeReader.hpp" 12 #include "libslic3r/Flow.hpp" 13 #include "libslic3r/libslic3r.h" 14 15 using namespace Slic3r::Test; 16 using namespace Slic3r; 17 18 SCENARIO("Extrusion width specifics", "[Flow]") { 19 GIVEN("A config with a skirt, brim, some fill density, 3 perimeters, and 1 bottom solid layer and a 20mm cube mesh") { 20 // this is a sharedptr 21 DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config(); 22 config.set_deserialize_strict({ 23 { "brim_width", 2 }, 24 { "skirts", 1 }, 25 { "perimeters", 3 }, 26 { "fill_density", "40%" }, 27 { "first_layer_height", "100%" } 28 }); 29 30 WHEN("first layer width set to 2mm") { 31 Slic3r::Model model; 32 config.set("first_layer_extrusion_width", 2); 33 Slic3r::Print print; 34 Slic3r::Test::init_print({TestMesh::cube_20x20x20}, print, model, config); 35 36 std::vector<double> E_per_mm_bottom; 37 std::string gcode = Test::gcode(print); 38 Slic3r::GCodeReader parser; 39 const double layer_height = config.opt_float("layer_height"); 40 parser.parse_buffer(gcode, [&E_per_mm_bottom, layer_height] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line) __anon8fbc62960102(Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line) 41 { 42 if (self.z() == Approx(layer_height).margin(0.01)) { // only consider first layer 43 if (line.extruding(self) && line.dist_XY(self) > 0) { 44 E_per_mm_bottom.emplace_back(line.dist_E(self) / line.dist_XY(self)); 45 } 46 } 47 }); 48 THEN(" First layer width applies to everything on first layer.") { 49 bool pass = false; 50 double avg_E = std::accumulate(E_per_mm_bottom.cbegin(), E_per_mm_bottom.cend(), 0.0) / static_cast<double>(E_per_mm_bottom.size()); 51 __anon8fbc62960202(const double& v) 52 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); 53 REQUIRE(pass == true); 54 REQUIRE(E_per_mm_bottom.size() > 0); // make sure it actually passed because of extrusion 55 } 56 THEN(" First layer width does not apply to upper layer.") { 57 } 58 } 59 } 60 } 61 // needs gcode export 62 SCENARIO(" Bridge flow specifics.", "[Flow]") { 63 GIVEN("A default config with no cooling and a fixed bridge speed, flow ratio and an overhang mesh.") { 64 WHEN("bridge_flow_ratio is set to 1.0") { 65 THEN("Output flow is as expected.") { 66 } 67 } 68 WHEN("bridge_flow_ratio is set to 0.5") { 69 THEN("Output flow is as expected.") { 70 } 71 } 72 WHEN("bridge_flow_ratio is set to 2.0") { 73 THEN("Output flow is as expected.") { 74 } 75 } 76 } 77 GIVEN("A default config with no cooling and a fixed bridge speed, flow ratio, fixed extrusion width of 0.4mm and an overhang mesh.") { 78 WHEN("bridge_flow_ratio is set to 1.0") { 79 THEN("Output flow is as expected.") { 80 } 81 } 82 WHEN("bridge_flow_ratio is set to 0.5") { 83 THEN("Output flow is as expected.") { 84 } 85 } 86 WHEN("bridge_flow_ratio is set to 2.0") { 87 THEN("Output flow is as expected.") { 88 } 89 } 90 } 91 } 92 93 /// Test the expected behavior for auto-width, 94 /// spacing, etc 95 SCENARIO("Flow: Flow math for non-bridges", "[Flow]") { 96 GIVEN("Nozzle Diameter of 0.4, a desired width of 1mm and layer height of 0.5") { 97 ConfigOptionFloatOrPercent width(1.0, false); 98 float nozzle_diameter = 0.4f; 99 float bridge_flow = 0.f; 100 float layer_height = 0.5f; 101 102 // Spacing for non-bridges is has some overlap 103 THEN("External perimeter flow has spacing fixed to 1.125 * nozzle_diameter") { 104 auto flow = Flow::new_from_config_width(frExternalPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, bridge_flow); 105 REQUIRE(flow.spacing() == Approx(1.125 * nozzle_diameter - layer_height * (1.0 - PI / 4.0))); 106 } 107 108 THEN("Internal perimeter flow has spacing fixed to 1.125 * nozzle_diameter") { 109 auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, bridge_flow); 110 REQUIRE(flow.spacing() == Approx(1.125 *nozzle_diameter - layer_height * (1.0 - PI / 4.0))); 111 } 112 THEN("Spacing for supplied width is 0.8927f") { 113 auto flow = Flow::new_from_config_width(frExternalPerimeter, width, nozzle_diameter, layer_height, bridge_flow); 114 REQUIRE(flow.spacing() == Approx(width.value - layer_height * (1.0 - PI / 4.0))); 115 flow = Flow::new_from_config_width(frPerimeter, width, nozzle_diameter, layer_height, bridge_flow); 116 REQUIRE(flow.spacing() == Approx(width.value - layer_height * (1.0 - PI / 4.0))); 117 } 118 } 119 /// Check the min/max 120 GIVEN("Nozzle Diameter of 0.25") { 121 float nozzle_diameter = 0.25f; 122 float bridge_flow = 0.f; 123 float layer_height = 0.5f; 124 WHEN("layer height is set to 0.2") { 125 layer_height = 0.15f; 126 THEN("Max width is set.") { 127 auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, bridge_flow); 128 REQUIRE(flow.width == Approx(1.125 * nozzle_diameter)); 129 } 130 } 131 WHEN("Layer height is set to 0.2") { 132 layer_height = 0.3f; 133 THEN("Min width is set.") { 134 auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, bridge_flow); 135 REQUIRE(flow.width == Approx(1.125 * nozzle_diameter)); 136 } 137 } 138 } 139 140 #if 0 141 /// Check for an edge case in the maths where the spacing could be 0; original 142 /// math is 0.99. Slic3r issue #4654 143 GIVEN("Input spacing of 0.414159 and a total width of 2") { 144 double in_spacing = 0.414159; 145 double total_width = 2.0; 146 auto flow = Flow::new_from_spacing(1.0, 0.4, 0.3, false); 147 WHEN("solid_spacing() is called") { 148 double result = flow.solid_spacing(total_width, in_spacing); 149 THEN("Yielded spacing is greater than 0") { 150 REQUIRE(result > 0); 151 } 152 } 153 } 154 #endif 155 156 } 157 158 /// Spacing, width calculation for bridge extrusions 159 SCENARIO("Flow: Flow math for bridges", "[Flow]") { 160 GIVEN("Nozzle Diameter of 0.4, a desired width of 1mm and layer height of 0.5") { 161 auto width = ConfigOptionFloatOrPercent(1.0, false); 162 float nozzle_diameter = 0.4f; 163 float bridge_flow = 1.0f; 164 float layer_height = 0.5f; 165 WHEN("Flow role is frExternalPerimeter") { 166 auto flow = Flow::new_from_config_width(frExternalPerimeter, width, nozzle_diameter, layer_height, bridge_flow); 167 THEN("Bridge width is same as nozzle diameter") { 168 REQUIRE(flow.width == Approx(nozzle_diameter)); 169 } 170 THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") { 171 REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING)); 172 } 173 } 174 WHEN("Flow role is frInfill") { 175 auto flow = Flow::new_from_config_width(frInfill, width, nozzle_diameter, layer_height, bridge_flow); 176 THEN("Bridge width is same as nozzle diameter") { 177 REQUIRE(flow.width == Approx(nozzle_diameter)); 178 } 179 THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") { 180 REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING)); 181 } 182 } 183 WHEN("Flow role is frPerimeter") { 184 auto flow = Flow::new_from_config_width(frPerimeter, width, nozzle_diameter, layer_height, bridge_flow); 185 THEN("Bridge width is same as nozzle diameter") { 186 REQUIRE(flow.width == Approx(nozzle_diameter)); 187 } 188 THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") { 189 REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING)); 190 } 191 } 192 WHEN("Flow role is frSupportMaterial") { 193 auto flow = Flow::new_from_config_width(frSupportMaterial, width, nozzle_diameter, layer_height, bridge_flow); 194 THEN("Bridge width is same as nozzle diameter") { 195 REQUIRE(flow.width == Approx(nozzle_diameter)); 196 } 197 THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") { 198 REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING)); 199 } 200 } 201 } 202 } 203