test_expolygon.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. #include <catch2/catch.hpp>
  2. #include "libslic3r/Point.hpp"
  3. #include "libslic3r/Polygon.hpp"
  4. #include "libslic3r/ExPolygon.hpp"
  5. using namespace Slic3r;
  6. static inline bool points_close(const Point &p1, const Point &p2)
  7. {
  8. return (p1 - p2).cast<double>().norm() < SCALED_EPSILON;
  9. }
  10. static bool polygons_close_permuted(const Polygon &poly1, const Polygon &poly2, const std::vector<int> &permutation2)
  11. {
  12. if (poly1.size() != poly2.size() || poly1.size() != permutation2.size())
  13. return false;
  14. for (size_t i = 0; i < poly1.size(); ++ i)
  15. if (poly1[i] != poly2[permutation2[i]])
  16. return false;
  17. return true;
  18. }
  19. SCENARIO("Basics", "[ExPolygon]") {
  20. GIVEN("ccw_square") {
  21. Polygon ccw_square{ { 100, 100 }, { 200, 100 }, { 200, 200 }, { 100, 200 } };
  22. Polygon cw_hole_in_square{ { 140, 140 }, { 140, 160 }, { 160, 160 }, { 160, 140 } };
  23. ExPolygon expolygon { ccw_square, cw_hole_in_square };
  24. THEN("expolygon is valid") {
  25. REQUIRE(expolygon.is_valid());
  26. }
  27. THEN("expolygon area") {
  28. REQUIRE(expolygon.area() == Approx(100*100-20*20));
  29. }
  30. WHEN("Expolygon scaled") {
  31. ExPolygon expolygon2 = expolygon;
  32. expolygon2.scale(2.5);
  33. REQUIRE(expolygon.contour.size() == expolygon2.contour.size());
  34. REQUIRE(expolygon.holes.size() == 1);
  35. REQUIRE(expolygon2.holes.size() == 1);
  36. for (size_t i = 0; i < expolygon.contour.size(); ++ i)
  37. REQUIRE(points_close(expolygon.contour[i] * 2.5, expolygon2.contour[i]));
  38. for (size_t i = 0; i < expolygon.holes.front().size(); ++ i)
  39. REQUIRE(points_close(expolygon.holes.front()[i] * 2.5, expolygon2.holes.front()[i]));
  40. }
  41. WHEN("Expolygon translated") {
  42. ExPolygon expolygon2 = expolygon;
  43. expolygon2.translate(10, -5);
  44. REQUIRE(expolygon.contour.size() == expolygon2.contour.size());
  45. REQUIRE(expolygon.holes.size() == 1);
  46. REQUIRE(expolygon2.holes.size() == 1);
  47. for (size_t i = 0; i < expolygon.contour.size(); ++ i)
  48. REQUIRE(points_close(expolygon.contour[i] + Point(10, -5), expolygon2.contour[i]));
  49. for (size_t i = 0; i < expolygon.holes.front().size(); ++ i)
  50. REQUIRE(points_close(expolygon.holes.front()[i] + Point(10, -5), expolygon2.holes.front()[i]));
  51. }
  52. WHEN("Expolygon rotated around point") {
  53. ExPolygon expolygon2 = expolygon;
  54. expolygon2.rotate(M_PI / 2, Point(150, 150));
  55. REQUIRE(expolygon.contour.size() == expolygon2.contour.size());
  56. REQUIRE(expolygon.holes.size() == 1);
  57. REQUIRE(expolygon2.holes.size() == 1);
  58. REQUIRE(polygons_close_permuted(expolygon2.contour, expolygon.contour, { 1, 2, 3, 0}));
  59. REQUIRE(polygons_close_permuted(expolygon2.holes.front(), expolygon.holes.front(), { 3, 0, 1, 2}));
  60. }
  61. }
  62. }
  63. #include <sstream>
  64. #include <cereal/cereal.hpp>
  65. #include <cereal/archives/binary.hpp>
  66. #include "libslic3r/ExPolygonSerialize.hpp"
  67. TEST_CASE("Serialization of expolygons", "[ExPolygon, Cereal, serialization]")
  68. {
  69. ExPolygons expolys{{
  70. // expolygon 1 - without holes
  71. {{0,0}, {10,0}, {10,10}, {0,10}}, // contour
  72. // expolygon 2 - with rect 1px hole
  73. {{{0,0}, {10,0}, {10,10}, {0,10}},
  74. {{5, 5}, {6, 5}, {6, 6}, {5, 6}}}
  75. }};
  76. std::stringstream ss; // any stream can be used
  77. {
  78. cereal::BinaryOutputArchive oarchive(ss); // Create an output archive
  79. oarchive(expolys);
  80. } // archive goes out of scope, ensuring all contents are flushed
  81. std::string data = ss.str();
  82. CHECK(!data.empty());
  83. ExPolygons expolys_loaded;
  84. {
  85. cereal::BinaryInputArchive iarchive(ss); // Create an input archive
  86. iarchive(expolys_loaded);
  87. }
  88. CHECK(expolys == expolys_loaded);
  89. }
  90. #include <cereal/archives/json.hpp>
  91. #include <regex>
  92. // It is used to serialize expolygons into 3mf.
  93. TEST_CASE("Serialization of expolygons to string", "[ExPolygon, Cereal, serialization]")
  94. {
  95. ExPolygons expolys{{
  96. // expolygon 1 - without holes
  97. {{0,0}, {10,0}, {10,10}, {0,10}}, // contour
  98. // expolygon 2 - with rect 1px hole
  99. {{{0,0}, {10,0}, {10,10}, {0,10}},
  100. {{5, 5}, {6, 5}, {6, 6}, {5, 6}}}
  101. }};
  102. std::stringstream ss_out; // any stream can be used
  103. {
  104. cereal::JSONOutputArchive oarchive(ss_out); // Create an output archive
  105. oarchive(expolys);
  106. } // archive goes out of scope, ensuring all contents are flushed
  107. //Simplify text representation of expolygons
  108. std::string data = ss_out.str();
  109. // Data contain this JSON string
  110. //{
  111. // "value0": [
  112. // {
  113. // "value0": {
  114. // "value0":
  115. // [{"value0": 0, "value1": 0}, {"value0": 10, "value1": 0}, {"value0": 10, "value1": 10}, {"value0": 0, "value1": 10}]
  116. // },
  117. // "value1": []
  118. // },
  119. // {
  120. // "value0": {
  121. // "value0":
  122. // [{"value0": 0, "value1": 0}, {"value0": 10, "value1": 0}, {"value0": 10, "value1": 10}, {"value0": 0, "value1": 10}]
  123. // },
  124. // "value1": [{
  125. // "value0":
  126. // [{"value0": 5, "value1": 5}, {"value0": 6, "value1": 5}, {"value0": 6, "value1": 6}, {"value0": 5, "value1": 6}]
  127. // }]
  128. // }
  129. // ]
  130. //}
  131. // Change JSON named object to JSON arrays(without name)
  132. // RegEx for wihitespace = "[ \t\r\n\v\f]"
  133. std::regex r("\"value[0-9]+\":|[ \t\r\n\v\f]");
  134. std::string data_short = std::regex_replace(data, r , "");
  135. std::replace(data_short.begin(), data_short.end(), '{', '[');
  136. std::replace(data_short.begin(), data_short.end(), '}', ']');
  137. CHECK(!data_short.empty());
  138. // Cereal acceptable string
  139. // [[[[[[0,0],[10,0],[10,10],[0,10]]],[]],[[[[0,0],[10,0],[10,10],[0,10]]],[[[[5,5],[6,5],[6,6],[5,6]]]]]]]
  140. std::stringstream ss_in(data_short);
  141. ExPolygons expolys_loaded;
  142. {
  143. cereal::JSONInputArchive iarchive(ss_in); // Create an input archive
  144. iarchive(expolys_loaded);
  145. }
  146. CHECK(expolys == expolys_loaded);
  147. }