test_skirt_brim.cpp 19 KB

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