test_clipper.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. #include <catch2/catch.hpp>
  2. #include "test_data.hpp"
  3. #include "libslic3r/ClipperZUtils.hpp"
  4. #include "libslic3r/clipper.hpp"
  5. using namespace Slic3r;
  6. // tests for ExPolygon::overlaps(const ExPolygon &other)
  7. SCENARIO("Clipper intersection with polyline", "[Clipper]")
  8. {
  9. struct TestData {
  10. ClipperLib::Path subject;
  11. ClipperLib::Path clip;
  12. ClipperLib::Paths result;
  13. };
  14. auto run_test = [](const TestData &data) {
  15. ClipperLib::Clipper clipper;
  16. clipper.AddPath(data.subject, ClipperLib::ptSubject, false);
  17. clipper.AddPath(data.clip, ClipperLib::ptClip, true);
  18. ClipperLib::PolyTree polytree;
  19. ClipperLib::Paths paths;
  20. clipper.Execute(ClipperLib::ctIntersection, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
  21. ClipperLib::PolyTreeToPaths(polytree, paths);
  22. REQUIRE(paths == data.result);
  23. };
  24. WHEN("Open polyline completely inside stays inside") {
  25. run_test({
  26. { { 10, 0 }, { 20, 0 } },
  27. { { -1000, -1000 }, { -1000, 1000 }, { 1000, 1000 }, { 1000, -1000 } },
  28. { { { 20, 0 }, { 10, 0 } } }
  29. });
  30. };
  31. WHEN("Closed polyline completely inside stays inside") {
  32. run_test({
  33. { { 10, 0 }, { 20, 0 }, { 20, 20 }, { 10, 20 }, { 10, 0 } },
  34. { { -1000, -1000 }, { -1000, 1000 }, { 1000, 1000 }, { 1000, -1000 } },
  35. { { { 10, 0 }, { 20, 0 }, { 20, 20 }, { 10, 20 }, { 10, 0 } } }
  36. });
  37. };
  38. WHEN("Polyline which crosses right rectangle boundary is trimmed") {
  39. run_test({
  40. { { 10, 0 }, { 2000, 0 } },
  41. { { -1000, -1000 }, { -1000, 1000 }, { 1000, 1000 }, { 1000, -1000 } },
  42. { { { 1000, 0 }, { 10, 0 } } }
  43. });
  44. };
  45. WHEN("Polyline which is outside clipping region is removed") {
  46. run_test({
  47. { { 1500, 0 }, { 2000, 0 } },
  48. { { -1000, -1000 }, { -1000, 1000 }, { 1000, 1000 }, { 1000, -1000 } },
  49. { }
  50. });
  51. };
  52. WHEN("Polyline on left vertical boundary is kept") {
  53. run_test({
  54. { { -1000, -1000 }, { -1000, 1000 } },
  55. { { -1000, -1000 }, { -1000, 1000 }, { 1000, 1000 }, { 1000, -1000 } },
  56. { { { -1000, -1000 }, { -1000, 1000 } } }
  57. });
  58. run_test({
  59. { { -1000, 1000 }, { -1000, -1000 } },
  60. { { -1000, -1000 }, { -1000, 1000 }, { 1000, 1000 }, { 1000, -1000 } },
  61. { { { -1000, 1000 }, { -1000, -1000 } } }
  62. });
  63. };
  64. WHEN("Polyline on right vertical boundary is kept") {
  65. run_test({
  66. { { 1000, -1000 }, { 1000, 1000 } },
  67. { { -1000, -1000 }, { -1000, 1000 }, { 1000, 1000 }, { 1000, -1000 } },
  68. { { { 1000, -1000 }, { 1000, 1000 } } }
  69. });
  70. run_test({
  71. { { 1000, 1000 }, { 1000, -1000 } },
  72. { { -1000, -1000 }, { -1000, 1000 }, { 1000, 1000 }, { 1000, -1000 } },
  73. { { { 1000, 1000 }, { 1000, -1000 } } }
  74. });
  75. };
  76. WHEN("Polyline on bottom horizontal boundary is removed") {
  77. run_test({
  78. { { -1000, -1000 }, { 1000, -1000 } },
  79. { { -1000, -1000 }, { -1000, 1000 }, { 1000, 1000 }, { 1000, -1000 } },
  80. { }
  81. });
  82. run_test({
  83. { { 1000, -1000 }, { -1000, -1000 } },
  84. { { -1000, -1000 }, { -1000, 1000 }, { 1000, 1000 }, { 1000, -1000 } },
  85. { }
  86. });
  87. };
  88. WHEN("Polyline on top horizontal boundary is removed") {
  89. run_test({
  90. { { -1000, 1000 }, { 1000, 1000 } },
  91. { { -1000, -1000 }, { -1000, 1000 }, { 1000, 1000 }, { 1000, -1000 } },
  92. { }
  93. });
  94. run_test({
  95. { { 1000, 1000 }, { -1000, 1000 } },
  96. { { -1000, -1000 }, { -1000, 1000 }, { 1000, 1000 }, { 1000, -1000 } },
  97. { }
  98. });
  99. };
  100. }
  101. SCENARIO("Clipper Z", "[ClipperZ]")
  102. {
  103. ClipperLib_Z::Path subject { { -2000, -1000, 10 }, { -2000, 1000, 10 }, { 2000, 1000, 10 }, { 2000, -1000, 10 } };
  104. ClipperLib_Z::Path clip{ { -1000, -2000, -5 }, { -1000, 2000, -5 }, { 1000, 2000, -5 }, { 1000, -2000, -5 } };
  105. ClipperLib_Z::Clipper clipper;
  106. clipper.ZFillFunction([](const ClipperLib_Z::IntPoint &e1bot, const ClipperLib_Z::IntPoint &e1top, const ClipperLib_Z::IntPoint &e2bot,
  107. const ClipperLib_Z::IntPoint &e2top, ClipperLib_Z::IntPoint &pt) {
  108. pt.z() = 1;
  109. });
  110. clipper.AddPath(subject, ClipperLib_Z::ptSubject, false);
  111. clipper.AddPath(clip, ClipperLib_Z::ptClip, true);
  112. ClipperLib_Z::PolyTree polytree;
  113. ClipperLib_Z::Paths paths;
  114. clipper.Execute(ClipperLib_Z::ctIntersection, polytree, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero);
  115. ClipperLib_Z::PolyTreeToPaths(polytree, paths);
  116. REQUIRE(paths.size() == 1);
  117. REQUIRE(paths.front().size() == 2);
  118. for (const ClipperLib_Z::IntPoint &pt : paths.front())
  119. REQUIRE(pt.z() == 1);
  120. }
  121. SCENARIO("Intersection with multiple polylines", "[ClipperZ]")
  122. {
  123. // 1000x1000 CCQ square
  124. ClipperLib_Z::Path clip { { 0, 0, 1 }, { 1000, 0, 1 }, { 1000, 1000, 1 }, { 0, 1000, 1 } };
  125. // Two lines interseting inside the square above, crossing the bottom edge of the square.
  126. ClipperLib_Z::Path line1 { { +100, -100, 2 }, { +900, +900, 2 } };
  127. ClipperLib_Z::Path line2 { { +100, +900, 3 }, { +900, -100, 3 } };
  128. ClipperLib_Z::Clipper clipper;
  129. ClipperZUtils::ClipperZIntersectionVisitor::Intersections intersections;
  130. ClipperZUtils::ClipperZIntersectionVisitor visitor(intersections);
  131. clipper.ZFillFunction(visitor.clipper_callback());
  132. clipper.AddPath(line1, ClipperLib_Z::ptSubject, false);
  133. clipper.AddPath(line2, ClipperLib_Z::ptSubject, false);
  134. clipper.AddPath(clip, ClipperLib_Z::ptClip, true);
  135. ClipperLib_Z::PolyTree polytree;
  136. ClipperLib_Z::Paths paths;
  137. clipper.Execute(ClipperLib_Z::ctIntersection, polytree, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero);
  138. ClipperLib_Z::PolyTreeToPaths(polytree, paths);
  139. REQUIRE(paths.size() == 2);
  140. THEN("First output polyline is a trimmed 2nd line") {
  141. // Intermediate point (intersection) was removed)
  142. REQUIRE(paths.front().size() == 2);
  143. REQUIRE(paths.front().front().z() == 3);
  144. REQUIRE(paths.front().back().z() < 0);
  145. REQUIRE(intersections[- paths.front().back().z() - 1] == std::pair<coord_t, coord_t>(1, 3));
  146. }
  147. THEN("Second output polyline is a trimmed 1st line") {
  148. // Intermediate point (intersection) was removed)
  149. REQUIRE(paths[1].size() == 2);
  150. REQUIRE(paths[1].front().z() < 0);
  151. REQUIRE(paths[1].back().z() == 2);
  152. REQUIRE(intersections[- paths[1].front().z() - 1] == std::pair<coord_t, coord_t>(1, 2));
  153. }
  154. }
  155. SCENARIO("Interseting a closed loop as an open polyline", "[ClipperZ]")
  156. {
  157. // 1000x1000 CCQ square
  158. ClipperLib_Z::Path clip{ { 0, 0, 1 }, { 1000, 0, 1 }, { 1000, 1000, 1 }, { 0, 1000, 1 } };
  159. // Two lines interseting inside the square above, crossing the bottom edge of the square.
  160. ClipperLib_Z::Path rect{ { 500, 500, 2}, { 500, 1500, 2 }, { 1500, 1500, 2}, { 500, 1500, 2}, { 500, 500, 2 } };
  161. ClipperLib_Z::Clipper clipper;
  162. clipper.AddPath(rect, ClipperLib_Z::ptSubject, false);
  163. clipper.AddPath(clip, ClipperLib_Z::ptClip, true);
  164. ClipperLib_Z::PolyTree polytree;
  165. ClipperLib_Z::Paths paths;
  166. ClipperZUtils::ClipperZIntersectionVisitor::Intersections intersections;
  167. ClipperZUtils::ClipperZIntersectionVisitor visitor(intersections);
  168. clipper.ZFillFunction(visitor.clipper_callback());
  169. clipper.Execute(ClipperLib_Z::ctIntersection, polytree, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero);
  170. ClipperLib_Z::PolyTreeToPaths(std::move(polytree), paths);
  171. THEN("Open polyline is clipped into two pieces") {
  172. REQUIRE(paths.size() == 2);
  173. REQUIRE(paths.front().size() == 2);
  174. REQUIRE(paths.back().size() == 2);
  175. REQUIRE(paths.front().front().z() == 2);
  176. REQUIRE(paths.back().back().z() == 2);
  177. REQUIRE(paths.front().front().x() == paths.back().back().x());
  178. REQUIRE(paths.front().front().y() == paths.back().back().y());
  179. }
  180. }