test_print.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. //#define CATCH_CONFIG_DISABLE
  2. #include <catch_main.hpp>
  3. #include "test_data.hpp"
  4. #include <libslic3r/libslic3r.h>
  5. #include <libslic3r/Layer.hpp>
  6. #include <libslic3r/SVG.hpp>
  7. #include <libslic3r/Format/3mf.hpp>
  8. //#include <libslic3r/config.hpp>
  9. #include <string>
  10. using namespace Slic3r;
  11. using namespace Slic3r::Test;
  12. using namespace std::literals;
  13. SCENARIO("PrintObject: Perimeter generation") {
  14. GIVEN("20mm cube and default config & 0.3 layer height") {
  15. DynamicPrintConfig &config = Slic3r::DynamicPrintConfig::full_print_config();
  16. TestMesh m = TestMesh::cube_20x20x20;
  17. Model model{};
  18. config.set_key_value("fill_density", new ConfigOptionPercent(0));
  19. config.set_deserialize("nozzle_diameter", "0.4");
  20. config.set_deserialize("layer_height", "0.3");
  21. WHEN("make_perimeters() is called") {
  22. Print print{};
  23. Slic3r::Test::init_print(print, { m }, model, &config);
  24. PrintObject& object = *print.objects_mutable().at(0);
  25. print.process();
  26. // there are 66.66666.... layers for 0.3mm in 20mm
  27. //slic3r is rounded (slice at half-layer), slic3rPE is less?
  28. //TODO: check the slic32r why it's not cut at half-layer
  29. THEN("67 layers exist in the model") {
  30. REQUIRE(object.layers().size() == 67);
  31. }
  32. THEN("Every layer in region 0 has 1 island of perimeters") {
  33. for(Layer* layer : object.layers()) {
  34. REQUIRE(layer->regions()[0]->perimeters.entities().size() == 1);
  35. }
  36. }
  37. THEN("Every layer (but top) in region 0 has 3 paths in its perimeters list.") {
  38. LayerPtrs layers = object.layers();
  39. for (auto it_layer = layers.begin(); it_layer != layers.end() - 1; ++it_layer) {
  40. REQUIRE((*it_layer)->regions()[0]->perimeters.items_count() == 3);
  41. }
  42. }
  43. THEN("Top layer in region 0 has 1 path in its perimeters list (only 1 perimeter on top).") {
  44. REQUIRE(object.layers().back()->regions()[0]->perimeters.items_count() == 1);
  45. }
  46. }
  47. }
  48. }
  49. SCENARIO("Print: Skirt generation") {
  50. GIVEN("20mm cube and default config") {
  51. DynamicPrintConfig &config = Slic3r::DynamicPrintConfig::full_print_config();
  52. TestMesh m = TestMesh::cube_20x20x20;
  53. Slic3r::Model model{};
  54. config.set_key_value("skirt_height", new ConfigOptionInt(1));
  55. config.set_key_value("skirt_distance", new ConfigOptionFloat(1));
  56. WHEN("Skirts is set to 2 loops") {
  57. config.set_key_value("skirts", new ConfigOptionInt(2));
  58. Print print{};
  59. Slic3r::Test::init_print(print, { m }, model, &config);
  60. print.process();
  61. THEN("Skirt Extrusion collection has 2 loops in it") {
  62. REQUIRE(print.skirt().items_count() == 2);
  63. REQUIRE(print.skirt().flatten().entities().size() == 2);
  64. }
  65. }
  66. }
  67. }
  68. void test_is_solid_infill(Print &p, size_t obj_id, size_t layer_id, bool check = true ) {
  69. const PrintObject& obj { *(p.objects().at(obj_id)) };
  70. const Layer& layer { *(obj.get_layer((int)layer_id)) };
  71. // iterate over all of the regions in the layer
  72. for (const LayerRegion* reg : layer.regions()) {
  73. // for each region, iterate over the fill surfaces
  74. for (const Surface& su : reg->fill_surfaces.surfaces) {
  75. CHECK(su.has_fill_solid() == check);
  76. }
  77. }
  78. }
  79. SCENARIO("Print: Changing number of solid surfaces does not cause all surfaces to become internal.") {
  80. GIVEN("sliced 20mm cube and config with top_solid_surfaces = 2 and bottom_solid_surfaces = 1") {
  81. DynamicPrintConfig &config = Slic3r::DynamicPrintConfig::full_print_config();
  82. TestMesh m { TestMesh::cube_20x20x20 };
  83. config.set_key_value("top_solid_layers", new ConfigOptionInt(2));
  84. config.set_key_value("bottom_solid_layers", new ConfigOptionInt(1));
  85. config.set_key_value("layer_height", new ConfigOptionFloat(0.5)); // get a known number of layers
  86. config.set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(0.5, false));
  87. config.set_key_value("nozzle_diameter", new ConfigOptionFloats({1})); // has to be large enough for 0.5 layer height, the min/max lh depends on it
  88. config.set_key_value("enforce_full_fill_volume", new ConfigOptionBool(true));
  89. Slic3r::Model model;
  90. auto event_counter {0U};
  91. std::string stage;
  92. Print print{};
  93. Slic3r::Test::init_print(print, { m }, model, &config);
  94. print.process();
  95. // Precondition: Ensure that the model has 2 solid top layers (39, 38)
  96. // and one solid bottom layer (0).
  97. THEN("20 mm with 0.5 layer height means 40 layers.")
  98. {
  99. REQUIRE(print.objects().front()->layer_count() == 40);
  100. }
  101. THEN("First layer is solid bottom layer.")
  102. {
  103. test_is_solid_infill(print, 0, 0); // should be solid
  104. test_is_solid_infill(print, 0, 1, false); // should not be solid
  105. }
  106. THEN("Two last layers are solid bottom layer.")
  107. {
  108. test_is_solid_infill(print, 0, 39); // should be solid
  109. test_is_solid_infill(print, 0, 38); // should be solid
  110. test_is_solid_infill(print, 0, 37, false); // should not be solid
  111. }
  112. WHEN("Model is re-sliced with top_solid_layers == 3") {
  113. ((ConfigOptionInt&)(print.get_print_region(0).config().top_solid_layers)).value = 3;
  114. REQUIRE(print.get_print_region(0).config().top_solid_layers.value == 3);
  115. DynamicPrintConfig useless;
  116. print.invalidate_state_by_config_options(useless, std::vector<Slic3r::t_config_option_key>{ "posPrepareInfill" });
  117. print.process();
  118. THEN("Print object does not have 0 solid bottom layers.") {
  119. test_is_solid_infill(print, 0, 0);
  120. }
  121. AND_THEN("Print object has 3 top solid layers") {
  122. test_is_solid_infill(print, 0, 39);
  123. test_is_solid_infill(print, 0, 38);
  124. test_is_solid_infill(print, 0, 37);
  125. }
  126. }
  127. }
  128. }
  129. SCENARIO("Print: Brim generation") {
  130. GIVEN("20mm cube and default config, 1mm first layer width") {
  131. DynamicPrintConfig &config = Slic3r::DynamicPrintConfig::full_print_config();
  132. TestMesh m{ TestMesh::cube_20x20x20 };
  133. Slic3r::Model model{};
  134. config.set_key_value("first_layer_extrusion_width", new ConfigOptionFloatOrPercent(1, false));
  135. WHEN("Brim is set to 3mm") {
  136. config.set_key_value("brim_width", new ConfigOptionFloat(3));
  137. Print print{};
  138. Slic3r::Test::init_print(print, { m }, model, &config);
  139. print.process();
  140. //{
  141. // std::stringstream stri;
  142. // stri << "20mm_cube_brim_test_" << ".svg";
  143. // SVG svg(stri.str());
  144. // svg.draw(print.brim().as_polylines(), "red");
  145. // svg.Close();
  146. //}
  147. THEN("Brim Extrusion collection has 3 loops in it") {
  148. REQUIRE(print.brim().items_count() == 3);
  149. }
  150. }
  151. WHEN("Brim is set to 6mm") {
  152. config.set_key_value("brim_width", new ConfigOptionFloat(6));
  153. Print print{};
  154. Slic3r::Test::init_print(print, { m }, model, &config);
  155. print.process();
  156. THEN("Brim Extrusion collection has 6 loops in it") {
  157. REQUIRE(print.brim().items_count() == 6);
  158. }
  159. }
  160. WHEN("Brim is set to 6mm with 1mm offset") {
  161. config.set_key_value("brim_width", new ConfigOptionFloat(6));
  162. config.set_key_value("brim_offset", new ConfigOptionFloat(1));
  163. Print print{};
  164. Slic3r::Test::init_print(print, { m }, model, &config);
  165. print.process();
  166. THEN("Brim Extrusion collection has 6 loops in it, even with offset") {
  167. REQUIRE(print.brim().items_count() == 6);
  168. }
  169. }
  170. WHEN("Brim without first layer compensation") {
  171. config.set_key_value("brim_width", new ConfigOptionFloat(1));
  172. config.set_key_value("brim_offset", new ConfigOptionFloat(0));
  173. Print print{};
  174. Slic3r::Test::init_print(print, { m }, model, &config);
  175. print.process();
  176. Flow brim_flow = print.brim_flow(0, print.default_object_config());
  177. THEN("First Brim Extrusion has a length of ~84") {
  178. REQUIRE(print.brim().entities().size() > 0);
  179. double dist = unscaled(ExtrusionLength{}.length(*print.brim().entities().front()));
  180. REQUIRE(dist < (20 + brim_flow.spacing()) * 4 ); // little lower because of the round edge at the corners
  181. REQUIRE(dist > (20 + brim_flow.spacing()) * 4 - 1);
  182. }
  183. }
  184. WHEN("Brim with 1mm first layer compensation") {
  185. config.set_key_value("brim_width", new ConfigOptionFloat(1));
  186. config.set_key_value("brim_offset", new ConfigOptionFloat(0));
  187. config.set_key_value("first_layer_size_compensation", new ConfigOptionFloat(-0.5));
  188. Print print{};
  189. Slic3r::Test::init_print(print, { m }, model, &config);
  190. print.process();
  191. Flow brim_flow = print.brim_flow(0, print.objects().at(0)->config());
  192. THEN("First Brim Extrusion has a length of ~80") {
  193. REQUIRE(print.brim().entities().size() > 0);
  194. double dist = unscaled(ExtrusionLength{}.length(*print.brim().entities().front()));
  195. REQUIRE(dist < (20 + brim_flow.spacing() - 1) * 4 );// little lower because of the round edge at the corners
  196. REQUIRE(dist > (20 + brim_flow.spacing() - 1) * 4 - 1);
  197. }
  198. }
  199. WHEN("Brim is set to 6mm, extrusion width 0.5mm") {
  200. config.set_key_value("brim_width", new ConfigOptionFloat(6));
  201. config.set_key_value("first_layer_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
  202. Print print{};
  203. Slic3r::Test::init_print(print, { m }, model, &config);
  204. print.process();
  205. double nbLoops = 6.0 / print.brim_flow(*print.extruders().begin(), print.objects().at(0)->config()).spacing();
  206. THEN("Brim Extrusion collection has " + std::to_string(nbLoops) + " loops in it (flow=" +
  207. std::to_string(print.brim_flow(*print.extruders().begin(), print.objects().at(0)->config()).spacing()) + ")")
  208. {
  209. REQUIRE(print.brim().items_count() == floor(nbLoops));
  210. }
  211. }
  212. WHEN("Brim ears activated, 3mm") {
  213. config.set_key_value("brim_width", new ConfigOptionFloat(3));
  214. config.set_key_value("brim_ears", new ConfigOptionBool(true));
  215. Print print{};
  216. Slic3r::Test::init_print(print, { m }, model, &config);
  217. print.process();
  218. THEN("Brim ears Extrusion collection has 4 extrusions in it") {
  219. REQUIRE(print.brim().items_count() == 4);
  220. }
  221. }
  222. }
  223. }
  224. struct GetFirst : ExtrusionVisitorRecursive
  225. {
  226. ExtrusionLoop* first = nullptr;
  227. ExtrusionLoop* previous_last = nullptr;
  228. ExtrusionLoop* last = nullptr;
  229. void default_use(ExtrusionEntity &entity) override { assert(false); };
  230. void use(ExtrusionLoop& loop) override { if(!first) first = &loop; previous_last = last; last = &loop; }
  231. };
  232. SCENARIO("Print: perimeter generation : cube with hole, just enough space for two loops at a point")
  233. {
  234. DynamicPrintConfig &config = Slic3r::DynamicPrintConfig::full_print_config();
  235. Slic3r::Model model{};
  236. config.set_key_value("first_layer_extrusion_width", new ConfigOptionFloatOrPercent(0.42, false));
  237. config.set_deserialize("nozzle_diameter", "0.4");
  238. config.set_deserialize("layer_height", "0.2");
  239. config.set_deserialize("first_layer_height", "0.2");
  240. config.set_key_value("only_one_perimeter_top", new ConfigOptionBool(false));
  241. auto facets = std::vector<Vec3i32>{Vec3i32(1, 4, 3), Vec3i32(4, 1, 2), Vec3i32(16, 12, 14),
  242. Vec3i32(16, 10, 12), Vec3i32(10, 4, 6), Vec3i32(4, 10, 16),
  243. Vec3i32(8, 14, 12), Vec3i32(8, 2, 14), Vec3i32(6, 2, 8),
  244. Vec3i32(2, 6, 4), Vec3i32(14, 15, 16), Vec3i32(15, 14, 13),
  245. Vec3i32(15, 4, 16), Vec3i32(4, 15, 3), Vec3i32(13, 11, 15),
  246. Vec3i32(13, 7, 11), Vec3i32(7, 1, 5), Vec3i32(1, 7, 13),
  247. Vec3i32(9, 15, 11), Vec3i32(9, 3, 15), Vec3i32(5, 3, 9),
  248. Vec3i32(3, 5, 1), Vec3i32(1, 14, 2), Vec3i32(14, 1, 13),
  249. Vec3i32(9, 12, 10), Vec3i32(12, 9, 11), Vec3i32(6, 9, 10),
  250. Vec3i32(9, 6, 5), Vec3i32(8, 5, 6), Vec3i32(5, 8, 7),
  251. Vec3i32(7, 12, 11), Vec3i32(12, 7, 8)};
  252. for (Vec3i32 &vec : facets) vec -= Vec3i32(1, 1, 1);
  253. TriangleMesh tm = TriangleMesh{std::vector<Vec3f>{Vec3f(-5, -5, -0.1), Vec3f(-5, -5, 0.1), Vec3f(-5, 5, -0.1),
  254. Vec3f(-5, 5, 0.1), Vec3f(-1.328430, 0, -0.1),
  255. Vec3f(-1.328430, 0, 0.1), Vec3f(1.5, -2.828430, -0.1),
  256. Vec3f(1.5, -2.828430, 0.1), Vec3f(1.5, 2.828430, -0.1),
  257. Vec3f(1.5, 2.828430, 0.1), Vec3f(4.328430, 0, -0.1),
  258. Vec3f(4.328430, 0, 0.1), Vec3f(5, -5, -0.1), Vec3f(5, -5, 0.1),
  259. Vec3f(5, 5, -0.1), Vec3f(5, 5, 0.1)},
  260. facets};
  261. GIVEN("no brim")
  262. {
  263. Print print{};
  264. Slic3r::Test::init_print(print, {tm}, model, &config);
  265. print.process();
  266. ExtrusionPrinter printer(/*mult=*/0.000001, /*trunc=*/100, /*json=*/true);
  267. //std::cout << "\n\n\nNO BRIM EXTUSIONS:\n";
  268. //print.objects()[0]->layers()[0]->regions()[0]->perimeters.visit(printer);
  269. //std::cout << "{" << printer.str() << "}";
  270. //Model model = print.model();
  271. //Slic3r::store_3mf("test_without_brim.3mf", &model, &print.full_print_config(), OptionStore3mf{});
  272. // see https://github.com/supermerill/SuperSlicer/issues/242, why the hole is after the contour inner
  273. THEN("hole perimeter should not be printed first, but still before external one")
  274. {
  275. GetFirst get_first_visitor;
  276. print.objects()[0]->layers()[0]->regions()[0]->perimeters.visit(get_first_visitor);
  277. REQUIRE(get_first_visitor.first != nullptr);
  278. REQUIRE(get_first_visitor.first->is_loop());
  279. // first inner contour peri
  280. REQUIRE((get_first_visitor.first->loop_role() & ExtrusionLoopRole::elrHole) == 0);
  281. REQUIRE(get_first_visitor.first->role() == ExtrusionRole::erPerimeter);
  282. // the external hole perimeter (because it's alone without inner ones, we don't want it to be used as a skirt)
  283. // note: here we may want to have the seam of this one near the next one instead of near our current pos,
  284. // if it's an external one, to avoid oozing before external.
  285. REQUIRE((get_first_visitor.previous_last->loop_role() & ExtrusionLoopRole::elrHole) != 0);
  286. REQUIRE(get_first_visitor.previous_last->role() == ExtrusionRole::erExternalPerimeter);
  287. // the external contour perimeter
  288. REQUIRE((get_first_visitor.last->loop_role() & ExtrusionLoopRole::elrHole) == 0);
  289. REQUIRE(get_first_visitor.last->role() == ExtrusionRole::erExternalPerimeter);
  290. }
  291. }
  292. GIVEN("brim")
  293. {
  294. config.set_deserialize("brim_width", "0.2");
  295. Print print{};
  296. Slic3r::Test::init_print(print, {tm}, model, &config);
  297. print.process();
  298. THEN("hole perimeter should not be printed first")
  299. {
  300. GetFirst get_first_visitor;
  301. print.objects()[0]->layers()[0]->regions()[0]->perimeters.visit(get_first_visitor);
  302. // the external contour perimeter
  303. REQUIRE((get_first_visitor.first->loop_role() & ExtrusionLoopRole::elrHole) == 0);
  304. REQUIRE(get_first_visitor.first->role() == ExtrusionRole::erExternalPerimeter);
  305. // first inner contour peri
  306. REQUIRE((get_first_visitor.previous_last->loop_role() & ExtrusionLoopRole::elrHole) == 0);
  307. REQUIRE(get_first_visitor.previous_last->role() == ExtrusionRole::erPerimeter);
  308. // the external hole perimeter
  309. REQUIRE((get_first_visitor.last->loop_role() & ExtrusionLoopRole::elrHole) == ExtrusionLoopRole::elrHole);
  310. REQUIRE(get_first_visitor.last->role() == ExtrusionRole::erExternalPerimeter);
  311. }
  312. }
  313. }
  314. struct GetAll : ExtrusionVisitorRecursive
  315. {
  316. std::vector<ExtrusionLoop*> loops;
  317. void default_use(ExtrusionEntity &entity) override { assert(false); };
  318. void use(ExtrusionLoop& loop) override { loops.push_back(&loop); }
  319. };
  320. SCENARIO("Print: perimeter generation : cube with hole in center") {
  321. DynamicPrintConfig& config = Slic3r::DynamicPrintConfig::full_print_config();
  322. Slic3r::Model model{};
  323. config.set_key_value("first_layer_extrusion_width", new ConfigOptionFloatOrPercent(0.42, false));
  324. config.set_deserialize("nozzle_diameter", "0.4");
  325. config.set_deserialize("layer_height", "0.2");
  326. config.set_deserialize("first_layer_height", "0.2");
  327. config.set_key_value("only_one_perimeter_top", new ConfigOptionBool(false));
  328. std::vector<Vec3f> v{{-10, 10, -0.1}, {-10, -10, -0.1}, {-10, 10, 0.1}, {-10, -10, 0.1},
  329. {10, -10, -0.1}, {10, -10, 0.1}, {-2.5, 2.5, 0.1}, {-2.5, -2.5, 0.1},
  330. {2.5, -2.5, 0.1}, {10, 10, 0.1}, {2.5, 2.5, 0.1}, {10, 10, -0.1},
  331. {-2.5, 2.5, -0.1}, {-2.5, -2.5, -0.1}, {2.5, -2.5, -0.1}, {2.5, 2.5, -0.1}};
  332. std::vector<Vec3i32> i{{0, 1, 2}, {1, 3, 2}, {1, 4, 3}, {4, 5, 3}, {6, 2, 3}, {6, 3, 7},
  333. {5, 8, 7}, {5, 7, 3}, {9, 6, 10}, {9, 10, 8}, {9, 8, 5}, {9, 2, 6},
  334. {11, 0, 2}, {9, 11, 2}, {0, 12, 1}, {1, 12, 13}, {14, 4, 13}, {13, 4, 1},
  335. {12, 11, 15}, {15, 11, 14}, {14, 11, 4}, {0, 11, 12}, {4, 11, 9}, {5, 4, 9},
  336. {7, 13, 12}, {7, 12, 6}, {8, 14, 13}, {8, 13, 7}, {14, 8, 15}, {15, 8, 10},
  337. {15, 10, 12}, {12, 10, 6}};
  338. TriangleMesh tm = TriangleMesh(v, i);
  339. GIVEN("no brim")
  340. {
  341. config.set_deserialize("brim_width", "0");
  342. config.set_deserialize("brim_width_interior", "0");
  343. Print print{};
  344. Slic3r::Test::init_print(print, {tm}, model, &config);
  345. print.process();
  346. // see https://github.com/supermerill/SuperSlicer/issues/242, why the hole is after the contour inner
  347. GetAll get_all_visitor;
  348. print.objects()[0]->layers()[0]->regions()[0]->perimeters.visit(get_all_visitor);
  349. auto &loops = get_all_visitor.loops;
  350. THEN("hole printed first, external last")
  351. {
  352. REQUIRE(loops.size() == 6);
  353. // first holes
  354. REQUIRE((loops[0]->loop_role() & ExtrusionLoopRole::elrHole) == ExtrusionLoopRole::elrHole);
  355. REQUIRE(loops[0]->role() == ExtrusionRole::erPerimeter);
  356. REQUIRE((loops[2]->loop_role() & ExtrusionLoopRole::elrHole) == ExtrusionLoopRole::elrHole);
  357. REQUIRE(loops[2]->role() == ExtrusionRole::erExternalPerimeter);
  358. }
  359. THEN("contour printed last, external last")
  360. {
  361. //then contour
  362. REQUIRE( (loops[3]->loop_role() & ExtrusionLoopRole::elrHole) == 0);
  363. REQUIRE( loops[3]->role() == ExtrusionRole::erPerimeter);
  364. REQUIRE( (loops[5]->loop_role() & ExtrusionLoopRole::elrHole) == 0);
  365. REQUIRE( loops[5]->role() == ExtrusionRole::erExternalPerimeter);
  366. }
  367. }
  368. GIVEN("contour brim")
  369. {
  370. config.set_deserialize("brim_width", "1");
  371. config.set_deserialize("brim_width_interior", "0");
  372. Print print{};
  373. Slic3r::Test::init_print(print, {tm}, model, &config);
  374. print.process();
  375. ExtrusionPrinter printer(/*mult=*/0.000001, /*trunc=*/100, /*json=*/true);
  376. GetAll get_all_visitor;
  377. print.objects()[0]->layers()[0]->regions()[0]->perimeters.visit(get_all_visitor);
  378. auto &loops = get_all_visitor.loops;
  379. REQUIRE(loops.size() == 6);
  380. THEN("contour printed first, external first")
  381. {
  382. //then contour
  383. REQUIRE( (loops[0]->loop_role() & ExtrusionLoopRole::elrHole) == 0);
  384. REQUIRE( loops[0]->role() == ExtrusionRole::erExternalPerimeter);
  385. REQUIRE( (loops[2]->loop_role() & ExtrusionLoopRole::elrHole) == 0);
  386. REQUIRE( loops[2]->role() == ExtrusionRole::erPerimeter);
  387. }
  388. THEN("hole printed last, external last as there is no hole brim")
  389. {
  390. // first holes
  391. REQUIRE((loops[3]->loop_role() & ExtrusionLoopRole::elrHole) == ExtrusionLoopRole::elrHole);
  392. REQUIRE(loops[3]->role() == ExtrusionRole::erPerimeter);
  393. REQUIRE((loops[5]->loop_role() & ExtrusionLoopRole::elrHole) == ExtrusionLoopRole::elrHole);
  394. REQUIRE(loops[5]->role() == ExtrusionRole::erExternalPerimeter);
  395. }
  396. //TODO: thinwall after evrything
  397. }
  398. GIVEN("hole brim")
  399. {
  400. config.set_deserialize("brim_width", "0");
  401. config.set_deserialize("brim_width_interior", "1");
  402. Print print{};
  403. Slic3r::Test::init_print(print, {tm}, model, &config);
  404. print.process();
  405. ExtrusionPrinter printer(/*mult=*/0.000001, /*trunc=*/100, /*json=*/true);
  406. GetAll get_all_visitor;
  407. print.objects()[0]->layers()[0]->regions()[0]->perimeters.visit(get_all_visitor);
  408. auto &loops = get_all_visitor.loops;
  409. REQUIRE(loops.size() == 6);
  410. THEN("hole printed first, external first")
  411. {
  412. // first holes
  413. REQUIRE((loops[0]->loop_role() & ExtrusionLoopRole::elrHole) == ExtrusionLoopRole::elrHole);
  414. REQUIRE(loops[0]->role() == ExtrusionRole::erExternalPerimeter);
  415. REQUIRE((loops[2]->loop_role() & ExtrusionLoopRole::elrHole) == ExtrusionLoopRole::elrHole);
  416. REQUIRE(loops[2]->role() == ExtrusionRole::erPerimeter);
  417. }
  418. THEN("contour printed last, external last")
  419. {
  420. //then contour
  421. REQUIRE( (loops[3]->loop_role() & ExtrusionLoopRole::elrHole) == 0);
  422. REQUIRE( loops[3]->role() == ExtrusionRole::erPerimeter);
  423. REQUIRE( (loops[5]->loop_role() & ExtrusionLoopRole::elrHole) == 0);
  424. REQUIRE( loops[5]->role() == ExtrusionRole::erExternalPerimeter);
  425. }
  426. //TODO: thinwall after evrything
  427. }
  428. GIVEN("both brim")
  429. {
  430. config.set_deserialize("brim_width", "1");
  431. config.set_deserialize("brim_width_interior", "1");
  432. Print print{};
  433. Slic3r::Test::init_print(print, {tm}, model, &config);
  434. print.process();
  435. ExtrusionPrinter printer(/*mult=*/0.000001, /*trunc=*/100, /*json=*/true);
  436. GetAll get_all_visitor;
  437. print.objects()[0]->layers()[0]->regions()[0]->perimeters.visit(get_all_visitor);
  438. auto &loops = get_all_visitor.loops;
  439. REQUIRE(loops.size() == 6);
  440. THEN("contour printed first, external first")
  441. {
  442. //then contour
  443. REQUIRE( (loops[0]->loop_role() & ExtrusionLoopRole::elrHole) == 0);
  444. REQUIRE( loops[0]->role() == ExtrusionRole::erExternalPerimeter);
  445. REQUIRE( (loops[2]->loop_role() & ExtrusionLoopRole::elrHole) == 0);
  446. REQUIRE( loops[2]->role() == ExtrusionRole::erPerimeter);
  447. }
  448. THEN("hole printed last, external first")
  449. {
  450. // first holes
  451. REQUIRE((loops[3]->loop_role() & ExtrusionLoopRole::elrHole) == ExtrusionLoopRole::elrHole);
  452. REQUIRE(loops[3]->role() == ExtrusionRole::erExternalPerimeter);
  453. REQUIRE((loops[5]->loop_role() & ExtrusionLoopRole::elrHole) == ExtrusionLoopRole::elrHole);
  454. REQUIRE(loops[5]->role() == ExtrusionRole::erPerimeter);
  455. }
  456. //TODO: thinwall after evrything
  457. }
  458. }