test_skirt_brim.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. //#define CATCH_CONFIG_DISABLE
  2. #include <catch2/catch.hpp>
  3. #include "test_data.hpp"
  4. #include <libslic3r/GCodeReader.hpp>
  5. using namespace Slic3r::Test;
  6. using namespace Slic3r;
  7. SCENARIO("skirt test by merill", "") {
  8. GIVEN("2 objects, don't complete individual object") {
  9. DynamicPrintConfig &config = Slic3r::DynamicPrintConfig::full_print_config();
  10. // remove noise
  11. config.set_deserialize("top_solid_layers", "0");
  12. config.set_deserialize("bottom_solid_layers", "0");
  13. config.set_deserialize("fill_density", "0");
  14. config.set_deserialize("perimeters", "1");
  15. config.set_deserialize("complete_objects", "0");
  16. config.set_key_value("gcode_comments", new ConfigOptionBool(true));
  17. WHEN("skirt with 3 layers is requested") {
  18. config.set_deserialize("skirts", "1");
  19. config.set_deserialize("skirt_height", "3");
  20. config.set_deserialize("brim_width", "0");
  21. Model model{};
  22. Print print{};
  23. Slic3r::Test::init_print(print, { TestMesh::cube_20x20x20, TestMesh::cube_20x20x20 }, model, &config);
  24. std::map<double, bool> layers_with_skirt;
  25. std::map<double, bool> layers_with_brim;
  26. std::string gcode_filepath{ "" };
  27. Slic3r::Test::gcode(gcode_filepath, print);
  28. auto parser{ Slic3r::GCodeReader() };
  29. parser.parse_file(gcode_filepath, [&layers_with_skirt, &layers_with_brim, &config](Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
  30. {
  31. if (line.extruding(self) && line.comment().find("skirt") != std::string::npos) {
  32. layers_with_skirt[self.z()] = 1;
  33. }
  34. if (line.extruding(self) && line.comment().find("brim") != std::string::npos) {
  35. layers_with_brim[self.z()] = 1;
  36. }
  37. });
  38. clean_file(gcode_filepath, "gcode");
  39. THEN("one skrit generated") {
  40. REQUIRE(print.skirt().entities.size() == 1);
  41. for (auto obj : print.objects()) {
  42. REQUIRE(obj->skirt().entities.size() == 0);
  43. }
  44. }
  45. THEN("brim is not generated") {
  46. REQUIRE(print.brim().entities.size() == 0);
  47. for (auto obj : print.objects()) {
  48. REQUIRE(obj->brim().entities.size() == 0);
  49. }
  50. REQUIRE(layers_with_brim.size() == 0);
  51. }
  52. THEN("skirt_height is honored") {
  53. REQUIRE(layers_with_skirt.size() == (size_t)config.opt_int("skirt_height"));
  54. }
  55. }
  56. WHEN("brim and skirt with 3 layers is requested") {
  57. config.set_deserialize("skirts", "1");
  58. config.set_deserialize("skirt_height", "3");
  59. config.set_deserialize("brim_width", "4");
  60. Model model{};
  61. Print print{};
  62. Slic3r::Test::init_print(print, { TestMesh::cube_20x20x20, TestMesh::cube_20x20x20 }, model, &config);
  63. std::map<double, bool> layers_with_skirt;
  64. std::map<double, bool> layers_with_brim;
  65. std::string gcode_filepath{ "" };
  66. Slic3r::Test::gcode(gcode_filepath, print);
  67. auto parser{ Slic3r::GCodeReader() };
  68. parser.parse_file(gcode_filepath, [&layers_with_skirt, &layers_with_brim, &config](Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
  69. {
  70. if (line.extruding(self) && line.comment().find("skirt") != std::string::npos) {
  71. layers_with_skirt[self.z()] = 1;
  72. }
  73. if (line.extruding(self) && line.comment().find("brim") != std::string::npos) {
  74. layers_with_brim[self.z()] = 1;
  75. }
  76. });
  77. clean_file(gcode_filepath, "gcode");
  78. THEN("one skirt generated") {
  79. REQUIRE(print.skirt().entities.size() == 1);
  80. for (auto obj : print.objects()) {
  81. REQUIRE(obj->skirt().entities.size() == 0);
  82. }
  83. }
  84. THEN("brim generated") {
  85. REQUIRE(print.brim().entities.size() > 0);
  86. for (auto obj : print.objects()) {
  87. REQUIRE(obj->brim().entities.size() == 0);
  88. }
  89. REQUIRE(layers_with_brim.size() == 1);
  90. }
  91. THEN("skirt_height is honored") {
  92. REQUIRE(layers_with_skirt.size() == (size_t)config.opt_int("skirt_height"));
  93. }
  94. }
  95. WHEN("brim is requested") {
  96. config.set_deserialize("skirts", "0");
  97. config.set_deserialize("skirt_height", "3");
  98. config.set_deserialize("brim_width", "4");
  99. Model model{};
  100. Print print{};
  101. Slic3r::Test::init_print(print, { TestMesh::cube_20x20x20, TestMesh::cube_20x20x20 }, model, &config);
  102. std::map<double, bool> layers_with_skirt;
  103. std::map<double, bool> layers_with_brim;
  104. std::string gcode_filepath{ "" };
  105. Slic3r::Test::gcode(gcode_filepath, print);
  106. auto parser{ Slic3r::GCodeReader() };
  107. parser.parse_file(gcode_filepath, [&layers_with_skirt, &layers_with_brim, &config](Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
  108. {
  109. if (line.extruding(self) && line.comment().find("skirt") != std::string::npos) {
  110. layers_with_skirt[self.z()] = 1;
  111. }
  112. if (line.extruding(self) && line.comment().find("brim") != std::string::npos) {
  113. layers_with_brim[self.z()] = 1;
  114. }
  115. });
  116. clean_file(gcode_filepath, "gcode");
  117. THEN("no skirt generated") {
  118. REQUIRE(print.skirt().entities.size() == 0);
  119. for (auto obj : print.objects()) {
  120. REQUIRE(obj->skirt().entities.size() == 0);
  121. }
  122. }
  123. THEN("brim generated") {
  124. REQUIRE(print.brim().entities.size() > 0);
  125. for (auto obj : print.objects()) {
  126. REQUIRE(obj->brim().entities.size() == 0);
  127. }
  128. REQUIRE(layers_with_brim.size() == 1);
  129. }
  130. THEN("skirt_height is honored") {
  131. REQUIRE(layers_with_skirt.size() == 0);
  132. }
  133. }
  134. }
  135. GIVEN("3 objects, complete individual object") {
  136. DynamicPrintConfig &config = Slic3r::DynamicPrintConfig::full_print_config();
  137. // remove noise
  138. config.set_deserialize("top_solid_layers", "0");
  139. config.set_deserialize("bottom_solid_layers", "0");
  140. config.set_deserialize("fill_density", "0");
  141. config.set_deserialize("perimeters", "1");
  142. config.set_deserialize("complete_objects", "1");
  143. config.set_key_value("gcode_comments", new ConfigOptionBool(true));
  144. WHEN("skirt with 3 layers is requested") {
  145. config.set_deserialize("skirts", "1");
  146. config.set_deserialize("skirt_height", "3");
  147. config.set_deserialize("brim_width", "0");
  148. Model model{};
  149. Print print{};
  150. Slic3r::Test::init_print(print, { TestMesh::cube_20x20x20, TestMesh::cube_20x20x20, TestMesh::pyramid }, model, &config);
  151. std::map<double, bool> layers_with_skirt;
  152. std::map<double, bool> layers_with_brim;
  153. std::string gcode_filepath{ "" };
  154. Slic3r::Test::gcode(gcode_filepath, print);
  155. auto parser{ Slic3r::GCodeReader() };
  156. parser.parse_file(gcode_filepath, [&layers_with_skirt, &layers_with_brim, &config](Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
  157. {
  158. if (line.extruding(self) && line.comment().find("skirt") != std::string::npos) {
  159. layers_with_skirt[self.z()] = 1;
  160. }
  161. if (line.extruding(self) && line.comment().find("brim") != std::string::npos) {
  162. layers_with_brim[self.z()] = 1;
  163. }
  164. });
  165. THEN("one skirt per object") {
  166. REQUIRE(print.skirt().entities.size() == 0);
  167. for (auto obj : print.objects()) {
  168. REQUIRE(obj->skirt().entities.size() == 1);
  169. }
  170. }
  171. THEN("brim is not generated") {
  172. REQUIRE(print.brim().entities.size() == 0);
  173. for (auto obj : print.objects()) {
  174. REQUIRE(obj->brim().entities.size() == 0);
  175. }
  176. REQUIRE(layers_with_brim.size() == 0);
  177. }
  178. THEN("skirt_height is honored") {
  179. REQUIRE(layers_with_skirt.size() == (size_t)config.opt_int("skirt_height"));
  180. }
  181. }
  182. WHEN("brim and skirt with 3 layers is requested") {
  183. config.set_deserialize("skirts", "1");
  184. config.set_deserialize("skirt_height", "3");
  185. config.set_deserialize("brim_width", "4");
  186. Model model{};
  187. Print print{};
  188. Slic3r::Test::init_print(print, { TestMesh::cube_20x20x20, TestMesh::cube_20x20x20 }, model, &config);
  189. std::map<double, bool> layers_with_skirt;
  190. std::map<double, bool> layers_with_brim;
  191. std::string gcode_filepath{ "" };
  192. Slic3r::Test::gcode(gcode_filepath, print);
  193. auto parser{ Slic3r::GCodeReader() };
  194. parser.parse_file(gcode_filepath, [&layers_with_skirt, &layers_with_brim, &config](Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
  195. {
  196. if (line.extruding(self) && line.comment().find("skirt") != std::string::npos) {
  197. layers_with_skirt[self.z()] = 1;
  198. }
  199. if (line.extruding(self) && line.comment().find("brim") != std::string::npos) {
  200. layers_with_brim[self.z()] = 1;
  201. }
  202. });
  203. clean_file(gcode_filepath, "gcode");
  204. THEN("one skirt per object") {
  205. REQUIRE(print.skirt().entities.size() == 0);
  206. for (auto obj : print.objects()) {
  207. REQUIRE(obj->skirt().entities.size() == 1);
  208. }
  209. }
  210. THEN("brim generated") {
  211. REQUIRE(print.brim().entities.size() == 0);
  212. for (auto obj : print.objects()) {
  213. REQUIRE(obj->brim().entities.size() > 0);
  214. }
  215. REQUIRE(layers_with_brim.size() == 1);
  216. }
  217. THEN("skirt_height is honored") {
  218. REQUIRE(layers_with_skirt.size() == (size_t)config.opt_int("skirt_height"));
  219. }
  220. }
  221. WHEN("brim is requested") {
  222. config.set_deserialize("skirts", "0");
  223. config.set_deserialize("skirt_height", "3");
  224. config.set_deserialize("brim_width", "4");
  225. Model model{};
  226. Print print{};
  227. Slic3r::Test::init_print(print, { TestMesh::cube_20x20x20, TestMesh::cube_20x20x20 }, model, &config);
  228. std::map<double, bool> layers_with_skirt;
  229. std::map<double, bool> layers_with_brim;
  230. std::string gcode_filepath{ "" };
  231. Slic3r::Test::gcode(gcode_filepath, print);
  232. auto parser{ Slic3r::GCodeReader() };
  233. parser.parse_file(gcode_filepath, [&layers_with_skirt, &layers_with_brim, &config](Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
  234. {
  235. if (line.extruding(self) && line.comment().find("skirt") != std::string::npos) {
  236. layers_with_skirt[self.z()] = 1;
  237. }
  238. if (line.extruding(self) && line.comment().find("brim") != std::string::npos) {
  239. layers_with_brim[self.z()] = 1;
  240. }
  241. });
  242. clean_file(gcode_filepath, "gcode");
  243. THEN("no skrit") {
  244. REQUIRE(print.skirt().entities.size() == 0);
  245. for (auto obj : print.objects()) {
  246. REQUIRE(obj->skirt().entities.size() == 0);
  247. }
  248. }
  249. THEN("brim generated") {
  250. REQUIRE(print.brim().entities.size() == 0);
  251. for (auto obj : print.objects()) {
  252. REQUIRE(obj->brim().entities.size() > 0);
  253. }
  254. REQUIRE(layers_with_brim.size() == 1);
  255. }
  256. THEN("skirt_height is honored") {
  257. REQUIRE(layers_with_skirt.size() == 0);
  258. }
  259. }
  260. }
  261. }
  262. SCENARIO("Original Slic3r Skirt/Brim tests", "[!mayfail]") {
  263. GIVEN("Configuration with a skirt height of 2") {
  264. DynamicPrintConfig &config = Slic3r::DynamicPrintConfig::full_print_config();
  265. config.set_deserialize("skirts", "1");
  266. config.set_deserialize("skirt_height", "2");
  267. config.set_deserialize("perimeters", "1");
  268. config.set_deserialize("support_material_speed", "99");
  269. config.set_key_value("gcode_comments", new ConfigOptionBool(true));
  270. // avoid altering speeds unexpectedly
  271. config.set_deserialize("cooling", "0");
  272. config.set_deserialize("first_layer_speed", "100%");
  273. WHEN("multiple objects are printed") {
  274. auto gcode {std::stringstream("")};
  275. Model model{};
  276. Print print{};
  277. Slic3r::Test::init_print(print, { TestMesh::cube_20x20x20, TestMesh::cube_20x20x20 }, model, &config, false);
  278. std::map<double, bool> layers_with_skirt;
  279. std::string gcode_filepath{ "" };
  280. Slic3r::Test::gcode(gcode_filepath, print);
  281. auto parser {Slic3r::GCodeReader()};
  282. parser.parse_file(gcode_filepath, [&layers_with_skirt, &config] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
  283. {
  284. if (line.extruding(self) && line.comment().find("skirt") != std::string::npos) {
  285. layers_with_skirt[self.z()] = 1;
  286. }
  287. //if (self.z() > 0) {
  288. // if (line.extruding(self) && (line.new_F(self) == config.opt_float("support_material_speed") * 60.0) ) {
  289. // layers_with_skirt[self.z()] = 1;
  290. // }
  291. //}
  292. });
  293. clean_file(gcode_filepath, "gcode");
  294. THEN("skirt_height is honored") {
  295. REQUIRE(layers_with_skirt.size() == (size_t)config.opt_int("skirt_height"));
  296. }
  297. }
  298. }
  299. GIVEN("A default configuration") {
  300. DynamicPrintConfig &config = Slic3r::DynamicPrintConfig::full_print_config();
  301. config.set_deserialize("support_material_speed", "99");
  302. // avoid altering speeds unexpectedly
  303. config.set_deserialize("cooling", "0");
  304. config.set_deserialize("first_layer_speed", "100%");
  305. // remove noise from top/solid layers
  306. config.set_deserialize("top_solid_layers", "0");
  307. config.set_deserialize("bottom_solid_layers", "0");
  308. WHEN("Brim width is set to 5") {
  309. config.set_deserialize("perimeters", "0");
  310. config.set_deserialize("skirts", "0");
  311. config.set_deserialize("brim_width", "5");
  312. THEN("Brim is generated") {
  313. Model model{};
  314. Print print{};
  315. Slic3r::Test::init_print(print, { TestMesh::cube_20x20x20 }, model, &config, false);
  316. print.process();
  317. REQUIRE(print.brim().entities.size()>0);
  318. }
  319. }
  320. WHEN("Skirt area is smaller than the brim") {
  321. config.set_deserialize("skirts", "1");
  322. config.set_deserialize("brim_width", "10");
  323. THEN("GCode generates successfully.") {
  324. Model model{};
  325. Print print{};
  326. Slic3r::Test::init_print(print, { TestMesh::cube_20x20x20 }, model, &config, false);
  327. std::string gcode_filepath{ "" };
  328. Slic3r::Test::gcode(gcode_filepath, print);
  329. std::string gcode_from_file = read_to_string(gcode_filepath);
  330. REQUIRE(gcode_from_file.size() > 0);
  331. clean_file(gcode_filepath, "gcode");
  332. }
  333. }
  334. WHEN("Skirt height is 0 and skirts > 0") {
  335. config.set_deserialize("skirts", "2");
  336. config.set_deserialize("skirt_height", "0");
  337. THEN("GCode generates successfully.") {
  338. Model model{};
  339. Print print{};
  340. Slic3r::Test::init_print(print, { TestMesh::cube_20x20x20 }, model, &config, false);
  341. std::string gcode_filepath{ "" };
  342. Slic3r::Test::gcode(gcode_filepath, print);
  343. std::string gcode_from_file = read_to_string(gcode_filepath);
  344. REQUIRE(gcode_from_file.size() > 0);
  345. clean_file(gcode_filepath, "gcode");
  346. }
  347. }
  348. WHEN("Perimeter extruder = 2 and support extruders = 3") {
  349. THEN("Brim is printed with the extruder used for the perimeters of first object") {
  350. REQUIRE(true); //TODO
  351. }
  352. }
  353. WHEN("Perimeter extruder = 2, support extruders = 3, raft is enabled") {
  354. THEN("brim is printed with same extruder as skirt") {
  355. REQUIRE(true); //TODO
  356. }
  357. }
  358. //TODO review this test that fail because "there are no layer" and it test nothing anyway
  359. //WHEN("Object is plated with overhang support and a brim") {
  360. // config.set_deserialize("layer_height", "0.4");
  361. // config.set_deserialize("first_layer_height", "0.4");
  362. // config.set_deserialize("skirts", "1");
  363. // config.set_deserialize("skirt_distance", "0");
  364. // config.set_deserialize("support_material_speed", "99");
  365. // config.set_deserialize("perimeter_extruder", "1");
  366. // config.set_deserialize("support_material_extruder", "2");
  367. // config.set_deserialize("cooling", "0"); // to prevent speeds to be altered
  368. // config.set_deserialize("first_layer_speed", "100%"); // to prevent speeds to be altered
  369. // Slic3r::Model model;
  370. // Print print{};
  371. // Slic3r::Test::init_print(print, { TestMesh::overhang }, model, &config, false);
  372. // print.process();
  373. //
  374. // config.set_deserialize("support_material", "true"); // to prevent speeds to be altered
  375. // THEN("skirt length is large enough to contain object with support") {
  376. // REQUIRE(true); //TODO
  377. // }
  378. //}
  379. WHEN("Large minimum skirt length is used.") {
  380. config.set_deserialize("min_skirt_length", "20");
  381. auto gcode {std::stringstream("")};
  382. Slic3r::Model model;
  383. Print print{};
  384. Slic3r::Test::init_print(print, { TestMesh::cube_20x20x20 }, model, &config, false);
  385. THEN("Gcode generation doesn't crash") {
  386. std::string gcode_filepath{ "" };
  387. Slic3r::Test::gcode(gcode_filepath, print);
  388. std::string gcode_from_file = read_to_string(gcode_filepath);
  389. REQUIRE(gcode_from_file.size() > 0);
  390. clean_file(gcode_filepath, "gcode");
  391. }
  392. }
  393. }
  394. }