test_printgcode.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. #include <catch2/catch.hpp>
  2. #include "libslic3r/libslic3r.h"
  3. #include "libslic3r/GCodeReader.hpp"
  4. #include "test_data.hpp"
  5. #include <algorithm>
  6. #include <boost/regex.hpp>
  7. using namespace Slic3r;
  8. using namespace Slic3r::Test;
  9. boost::regex perimeters_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; perimeter");
  10. boost::regex infill_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; infill");
  11. boost::regex skirt_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; skirt");
  12. SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
  13. GIVEN("A default configuration and a print test object") {
  14. WHEN("the output is executed with no support material") {
  15. Slic3r::Print print;
  16. Slic3r::Model model;
  17. Slic3r::Test::init_print({TestMesh::cube_20x20x20}, print, model, {
  18. { "layer_height", 0.2 },
  19. { "first_layer_height", 0.2 },
  20. { "first_layer_extrusion_width", 0 },
  21. { "gcode_comments", true },
  22. { "start_gcode", "" }
  23. });
  24. std::string gcode = Slic3r::Test::gcode(print);
  25. THEN("Some text output is generated.") {
  26. REQUIRE(gcode.size() > 0);
  27. }
  28. THEN("Exported text contains slic3r version") {
  29. REQUIRE(gcode.find(SLIC3R_VERSION) != std::string::npos);
  30. }
  31. //THEN("Exported text contains git commit id") {
  32. // REQUIRE(gcode.find("; Git Commit") != std::string::npos);
  33. // REQUIRE(gcode.find(SLIC3R_BUILD_ID) != std::string::npos);
  34. //}
  35. THEN("Exported text contains extrusion statistics.") {
  36. REQUIRE(gcode.find("; external perimeters extrusion width") != std::string::npos);
  37. REQUIRE(gcode.find("; perimeters extrusion width") != std::string::npos);
  38. REQUIRE(gcode.find("; infill extrusion width") != std::string::npos);
  39. REQUIRE(gcode.find("; solid infill extrusion width") != std::string::npos);
  40. REQUIRE(gcode.find("; top infill extrusion width") != std::string::npos);
  41. REQUIRE(gcode.find("; support material extrusion width") == std::string::npos);
  42. REQUIRE(gcode.find("; first layer extrusion width") == std::string::npos);
  43. }
  44. THEN("Exported text does not contain cooling markers (they were consumed)") {
  45. REQUIRE(gcode.find(";_EXTRUDE_SET_SPEED") == std::string::npos);
  46. }
  47. THEN("GCode preamble is emitted.") {
  48. REQUIRE(gcode.find("G21 ; set units to millimeters") != std::string::npos);
  49. }
  50. THEN("Config options emitted for print config, default region config, default object config") {
  51. REQUIRE(gcode.find("; first_layer_temperature") != std::string::npos);
  52. REQUIRE(gcode.find("; layer_height") != std::string::npos);
  53. REQUIRE(gcode.find("; fill_density") != std::string::npos);
  54. }
  55. THEN("Infill is emitted.") {
  56. boost::smatch has_match;
  57. REQUIRE(boost::regex_search(gcode, has_match, infill_regex));
  58. }
  59. THEN("Perimeters are emitted.") {
  60. boost::smatch has_match;
  61. REQUIRE(boost::regex_search(gcode, has_match, perimeters_regex));
  62. }
  63. THEN("Skirt is emitted.") {
  64. boost::smatch has_match;
  65. REQUIRE(boost::regex_search(gcode, has_match, skirt_regex));
  66. }
  67. THEN("final Z height is 20mm") {
  68. double final_z = 0.0;
  69. GCodeReader reader;
  70. reader.apply_config(print.config());
  71. reader.parse_buffer(gcode, [&final_z] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
  72. final_z = std::max<double>(final_z, static_cast<double>(self.z())); // record the highest Z point we reach
  73. });
  74. REQUIRE(final_z == Approx(20.));
  75. }
  76. }
  77. WHEN("output is executed with complete objects and two differently-sized meshes") {
  78. Slic3r::Print print;
  79. Slic3r::Model model;
  80. Slic3r::Test::init_print({TestMesh::cube_20x20x20,TestMesh::cube_20x20x20}, print, model, {
  81. { "first_layer_extrusion_width", 0 },
  82. { "first_layer_height", 0.3 },
  83. { "layer_height", 0.2 },
  84. { "support_material", false },
  85. { "raft_layers", 0 },
  86. { "complete_objects", true },
  87. { "gcode_comments", true },
  88. { "between_objects_gcode", "; between-object-gcode" }
  89. });
  90. std::string gcode = Slic3r::Test::gcode(print);
  91. THEN("Some text output is generated.") {
  92. REQUIRE(gcode.size() > 0);
  93. }
  94. THEN("Infill is emitted.") {
  95. boost::smatch has_match;
  96. REQUIRE(boost::regex_search(gcode, has_match, infill_regex));
  97. }
  98. THEN("Perimeters are emitted.") {
  99. boost::smatch has_match;
  100. REQUIRE(boost::regex_search(gcode, has_match, perimeters_regex));
  101. }
  102. THEN("Skirt is emitted.") {
  103. boost::smatch has_match;
  104. REQUIRE(boost::regex_search(gcode, has_match, skirt_regex));
  105. }
  106. THEN("Between-object-gcode is emitted.") {
  107. REQUIRE(gcode.find("; between-object-gcode") != std::string::npos);
  108. }
  109. THEN("final Z height is 20.1mm") {
  110. double final_z = 0.0;
  111. GCodeReader reader;
  112. reader.apply_config(print.config());
  113. reader.parse_buffer(gcode, [&final_z] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
  114. final_z = std::max(final_z, static_cast<double>(self.z())); // record the highest Z point we reach
  115. });
  116. REQUIRE(final_z == Approx(20.1));
  117. }
  118. THEN("Z height resets on object change") {
  119. double final_z = 0.0;
  120. bool reset = false;
  121. GCodeReader reader;
  122. reader.apply_config(print.config());
  123. reader.parse_buffer(gcode, [&final_z, &reset] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
  124. if (final_z > 0 && std::abs(self.z() - 0.3) < 0.01 ) { // saw higher Z before this, now it's lower
  125. reset = true;
  126. } else {
  127. final_z = std::max(final_z, static_cast<double>(self.z())); // record the highest Z point we reach
  128. }
  129. });
  130. REQUIRE(reset == true);
  131. }
  132. THEN("Shorter object is printed before taller object.") {
  133. double final_z = 0.0;
  134. bool reset = false;
  135. GCodeReader reader;
  136. reader.apply_config(print.config());
  137. reader.parse_buffer(gcode, [&final_z, &reset] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
  138. if (final_z > 0 && std::abs(self.z() - 0.3) < 0.01 ) {
  139. reset = (final_z > 20.0);
  140. } else {
  141. final_z = std::max(final_z, static_cast<double>(self.z())); // record the highest Z point we reach
  142. }
  143. });
  144. REQUIRE(reset == true);
  145. }
  146. }
  147. WHEN("the output is executed with support material") {
  148. std::string gcode = ::Test::slice({TestMesh::cube_20x20x20}, {
  149. { "first_layer_extrusion_width", 0 },
  150. { "support_material", true },
  151. { "raft_layers", 3 },
  152. { "gcode_comments", true }
  153. });
  154. THEN("Some text output is generated.") {
  155. REQUIRE(gcode.size() > 0);
  156. }
  157. THEN("Exported text contains extrusion statistics.") {
  158. REQUIRE(gcode.find("; external perimeters extrusion width") != std::string::npos);
  159. REQUIRE(gcode.find("; perimeters extrusion width") != std::string::npos);
  160. REQUIRE(gcode.find("; infill extrusion width") != std::string::npos);
  161. REQUIRE(gcode.find("; solid infill extrusion width") != std::string::npos);
  162. REQUIRE(gcode.find("; top infill extrusion width") != std::string::npos);
  163. REQUIRE(gcode.find("; support material extrusion width") != std::string::npos);
  164. REQUIRE(gcode.find("; first layer extrusion width") == std::string::npos);
  165. }
  166. THEN("Raft is emitted.") {
  167. REQUIRE(gcode.find("; raft") != std::string::npos);
  168. }
  169. }
  170. WHEN("the output is executed with a separate first layer extrusion width") {
  171. std::string gcode = ::Test::slice({ TestMesh::cube_20x20x20 }, {
  172. { "first_layer_extrusion_width", "0.5" }
  173. });
  174. THEN("Some text output is generated.") {
  175. REQUIRE(gcode.size() > 0);
  176. }
  177. THEN("Exported text contains extrusion statistics.") {
  178. REQUIRE(gcode.find("; external perimeters extrusion width") != std::string::npos);
  179. REQUIRE(gcode.find("; perimeters extrusion width") != std::string::npos);
  180. REQUIRE(gcode.find("; infill extrusion width") != std::string::npos);
  181. REQUIRE(gcode.find("; solid infill extrusion width") != std::string::npos);
  182. REQUIRE(gcode.find("; top infill extrusion width") != std::string::npos);
  183. REQUIRE(gcode.find("; support material extrusion width") == std::string::npos);
  184. REQUIRE(gcode.find("; first layer extrusion width") != std::string::npos);
  185. }
  186. }
  187. WHEN("Cooling is enabled and the fan is disabled.") {
  188. std::string gcode = ::Test::slice({ TestMesh::cube_20x20x20 }, {
  189. { "cooling", true },
  190. { "disable_fan_first_layers", 5 }
  191. });
  192. THEN("GCode to disable fan is emitted."){
  193. REQUIRE(gcode.find("M107") != std::string::npos);
  194. }
  195. }
  196. WHEN("end_gcode exists with layer_num and layer_z") {
  197. std::string gcode = ::Test::slice({ TestMesh::cube_20x20x20 }, {
  198. { "end_gcode", "; Layer_num [layer_num]\n; Layer_z [layer_z]" },
  199. { "layer_height", 0.1 },
  200. { "first_layer_height", 0.1 }
  201. });
  202. THEN("layer_num and layer_z are processed in the end gcode") {
  203. REQUIRE(gcode.find("; Layer_num 199") != std::string::npos);
  204. REQUIRE(gcode.find("; Layer_z 20") != std::string::npos);
  205. }
  206. }
  207. WHEN("current_extruder exists in start_gcode") {
  208. {
  209. std::string gcode = ::Test::slice({ TestMesh::cube_20x20x20 }, {
  210. { "start_gcode", "; Extruder [current_extruder]" }
  211. });
  212. THEN("current_extruder is processed in the start gcode and set for first extruder") {
  213. REQUIRE(gcode.find("; Extruder 0") != std::string::npos);
  214. }
  215. }
  216. {
  217. DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
  218. config.set_num_extruders(4);
  219. config.set_deserialize_strict({
  220. { "start_gcode", "; Extruder [current_extruder]" },
  221. { "infill_extruder", 2 },
  222. { "solid_infill_extruder", 2 },
  223. { "perimeter_extruder", 2 },
  224. { "support_material_extruder", 2 },
  225. { "support_material_interface_extruder", 2 }
  226. });
  227. std::string gcode = Slic3r::Test::slice({TestMesh::cube_20x20x20}, config);
  228. THEN("current_extruder is processed in the start gcode and set for second extruder") {
  229. REQUIRE(gcode.find("; Extruder 1") != std::string::npos);
  230. }
  231. }
  232. }
  233. WHEN("layer_num represents the layer's index from z=0") {
  234. std::string gcode = ::Test::slice({ TestMesh::cube_20x20x20, TestMesh::cube_20x20x20 }, {
  235. { "complete_objects", true },
  236. { "gcode_comments", true },
  237. { "layer_gcode", ";Layer:[layer_num] ([layer_z] mm)" },
  238. { "layer_height", 0.1 },
  239. { "first_layer_height", 0.1 }
  240. });
  241. // End of the 1st object.
  242. std::string token = ";Layer:199 ";
  243. size_t pos = gcode.find(token);
  244. THEN("First and second object last layer is emitted") {
  245. // First object
  246. REQUIRE(pos != std::string::npos);
  247. pos += token.size();
  248. REQUIRE(pos < gcode.size());
  249. double z = 0;
  250. REQUIRE((sscanf(gcode.data() + pos, "(%lf mm)", &z) == 1));
  251. REQUIRE(z == Approx(20.));
  252. // Second object
  253. pos = gcode.find(";Layer:399 ", pos);
  254. REQUIRE(pos != std::string::npos);
  255. pos += token.size();
  256. REQUIRE(pos < gcode.size());
  257. REQUIRE((sscanf(gcode.data() + pos, "(%lf mm)", &z) == 1));
  258. REQUIRE(z == Approx(20.));
  259. }
  260. }
  261. }
  262. }