test_cancel_object.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. #include <catch2/catch.hpp>
  2. #include <sstream>
  3. #include <fstream>
  4. #include "libslic3r/GCode.hpp"
  5. #include "test_data.hpp"
  6. using namespace Slic3r;
  7. using namespace Test;
  8. constexpr bool debug_files{false};
  9. std::string remove_object(const std::string &gcode, const int id) {
  10. std::string result{gcode};
  11. std::string start_token{"M486 S" + std::to_string(id) + "\n"};
  12. std::string end_token{"M486 S-1\n"};
  13. std::size_t start{result.find(start_token)};
  14. while (start != std::string::npos) {
  15. std::size_t end_token_start{result.find(end_token, start)};
  16. std::size_t end{end_token_start + end_token.size()};
  17. result.replace(start, end - start, "");
  18. start = result.find(start_token);
  19. }
  20. return result;
  21. }
  22. TEST_CASE("Remove object sanity check", "[CancelObject]") {
  23. // clang-format off
  24. const std::string gcode{
  25. "the\n"
  26. "M486 S2\n"
  27. "to delete\n"
  28. "M486 S-1\n"
  29. "kept\n"
  30. "M486 S2\n"
  31. "to also delete\n"
  32. "M486 S-1\n"
  33. "lines\n"
  34. };
  35. // clang-format on
  36. const std::string result{remove_object(gcode, 2)};
  37. // clang-format off
  38. CHECK(result == std::string{
  39. "the\n"
  40. "kept\n"
  41. "lines\n"
  42. });
  43. // clang-format on
  44. }
  45. void check_retraction(const std::string &gcode, double offset = 0.0) {
  46. GCodeReader parser;
  47. std::map<int, double> retracted;
  48. unsigned count{0};
  49. std::set<int> there_is_unretract;
  50. int extruder_id{0};
  51. parser.parse_buffer(
  52. gcode,
  53. [&](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line) {
  54. INFO("Line number: " + std::to_string(++count));
  55. INFO("Extruder id: " + std::to_string(extruder_id));
  56. if (!line.raw().empty() && line.raw().front() == 'T') {
  57. extruder_id = std::stoi(std::string{line.raw().back()});
  58. }
  59. if (line.dist_XY(self) < std::numeric_limits<double>::epsilon()) {
  60. if (line.has_e() && line.e() < 0) {
  61. retracted[extruder_id] += line.e();
  62. }
  63. if (line.has_e() && line.e() > 0) {
  64. INFO("Line: " + line.raw());
  65. if (there_is_unretract.count(extruder_id) == 0) {
  66. there_is_unretract.insert(extruder_id);
  67. REQUIRE(retracted[extruder_id] + offset + line.e() == Approx(0.0));
  68. } else {
  69. REQUIRE(retracted[extruder_id] + line.e() == Approx(0.0));
  70. }
  71. retracted[extruder_id] = 0.0;
  72. }
  73. }
  74. }
  75. );
  76. }
  77. void add_object(
  78. Model &model, const std::string &name, const int extruder, const Vec3d &offset = Vec3d::Zero()
  79. ) {
  80. std::string extruder_id{std::to_string(extruder)};
  81. ModelObject *object = model.add_object();
  82. object->name = name;
  83. ModelVolume *volume = object->add_volume(Test::mesh(Test::TestMesh::cube_20x20x20));
  84. volume->set_material_id("material" + extruder_id);
  85. volume->translate(offset);
  86. DynamicPrintConfig config;
  87. config.set_deserialize_strict({
  88. {"extruder", extruder_id},
  89. });
  90. volume->config.assign_config(config);
  91. object->add_instance();
  92. object->ensure_on_bed();
  93. }
  94. class CancelObjectFixture
  95. {
  96. public:
  97. CancelObjectFixture() {
  98. config.set_deserialize_strict({
  99. {"gcode_flavor", "marlin2"},
  100. {"gcode_label_objects", "firmware"},
  101. {"gcode_comments", "1"},
  102. {"use_relative_e_distances", "1"},
  103. {"wipe", "0"},
  104. {"skirts", "0"},
  105. });
  106. add_object(two_cubes, "no_offset_cube", 0);
  107. add_object(two_cubes, "offset_cube", 0, {30.0, 0.0, 0.0});
  108. add_object(multimaterial_cubes, "no_offset_cube", 1);
  109. add_object(multimaterial_cubes, "offset_cube", 2, {30.0, 0.0, 0.0});
  110. retract_length = config.option<ConfigOptionFloats>("retract_length")->get_at(0);
  111. retract_length_toolchange = config.option<ConfigOptionFloats>("retract_length_toolchange")
  112. ->get_at(0);
  113. }
  114. DynamicPrintConfig config{Slic3r::DynamicPrintConfig::full_print_config()};
  115. Model two_cubes;
  116. Model multimaterial_cubes;
  117. double retract_length{};
  118. double retract_length_toolchange{};
  119. };
  120. TEST_CASE_METHOD(CancelObjectFixture, "Single extruder", "[CancelObject]") {
  121. Print print;
  122. print.apply(two_cubes, config);
  123. print.validate();
  124. const std::string gcode{Test::gcode(print)};
  125. if constexpr (debug_files) {
  126. std::ofstream output{"single_extruder_two.gcode"};
  127. output << gcode;
  128. }
  129. SECTION("One remaining") {
  130. const std::string removed_object_gcode{remove_object(gcode, 0)};
  131. REQUIRE(removed_object_gcode.find("M486 S1\n") != std::string::npos);
  132. if constexpr (debug_files) {
  133. std::ofstream output{"single_extruder_one.gcode"};
  134. output << removed_object_gcode;
  135. }
  136. check_retraction(removed_object_gcode);
  137. }
  138. SECTION("All cancelled") {
  139. const std::string removed_all_gcode{remove_object(remove_object(gcode, 0), 1)};
  140. // First retraction is not compensated - set offset.
  141. check_retraction(removed_all_gcode, retract_length);
  142. }
  143. }
  144. TEST_CASE_METHOD(CancelObjectFixture, "Sequential print", "[CancelObject]") {
  145. config.set_deserialize_strict({{"complete_objects", 1}});
  146. Print print;
  147. print.apply(two_cubes, config);
  148. print.validate();
  149. const std::string gcode{Test::gcode(print)};
  150. if constexpr (debug_files) {
  151. std::ofstream output{"sequential_print_two.gcode"};
  152. output << gcode;
  153. }
  154. SECTION("One remaining") {
  155. const std::string removed_object_gcode{remove_object(gcode, 0)};
  156. REQUIRE(removed_object_gcode.find("M486 S1\n") != std::string::npos);
  157. if constexpr (debug_files) {
  158. std::ofstream output{"sequential_print_one.gcode"};
  159. output << removed_object_gcode;
  160. }
  161. check_retraction(removed_object_gcode);
  162. }
  163. SECTION("All cancelled") {
  164. const std::string removed_all_gcode{remove_object(remove_object(gcode, 0), 1)};
  165. // First retraction is not compensated - set offset.
  166. check_retraction(removed_all_gcode, retract_length);
  167. }
  168. }