test_gcode.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. #include <catch2/catch.hpp>
  2. #include <memory>
  3. #include "libslic3r/GCode.hpp"
  4. using namespace Slic3r;
  5. using namespace Slic3r::GCode::Impl;
  6. SCENARIO("Origin manipulation", "[GCode]") {
  7. Slic3r::GCodeGenerator gcodegen;
  8. WHEN("set_origin to (10,0)") {
  9. gcodegen.set_origin(Vec2d(10,0));
  10. REQUIRE(gcodegen.origin() == Vec2d(10, 0));
  11. }
  12. WHEN("set_origin to (10,0) and translate by (5, 5)") {
  13. gcodegen.set_origin(Vec2d(10,0));
  14. gcodegen.set_origin(gcodegen.origin() + Vec2d(5, 5));
  15. THEN("origin returns reference to point") {
  16. REQUIRE(gcodegen.origin() == Vec2d(15,5));
  17. }
  18. }
  19. }
  20. struct ApproxEqualsPoints : public Catch::MatcherBase<Points> {
  21. ApproxEqualsPoints(const Points& expected, unsigned tolerance): expected(expected), tolerance(tolerance) {}
  22. bool match(const Points& points) const override {
  23. if (points.size() != expected.size()) {
  24. return false;
  25. }
  26. for (auto i = 0u; i < points.size(); ++i) {
  27. const Point& point = points[i];
  28. const Point& expected_point = this->expected[i];
  29. if (
  30. std::abs(point.x() - expected_point.x()) > this->tolerance
  31. || std::abs(point.y() - expected_point.y()) > this->tolerance
  32. ) {
  33. return false;
  34. }
  35. }
  36. return true;
  37. }
  38. std::string describe() const override {
  39. std::stringstream ss;
  40. ss << std::endl;
  41. for (const Point& point : expected) {
  42. ss << "(" << point.x() << ", " << point.y() << ")" << std::endl;
  43. }
  44. ss << "With tolerance: " << this->tolerance;
  45. return "Equals " + ss.str();
  46. }
  47. private:
  48. Points expected;
  49. unsigned tolerance;
  50. };
  51. Points get_points(const std::vector<DistancedPoint>& result) {
  52. Points result_points;
  53. std::transform(
  54. result.begin(),
  55. result.end(),
  56. std::back_inserter(result_points),
  57. [](const DistancedPoint& point){
  58. return point.point;
  59. }
  60. );
  61. return result_points;
  62. }
  63. std::vector<double> get_distances(const std::vector<DistancedPoint>& result) {
  64. std::vector<double> result_distances;
  65. std::transform(
  66. result.begin(),
  67. result.end(),
  68. std::back_inserter(result_distances),
  69. [](const DistancedPoint& point){
  70. return point.distance_from_start;
  71. }
  72. );
  73. return result_distances;
  74. }
  75. TEST_CASE("Place points at distances - expected use", "[GCode]") {
  76. std::vector<Point> line{
  77. scaled(Vec2f{0, 0}),
  78. scaled(Vec2f{1, 0}),
  79. scaled(Vec2f{2, 1}),
  80. scaled(Vec2f{2, 2})
  81. };
  82. std::vector<double> distances{0, 0.2, 0.5, 1 + std::sqrt(2)/2, 1 + std::sqrt(2) + 0.5, 100.0};
  83. std::vector<DistancedPoint> result = slice_xy_path(line, distances);
  84. REQUIRE_THAT(get_points(result), ApproxEqualsPoints(Points{
  85. scaled(Vec2f{0, 0}),
  86. scaled(Vec2f{0.2, 0}),
  87. scaled(Vec2f{0.5, 0}),
  88. scaled(Vec2f{1, 0}),
  89. scaled(Vec2f{1.5, 0.5}),
  90. scaled(Vec2f{2, 1}),
  91. scaled(Vec2f{2, 1.5}),
  92. scaled(Vec2f{2, 2})
  93. }, 5));
  94. REQUIRE_THAT(get_distances(result), Catch::Matchers::Approx(std::vector<double>{
  95. distances[0], distances[1], distances[2], 1, distances[3], 1 + std::sqrt(2), distances[4], 2 + std::sqrt(2)
  96. }));
  97. }
  98. TEST_CASE("Place points at distances - edge case", "[GCode]") {
  99. std::vector<Point> line{
  100. scaled(Vec2f{0, 0}),
  101. scaled(Vec2f{1, 0}),
  102. scaled(Vec2f{2, 0})
  103. };
  104. std::vector<double> distances{0, 1, 1.5, 2};
  105. Points result{get_points(slice_xy_path(line, distances))};
  106. CHECK(result == Points{
  107. scaled(Vec2f{0, 0}),
  108. scaled(Vec2f{1, 0}),
  109. scaled(Vec2f{1.5, 0}),
  110. scaled(Vec2f{2, 0})
  111. });
  112. }
  113. TEST_CASE("Generate elevated travel", "[GCode]") {
  114. std::vector<Point> xy_path{
  115. scaled(Vec2f{0, 0}),
  116. scaled(Vec2f{1, 0}),
  117. };
  118. std::vector<double> ensure_points_at_distances{0.2, 0.5};
  119. Points3 result{generate_elevated_travel(xy_path, ensure_points_at_distances, 2.0, [](double x){return 1 + x;})};
  120. CHECK(result == Points3{
  121. scaled(Vec3f{0, 0, 3.0}),
  122. scaled(Vec3f{0.2, 0, 3.2}),
  123. scaled(Vec3f{0.5, 0, 3.5}),
  124. scaled(Vec3f{1, 0, 4.0})
  125. });
  126. }
  127. TEST_CASE("Get first crossed line distance", "[GCode]") {
  128. // A 2x2 square at 0, 0, with 1x1 square hole in its center.
  129. ExPolygon square_with_hole{
  130. {
  131. scaled(Vec2f{-1, -1}),
  132. scaled(Vec2f{1, -1}),
  133. scaled(Vec2f{1, 1}),
  134. scaled(Vec2f{-1, 1})
  135. },
  136. {
  137. scaled(Vec2f{-0.5, -0.5}),
  138. scaled(Vec2f{0.5, -0.5}),
  139. scaled(Vec2f{0.5, 0.5}),
  140. scaled(Vec2f{-0.5, 0.5})
  141. }
  142. };
  143. // A 2x2 square above the previous square at (0, 3).
  144. ExPolygon square_above{
  145. {
  146. scaled(Vec2f{-1, 2}),
  147. scaled(Vec2f{1, 2}),
  148. scaled(Vec2f{1, 4}),
  149. scaled(Vec2f{-1, 4})
  150. }
  151. };
  152. // Bottom-up travel intersecting the squares.
  153. Lines travel{Polyline{
  154. scaled(Vec2f{0, -2}),
  155. scaled(Vec2f{0, -0.7}),
  156. scaled(Vec2f{0, 0}),
  157. scaled(Vec2f{0, 1}),
  158. scaled(Vec2f{0, 1.3}),
  159. scaled(Vec2f{0, 2.4}),
  160. scaled(Vec2f{0, 4.5}),
  161. scaled(Vec2f{0, 5}),
  162. }.lines()};
  163. // Try different cases by skipping lines in the travel.
  164. AABBTreeLines::LinesDistancer<Linef> distancer = get_expolygons_distancer({square_with_hole, square_above});
  165. CHECK(*get_first_crossed_line_distance(travel, distancer) == Approx(1));
  166. CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(1), distancer) == Approx(0.2));
  167. CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(2), distancer) == Approx(0.5));
  168. CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(3), distancer) == Approx(1.0)); //Edge case
  169. CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(4), distancer) == Approx(0.7));
  170. CHECK(*get_first_crossed_line_distance(tcb::span{travel}.subspan(5), distancer) == Approx(1.6));
  171. CHECK_FALSE(get_first_crossed_line_distance(tcb::span{travel}.subspan(6), distancer));
  172. }
  173. TEST_CASE("Generate regular polygon", "[GCode]") {
  174. const unsigned points_count{32};
  175. const Point centroid{scaled(Vec2d{5, -2})};
  176. const Polygon result{generate_regular_polygon(centroid, scaled(Vec2d{0, 0}), points_count)};
  177. const Point oposite_point{centroid * 2};
  178. REQUIRE(result.size() == 32);
  179. CHECK(result[16].x() == Approx(oposite_point.x()));
  180. CHECK(result[16].y() == Approx(oposite_point.y()));
  181. std::vector<double> angles;
  182. angles.reserve(points_count);
  183. for (unsigned index = 0; index < points_count; index++) {
  184. const unsigned previous_index{index == 0 ? points_count - 1 : index - 1};
  185. const unsigned next_index{index == points_count - 1 ? 0 : index + 1};
  186. const Point previous_point = result.points[previous_index];
  187. const Point current_point = result.points[index];
  188. const Point next_point = result.points[next_index];
  189. angles.emplace_back(angle(Vec2crd{previous_point - current_point}, Vec2crd{next_point - current_point}));
  190. }
  191. std::vector<double> expected;
  192. angles.reserve(points_count);
  193. std::generate_n(std::back_inserter(expected), points_count, [&](){
  194. return angles.front();
  195. });
  196. CHECK_THAT(angles, Catch::Matchers::Approx(expected));
  197. }
  198. TEST_CASE("Square bed with padding", "[GCode]") {
  199. const Bed bed{
  200. {
  201. Vec2d{0, 0},
  202. Vec2d{100, 0},
  203. Vec2d{100, 100},
  204. Vec2d{0, 100}
  205. },
  206. 10.0
  207. };
  208. CHECK(bed.centroid.x() == 50);
  209. CHECK(bed.centroid.y() == 50);
  210. CHECK(bed.contains_within_padding(Vec2d{10, 10}));
  211. CHECK_FALSE(bed.contains_within_padding(Vec2d{9, 10}));
  212. }