test_flow.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. //#define CATCH_CONFIG_DISABLE
  2. #include <catch_main.hpp>
  3. #include <numeric>
  4. #include <sstream>
  5. #include "test_data.hpp" // get access to init_print, etc
  6. #include <libslic3r/Config.hpp>
  7. #include <libslic3r/Model.hpp>"
  8. #include <libslic3r/Config.hpp>
  9. #include <libslic3r/GCodeReader.hpp>
  10. #include <libslic3r/Flow.hpp>
  11. #include <libslic3r/libslic3r.h>
  12. #include <libslic3r/Fill/Fill.hpp>
  13. #include <libslic3r/Print.hpp>
  14. #include <libslic3r/ExtrusionEntity.hpp>
  15. #include <libslic3r/Layer.hpp>
  16. #include <libslic3r/Geometry.hpp>
  17. #include <libslic3r/Flow.hpp>
  18. #include <libslic3r/ClipperUtils.hpp>
  19. #include <libslic3r/SVG.hpp>
  20. #include <libslic3r/Format/3mf.hpp>
  21. using namespace Slic3r::Test;
  22. using namespace Slic3r;
  23. SCENARIO("Extrusion width specifics", "[!mayfail]") {
  24. GIVEN("A config with a skirt, brim, some fill density, 3 perimeters, and 1 bottom solid layer and a 20mm cube mesh") {
  25. // this is a sharedptr
  26. DynamicPrintConfig &config {Slic3r::DynamicPrintConfig::full_print_config()};
  27. config.set_key_value("skirts", new ConfigOptionInt{1});
  28. config.set_key_value("brim_width", new ConfigOptionFloat{2});
  29. config.set_key_value("perimeters", new ConfigOptionInt{3});
  30. config.set_key_value("fill_density", new ConfigOptionPercent{40});
  31. config.set_key_value("first_layer_height", new ConfigOptionFloatOrPercent{100, true});
  32. config.set_key_value("extruder", new ConfigOptionInt{0});
  33. WHEN("first layer width set to 2mm") {
  34. Slic3r::Model model;
  35. config.set_key_value("first_layer_extrusion_width", new ConfigOptionFloatOrPercent{2.0, false});
  36. Print print;
  37. Slic3r::Test::init_print(print, { TestMesh::cube_20x20x20 }, model, &config);
  38. //std::cout << "model pos: " << model.objects.front()->instances.front()->get_offset().x() << ": " << model.objects.front()->instances.front()->get_offset().x() << "\n";
  39. //Print print;
  40. //for (auto* mo : model.objects)
  41. // print.auto_assign_extruders(mo);
  42. //print.apply(model, *config);
  43. ////std::cout << "print volume: " << print.<< ": " << model.objects().front()->copies().front().x() << "\n";
  44. //std::string err = print.validate();
  45. std::vector<double> E_per_mm_bottom;
  46. std::string gcode_filepath("");
  47. Slic3r::Test::gcode(gcode_filepath, print);
  48. GCodeReader parser {Slic3r::GCodeReader()};
  49. const double layer_height = config.opt_float("layer_height");
  50. const double first_layer_height = config.get_computed_value("first_layer_height");
  51. std::string gcode_from_file= read_to_string(gcode_filepath);
  52. parser.parse_buffer(gcode_from_file, [&E_per_mm_bottom, layer_height, first_layer_height] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
  53. {
  54. if (self.z() <= first_layer_height + 0.01) { // only consider first layer
  55. if (line.extruding(self) && line.dist_XY(self) > 0) {
  56. E_per_mm_bottom.emplace_back(line.dist_E(self) / line.dist_XY(self));
  57. }
  58. }
  59. });
  60. THEN(" First layer width applies to everything on first layer.") {
  61. bool pass = false;
  62. 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())};
  63. 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);
  64. REQUIRE(pass == true);
  65. REQUIRE(E_per_mm_bottom.size() > 0); // make sure it actually passed because of extrusion
  66. }
  67. THEN(" First layer width does not apply to upper layer.") {
  68. }
  69. clean_file(gcode_filepath, "gcode");
  70. }
  71. }
  72. }
  73. // needs gcode export
  74. SCENARIO(" Bridge flow specifics.", "[!mayfail]") {
  75. GIVEN("A default config with no cooling and a fixed bridge speed, flow ratio and an overhang mesh.") {
  76. WHEN("bridge_flow_ratio is set to 1.0") {
  77. THEN("Output flow is as expected.") {
  78. }
  79. }
  80. WHEN("bridge_flow_ratio is set to 0.5") {
  81. THEN("Output flow is as expected.") {
  82. }
  83. }
  84. WHEN("bridge_flow_ratio is set to 2.0") {
  85. THEN("Output flow is as expected.") {
  86. }
  87. }
  88. }
  89. GIVEN("A default config with no cooling and a fixed bridge speed, flow ratio, fixed extrusion width of 0.4mm and an overhang mesh.") {
  90. WHEN("bridge_flow_ratio is set to 1.0") {
  91. THEN("Output flow is as expected.") {
  92. }
  93. }
  94. WHEN("bridge_flow_ratio is set to 0.5") {
  95. THEN("Output flow is as expected.") {
  96. }
  97. }
  98. WHEN("bridge_flow_ratio is set to 2.0") {
  99. THEN("Output flow is as expected.") {
  100. }
  101. }
  102. }
  103. }
  104. /// Test the expected behavior for auto-width,
  105. /// spacing, etc
  106. SCENARIO("Flow: Flow math for non-bridges", "[!mayfail]") {
  107. auto width_0 = ConfigOptionFloatOrPercent(0.0, false);
  108. auto spacing_0 = ConfigOptionFloatOrPercent(0.0, false,true);
  109. GIVEN("Nozzle Diameter of 0.4, a desired width of 1mm and layer height of 0.5") {
  110. auto width_1 = ConfigOptionFloatOrPercent(1.0, false);
  111. auto spacing_1 = ConfigOptionFloatOrPercent(1.0, false,true);
  112. float spacing {0.4f};
  113. float nozzle_diameter {0.4f};
  114. float bridge_flow {1.0f};
  115. float layer_height {0.25f};
  116. float spacing_ratio = 1.0f;
  117. assert(layer_height < nozzle_diameter);
  118. // Spacing for non-bridges is has some overlap
  119. THEN("External perimeter flow has a default spacing fixed to 1.05*nozzle_diameter") {
  120. Flow flow = Flow::new_from_config_width(frExternalPerimeter, width_0, spacing_0, nozzle_diameter, layer_height, spacing_ratio);
  121. REQUIRE(flow.spacing() == Approx((1.05f*nozzle_diameter) - layer_height * (1.0 - PI / 4.0)));
  122. }
  123. THEN("Internal perimeter flow has a default spacing fixed to 1.125*nozzle_diameter") {
  124. Flow flow {Flow::new_from_config_width(frPerimeter, width_0, spacing_0, nozzle_diameter, layer_height, spacing_ratio)};
  125. REQUIRE(flow.spacing() == Approx((1.125*nozzle_diameter) - layer_height * (1.0 - PI / 4.0)));
  126. }
  127. THEN("Spacing for supplied width is 0.8927f") {
  128. Flow flow {Flow::new_from_config_width(frExternalPerimeter, width_1, spacing_1, nozzle_diameter, layer_height, spacing_ratio)};
  129. REQUIRE(flow.spacing() == Approx(width_1.get_abs_value(1.f) - layer_height * (1.0 - PI / 4.0)));
  130. flow = Flow::new_from_config_width(frPerimeter, width_1, spacing_1, nozzle_diameter, layer_height, spacing_ratio);
  131. REQUIRE(flow.spacing() == Approx(width_1.get_abs_value(1.f) - layer_height * (1.0 - PI / 4.0)));
  132. }
  133. }
  134. /// Check the min/max
  135. GIVEN("Nozzle Diameter of 0.25 with extreme width") {
  136. float nozzle_diameter {0.25f};
  137. float layer_height {0.5f};
  138. float spacing_ratio {1.0f};
  139. WHEN("layer height is set to 0.15") {
  140. layer_height = 0.15f;
  141. THEN("Max width is respected.") {
  142. auto flow {Flow::new_from_config_width(frPerimeter, width_0, spacing_0, nozzle_diameter, layer_height, spacing_ratio)};
  143. REQUIRE(flow.width() <= Approx(1.4*nozzle_diameter));
  144. }
  145. THEN("Min width is respected") {
  146. auto flow{ Flow::new_from_config_width(frPerimeter, width_0, spacing_0, nozzle_diameter, layer_height, spacing_ratio) };
  147. REQUIRE(flow.width() >= Approx(1.05*nozzle_diameter));
  148. }
  149. }
  150. WHEN("Layer height is set to 0.3") {
  151. layer_height = 0.01f;
  152. THEN("Max width is respected.") {
  153. auto flow{ Flow::new_from_config_width(frPerimeter, width_0, spacing_0, nozzle_diameter, layer_height, spacing_ratio) };
  154. REQUIRE(flow.width() <= Approx(1.4*nozzle_diameter));
  155. }
  156. THEN("Min width is respected.") {
  157. auto flow{ Flow::new_from_config_width(frPerimeter, width_0, spacing_0, nozzle_diameter, layer_height, spacing_ratio) };
  158. REQUIRE(flow.width() >= Approx(1.05*nozzle_diameter));
  159. }
  160. }
  161. }
  162. ///// Check for an edge case in the maths where the spacing could be 0; original
  163. ///// math is 0.99. Slic3r issue #4654
  164. //GIVEN("Input spacing of 0.414159 and a total width of 2") {
  165. // double in_spacing = 0.414159;
  166. // double total_width = 2.0;
  167. // auto flow {Flow::new_from_spacing(1.0, 0.4, 0.3, false)};
  168. // WHEN("solid_spacing() is called") {
  169. // double result = flow.solid_spacing(total_width, in_spacing);
  170. // THEN("Yielded spacing is greater than 0") {
  171. // REQUIRE(result > 0);
  172. // }
  173. // }
  174. //}
  175. }
  176. /// Spacing, width calculation for bridge extrusions
  177. SCENARIO("Flow: Flow math for bridges", "[!mayfail]") {
  178. GIVEN("Nozzle Diameter of 0.4, a desired width of 1mm and layer height of 0.5") {
  179. float BRIDGE_EXTRA_SPACING_MULT = 0.f; // not used anymore
  180. auto width {ConfigOptionFloatOrPercent{1.0, false}};
  181. auto spacing = ConfigOptionFloatOrPercent(1.0, false, false);
  182. float nozzle_diameter {0.4f};
  183. float spacing_ratio {1.0f};
  184. float layer_height {0.5f};
  185. WHEN("via bridging_flow()") {
  186. auto flow {Flow::bridging_flow(nozzle_diameter, nozzle_diameter)};
  187. THEN("Bridge width is same as nozzle diameter") {
  188. REQUIRE(flow.width() == Approx(nozzle_diameter));
  189. }
  190. THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING_MULT * nozzle_diameter") {
  191. REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING_MULT * nozzle_diameter));
  192. }
  193. }
  194. REQUIRE(Flow::bridge_extrusion_spacing(nozzle_diameter) == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING_MULT * nozzle_diameter));
  195. }
  196. }
  197. SCENARIO("Flow: stats are okay") {
  198. DynamicPrintConfig &config{Slic3r::DynamicPrintConfig::full_print_config()};
  199. config.set_key_value("skirts", new ConfigOptionInt{0});
  200. config.set_key_value("brim_width", new ConfigOptionFloat{0});
  201. config.set_key_value("fill_density", new ConfigOptionPercent{0});
  202. config.set_key_value("first_layer_height", new ConfigOptionFloatOrPercent{0.2, false});
  203. config.set_key_value("layer_height", new ConfigOptionFloat{0.2});
  204. config.set_key_value("top_solid_layers", new ConfigOptionInt{1000});
  205. config.set_key_value("bottom_solid_layers", new ConfigOptionInt{1000});
  206. config.set_key_value("extruder", new ConfigOptionInt{0});
  207. config.set_key_value("nozzle_diameter", new ConfigOptionFloats(1, 0.5f));
  208. config.set_key_value("solid_fill_pattern", new ConfigOptionEnum<InfillPattern>(ipRectilinear));
  209. config.set_key_value("first_layer_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
  210. config.set_key_value("solid_infill_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
  211. config.set_key_value("top_infill_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
  212. config.set_key_value("external_perimeter_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
  213. config.set_key_value("perimeter_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
  214. config.update_phony({});
  215. double cube_side_spacing = 20;
  216. std::string name;
  217. WHEN("20x20x20 cube with only infill")
  218. {
  219. // only infill -> the sube border is at the spacing (so extrusion may go a bit outside)
  220. config.set_key_value("perimeters", new ConfigOptionInt{0});
  221. name = "no_peri";
  222. }
  223. WHEN("20x20x20 cube with one perimeter")
  224. {
  225. // only infill -> the perimeter width touch the border, so it has less fill than with only infill.
  226. // also, set seam_gap to 0 to avoid removing bits of the perimeters
  227. cube_side_spacing = 20 - 0.2/*height*/ * float(1. - 0.25 * PI);
  228. config.set_key_value("perimeters", new ConfigOptionInt{1});
  229. config.set_key_value("seam_gap", (new ConfigOptionFloatsOrPercents(1, FloatOrPercent{0,false}))->set_is_extruder_size());
  230. name = "one_peri";
  231. }
  232. WHEN("20x20x20 cube with three perimeters")
  233. {
  234. // only infill -> the perimeter width touch the border, so it has less fill than with only infill.
  235. // also, set seam_gap to 0 to avoid removing bits of the perimeters
  236. cube_side_spacing = 20 - 0.2/*height*/ * float(1. - 0.25 * PI);
  237. config.set_key_value("perimeters", new ConfigOptionInt{3});
  238. config.set_key_value("seam_gap", (new ConfigOptionFloatsOrPercents(1, FloatOrPercent{0,false}))->set_is_extruder_size());
  239. name = "three_peri";
  240. }
  241. WHEN("20x20x20 cube with three arachne perimeters")
  242. {
  243. // only infill -> the perimeter width touch the border, so it has less fill than with only infill.
  244. // also, set seam_gap to 0 to avoid removing bits of the perimeters
  245. cube_side_spacing = 20 - 0.2/*height*/ * float(1. - 0.25 * PI);
  246. config.set_key_value("perimeters", new ConfigOptionInt{3});
  247. config.set_key_value("perimeter_generator", new ConfigOptionEnum<PerimeterGeneratorType>(PerimeterGeneratorType::Arachne));
  248. config.set_key_value("seam_gap", (new ConfigOptionFloatsOrPercents(1, FloatOrPercent{0, false}))->set_is_extruder_size());
  249. name = "arachne_three_peri";
  250. }
  251. Slic3r::Model model;
  252. Print print;
  253. Slic3r::Test::init_print(print, {TestMesh::cube_20x20x20}, model, &config);
  254. const double volume = (cube_side_spacing*cube_side_spacing*20);
  255. const double volume_layer = (cube_side_spacing*cube_side_spacing*0.2);
  256. print.process();
  257. REQUIRE(print.get_object(0)->get_layer(0)->height == Approx(0.2));
  258. REQUIRE(print.get_object(0)->get_layer(1)->height == Approx(0.2));
  259. std::string gcode_filepath{ "" };
  260. Slic3r::Test::gcode(gcode_filepath, print);
  261. std::string gcode_from_file = read_to_string(gcode_filepath);
  262. Slic3r::PrintStatistics &stats = print.print_statistics();
  263. //string[] lineArray = gcode_from_file
  264. GCodeReader parser;
  265. double volume_extruded = 0;
  266. //int idx = 0;
  267. int step = 0;
  268. double volume_perimeter_extruded = 0;
  269. double volume_infill_extruded = 0;
  270. double volume_other_extruded = 0;
  271. // add remaining time lines where needed
  272. parser.parse_buffer(gcode_from_file,
  273. [&](GCodeReader& reader, const GCodeReader::GCodeLine& line)
  274. {
  275. if(line.comment() == "TYPE:External perimeter" || line.comment() == "TYPE:Internal perimeter")
  276. step = 1;
  277. else if(line.comment() == "TYPE:Solid infill" || line.comment() == "TYPE:Top solid infill")
  278. step = 2;
  279. else if(boost::starts_with(line.comment(),"TYPE:"))
  280. step = 0;
  281. if (line.cmd_is("G1"))
  282. {
  283. if (line.dist_E(reader) > 0 && line.dist_XY(reader) > 0) {
  284. volume_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.);
  285. if (step == 0) volume_other_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.);
  286. else if (step == 1) volume_perimeter_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.);
  287. else if (step == 2) volume_infill_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.);
  288. }
  289. }
  290. });
  291. REQUIRE(volume_other_extruded == 0);
  292. REQUIRE(print.get_object(0)->layers().size() == 100);
  293. for (size_t layer_id = 0; layer_id < 100; layer_id++) {
  294. double volumeExtrPerimeter = ExtrusionVolume{}.get(print.get_object(0)->get_layer(layer_id)->regions()[0]->perimeters);
  295. double volumeExtrInfill = ExtrusionVolume{}.get(print.get_object(0)->get_layer(layer_id)->regions()[0]->fills);
  296. REQUIRE(volume_layer == Approx(volumeExtrInfill + volumeExtrPerimeter));
  297. // no perimeter -> no fill_no_overlap_expolygons
  298. REQUIRE( (config.option("perimeters")->get_int() == 0) == (volumeExtrPerimeter == 0));
  299. REQUIRE( (volumeExtrPerimeter == 0) == print.get_object(0)->get_layer(0)->regions()[0]->fill_no_overlap_expolygons.empty());
  300. }
  301. std::cout<<name<<" : "<<volume<<" mm3\n";
  302. REQUIRE(volume == Approx(volume_infill_extruded+volume_perimeter_extruded));
  303. REQUIRE(volume == Approx(volume_extruded));
  304. REQUIRE(volume == Approx(stats.total_extruded_volume));
  305. clean_file(gcode_filepath, "gcode");
  306. Slic3r::store_3mf((name + std::string(".3mf")).c_str(), &model, &print.full_print_config(), OptionStore3mf{});
  307. }
  308. //
  309. //SCENARIO("Flow: stats from 3mf are okay")
  310. //{
  311. // std::string path = std::string(TEST_DATA_DIR) + "/test_3mf/20x20x1.3mf";
  312. // std::cout<<"path: "<<path<<"\n";
  313. // DynamicPrintConfig config;
  314. // ConfigSubstitutionContext ctxt{ForwardCompatibilitySubstitutionRule::Disable};
  315. // Slic3r::Model model;
  316. // bool ret = load_3mf(path.c_str(), config, ctxt, &model, false);
  317. // Print print;
  318. //
  319. // print.set_status_callback([](const PrintBase::SlicingStatus &) {});
  320. // print.apply(model, config);
  321. // const double layer_height = config.option("layer_height")->get_float();
  322. // std::cout<<"cube side: "<< (20 + layer_height * float(1. - 0.25 * PI))<<"\n";
  323. // const double cube_side_spacing = 20;// - layer_height * float(1. - 0.25 * PI);
  324. // const double volume = (cube_side_spacing*cube_side_spacing*1);
  325. // const double volume_layer = (cube_side_spacing*cube_side_spacing*layer_height);
  326. // print.process();
  327. // REQUIRE(print.get_object(0)->get_layer(0)->height == Approx(layer_height));
  328. // REQUIRE(print.get_object(0)->get_layer(1)->height == Approx(layer_height));
  329. //
  330. // std::string gcode_filepath{ "" };
  331. // Slic3r::Test::gcode(gcode_filepath, print);
  332. // std::string gcode_from_file = read_to_string(gcode_filepath);
  333. // Slic3r::PrintStatistics &stats = print.print_statistics();
  334. // //string[] lineArray = gcode_from_file
  335. // GCodeReader parser;
  336. // double volume_extruded = 0;
  337. // //int idx = 0;
  338. // int step = 0;
  339. // double volume_perimeter_extruded = 0;
  340. // double volume_infill_extruded = 0;
  341. // double volume_other_extruded = 0;
  342. // double volume_this_layer = 0;
  343. // // add remaining time lines where needed
  344. // parser.parse_buffer(gcode_from_file,
  345. // [&](GCodeReader& reader, const GCodeReader::GCodeLine& line)
  346. // {
  347. // if(line.comment() == "TYPE:External perimeter" || line.comment() == "TYPE:Internal perimeter")
  348. // step = 1;
  349. // else if(line.comment() == "TYPE:Solid infill" || line.comment() == "TYPE:Top solid infill")
  350. // step = 2;
  351. // else if(boost::starts_with(line.comment(),"TYPE:"))
  352. // step = 0;
  353. // if (line.cmd_is("G1"))
  354. // {
  355. // if (line.dist_E(reader) > 0 && line.dist_XY(reader) > 0) {
  356. // volume_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.);
  357. // volume_this_layer += line.dist_E(reader)*(PI*1.75*1.75 / 4.);
  358. // if (step == 0) volume_other_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.);
  359. // else if (step == 1) volume_perimeter_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.);
  360. // else if (step == 2) volume_infill_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.);
  361. // }
  362. // }
  363. // if (boost::starts_with(line.comment(), "LAYER_CHANGE")) {
  364. // std::cout<<"layer volume: "<<volume_this_layer<<"\n";
  365. // volume_this_layer = 0;
  366. // } else if (volume_this_layer == 0 && boost::starts_with(line.comment(), "Z")) {
  367. // std::cout<<"layer: "<<line.comment()<<", ";
  368. // }
  369. // });
  370. // std::cout<<"\n";
  371. // REQUIRE(volume_other_extruded == 0);
  372. //
  373. // double tot_vol_extrusion_struct = 0;
  374. // for (size_t layer_id = 0; layer_id < print.get_object(0)->layers().size(); layer_id++) {
  375. // double volumeExtrPerimeter = ExtrusionVolume{}.get(print.get_object(0)->get_layer(layer_id)->regions()[0]->perimeters);
  376. // double volumeExtrInfill = ExtrusionVolume{}.get(print.get_object(0)->get_layer(layer_id)->regions()[0]->fills);
  377. // REQUIRE(volume_layer == Approx(volumeExtrInfill + volumeExtrPerimeter));
  378. // //std::cout<<"
  379. // // no perimeter -> no fill_no_overlap_expolygons
  380. // REQUIRE( (config.option("perimeters")->get_int() == 0) == (volumeExtrPerimeter == 0));
  381. // REQUIRE( (volumeExtrPerimeter == 0) == print.get_object(0)->get_layer(0)->regions()[0]->fill_no_overlap_expolygons.empty());
  382. //
  383. // tot_vol_extrusion_struct += volumeExtrPerimeter + volumeExtrInfill;
  384. // }
  385. //
  386. // std::cout<<"from3mf"<<" : "<<volume<<" mm3\n";
  387. // REQUIRE(volume == Approx(tot_vol_extrusion_struct));
  388. // REQUIRE(volume == Approx(volume_infill_extruded+volume_perimeter_extruded));
  389. // REQUIRE(volume == Approx(volume_extruded));
  390. // REQUIRE(volume == Approx(stats.total_extruded_volume));
  391. // clean_file(gcode_filepath, "gcode");
  392. // Slic3r::store_3mf(( std::string("from3mf.3mf")).c_str(), &model, &print.full_print_config(), OptionStore3mf{});
  393. //}