test_geometry.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. #include <catch_main.hpp>
  2. #include <libslic3r/Point.hpp>
  3. #include <libslic3r/BoundingBox.hpp>
  4. #include <libslic3r/Polygon.hpp>
  5. #include <libslic3r/Polyline.hpp>
  6. #include <libslic3r/Line.hpp>
  7. #include <libslic3r/Geometry.hpp>
  8. #include <libslic3r/ClipperUtils.hpp>
  9. using namespace Slic3r;
  10. TEST_CASE("Polygon::contains works properly", ""){
  11. // this test was failing on Windows (GH #1950)
  12. Slic3r::Polygon polygon( Points{
  13. Point{207802834,-57084522},
  14. Point{196528149,-37556190},
  15. Point{173626821,-25420928},
  16. Point{171285751,-21366123},
  17. Point{118673592,-21366123},
  18. Point{116332562,-25420928},
  19. Point{93431208,-37556191},
  20. Point{82156517,-57084523},
  21. Point{129714478,-84542120},
  22. Point{160244873,-84542120}
  23. } );
  24. Point point{ 95706562, -57294774 };
  25. REQUIRE(polygon.contains(point));
  26. }
  27. SCENARIO("Intersections of line segments"){
  28. GIVEN("Integer coordinates"){
  29. Line line1{ Point{5,15},Point{30,15} };
  30. Line line2{ Point{10,20}, Point{10,10} };
  31. THEN("The intersection is valid"){
  32. Point point;
  33. line1.intersection(line2,&point);
  34. REQUIRE(Point{ 10,15 } == point);
  35. }
  36. }
  37. GIVEN("Scaled coordinates"){
  38. Line line1{ Point::new_scale(73.6310778185108, 371.74239268924), Point::new_scale(73.6310778185108, 501.74239268924) };
  39. Line line2{ Point::new_scale(75, 437.9853), Point::new_scale(62.7484, 440.4223) };
  40. THEN("There is still an intersection"){
  41. Point point;
  42. REQUIRE(line1.intersection(line2,&point));
  43. }
  44. }
  45. }
  46. /*
  47. Tests for unused methods still written in perl
  48. {
  49. my $polygon = Slic3r::Polygon->new(
  50. [45919000, 515273900], [14726100, 461246400], [14726100, 348753500], [33988700, 315389800],
  51. [43749700, 343843000], [45422300, 352251500], [52362100, 362637800], [62748400, 369577600],
  52. [75000000, 372014700], [87251500, 369577600], [97637800, 362637800], [104577600, 352251500],
  53. [107014700, 340000000], [104577600, 327748400], [97637800, 317362100], [87251500, 310422300],
  54. [82789200, 309534700], [69846100, 294726100], [254081000, 294726100], [285273900, 348753500],
  55. [285273900, 461246400], [254081000, 515273900],
  56. );
  57. # this points belongs to $polyline
  58. # note: it's actually a vertex, while we should better check an intermediate point
  59. my $point = Slic3r::Point->new(104577600, 327748400);
  60. local $Slic3r::Geometry::epsilon = 1E-5;
  61. is_deeply Slic3r::Geometry::polygon_segment_having_point($polygon, $point)->pp,
  62. [ [107014700, 340000000], [104577600, 327748400] ],
  63. 'polygon_segment_having_point';
  64. }
  65. {
  66. auto point = Point{736310778.185108, 5017423926.8924};
  67. auto line = Line(Point{(long int} 627484000, (long int) 3695776000), Point{(long int} 750000000, (long int)3720147000));
  68. //is Slic3r::Geometry::point_in_segment($point, $line), 0, 'point_in_segment';
  69. }
  70. // Possible to delete
  71. {
  72. //my $p1 = [10, 10];
  73. //my $p2 = [10, 20];
  74. //my $p3 = [10, 30];
  75. //my $p4 = [20, 20];
  76. //my $p5 = [0, 20];
  77. THEN("Points in a line give the correct angles"){
  78. //is Slic3r::Geometry::angle3points($p2, $p3, $p1), PI(), 'angle3points';
  79. //is Slic3r::Geometry::angle3points($p2, $p1, $p3), PI(), 'angle3points';
  80. }
  81. THEN("Left turns give the correct angle"){
  82. //is Slic3r::Geometry::angle3points($p2, $p4, $p3), PI()/2, 'angle3points';
  83. //is Slic3r::Geometry::angle3points($p2, $p1, $p4), PI()/2, 'angle3points';
  84. }
  85. THEN("Right turns give the correct angle"){
  86. //is Slic3r::Geometry::angle3points($p2, $p3, $p4), PI()/2*3, 'angle3points';
  87. //is Slic3r::Geometry::angle3points($p2, $p1, $p5), PI()/2*3, 'angle3points';
  88. }
  89. //my $p1 = [30, 30];
  90. //my $p2 = [20, 20];
  91. //my $p3 = [10, 10];
  92. //my $p4 = [30, 10];
  93. //is Slic3r::Geometry::angle3points($p2, $p1, $p3), PI(), 'angle3points';
  94. //is Slic3r::Geometry::angle3points($p2, $p1, $p4), PI()/2*3, 'angle3points';
  95. //is Slic3r::Geometry::angle3points($p2, $p1, $p1), 2*PI(), 'angle3points';
  96. }
  97. SCENARIO("polygon_is_convex works"){
  98. GIVEN("A square of dimension 10"){
  99. //my $cw_square = [ [0,0], [0,10], [10,10], [10,0] ];
  100. THEN("It is not convex clockwise"){
  101. //is polygon_is_convex($cw_square), 0, 'cw square is not convex';
  102. }
  103. THEN("It is convex counter-clockwise"){
  104. //is polygon_is_convex([ reverse @$cw_square ]), 1, 'ccw square is convex';
  105. }
  106. }
  107. GIVEN("A concave polygon"){
  108. //my $convex1 = [ [0,0], [10,0], [10,10], [0,10], [0,6], [4,6], [4,4], [0,4] ];
  109. THEN("It is concave"){
  110. //is polygon_is_convex($convex1), 0, 'concave polygon';
  111. }
  112. }
  113. }*/
  114. TEST_CASE("Creating a polyline generates the obvious lines"){
  115. auto polyline = Slic3r::Polyline();
  116. polyline.points = std::vector<Point>({Point{0, 0}, Point{10, 0}, Point{20, 0}});
  117. REQUIRE(polyline.lines().at(0).a == Point{0,0});
  118. REQUIRE(polyline.lines().at(0).b == Point{10,0});
  119. REQUIRE(polyline.lines().at(1).a == Point{10,0});
  120. REQUIRE(polyline.lines().at(1).b == Point{20,0});
  121. }
  122. TEST_CASE("Splitting a Polygon generates a polyline correctly"){
  123. auto polygon = Slic3r::Polygon(std::vector<Point>({Point{0, 0}, Point{10, 0}, Point{5, 5}}));
  124. auto split = polygon.split_at_index(1);
  125. REQUIRE(split.points[0]==Point{10,0});
  126. REQUIRE(split.points[1]==Point{5,5});
  127. REQUIRE(split.points[2]==Point{0,0});
  128. REQUIRE(split.points[3]==Point{10,0});
  129. }
  130. TEST_CASE("Bounding boxes are scaled appropriately"){
  131. auto bb = BoundingBox(std::vector<Point>({Point{0, 1}, Point{10, 2}, Point{20, 2}}));
  132. bb.scale(2);
  133. REQUIRE(bb.min == Point{0,2});
  134. REQUIRE(bb.max == Point{40,4});
  135. }
  136. TEST_CASE("Offseting a line generates a polygon correctly"){
  137. Slic3r::Polyline tmp(Points{{10,10},{20,10} });
  138. Slic3r::Polygon area = offset(tmp,5).at(0);
  139. REQUIRE(area.area() == Slic3r::Polygon(std::vector<Point>({Point{10,5},Point{20,5},Point{20,15},Point{10,15}})).area());
  140. }
  141. SCENARIO("Circle Fit, TaubinFit with Newton's method") {
  142. GIVEN("A vector of Pointfs arranged in a half-circle with approximately the same distance R from some point") {
  143. Vec2d expected_center(-6, 0);
  144. Pointfs sample {Vec2d{6.0, 0}, Vec2d{5.1961524, 3}, Vec2d{3 ,5.1961524}, Vec2d{0, 6.0}, Vec2d{-3, 5.1961524}, Vec2d{-5.1961524, 3}, Vec2d{-6.0, 0}};
  145. std::transform(sample.begin(), sample.end(), sample.begin(), [expected_center] (const Vec2d& a) { return a + expected_center;});
  146. WHEN("Circle fit is called on the entire array") {
  147. Vec2d result_center(0,0);
  148. result_center = Geometry::circle_center_taubin_newton(sample);
  149. THEN("A center point of -6,0 is returned.") {
  150. REQUIRE((result_center - expected_center).norm() < EPSILON);
  151. }
  152. }
  153. WHEN("Circle fit is called on the first four points") {
  154. Vec2d result_center(0,0);
  155. result_center = Geometry::circle_center_taubin_newton(sample.cbegin(), sample.cbegin()+4);
  156. THEN("A center point of -6,0 is returned.") {
  157. REQUIRE((result_center - expected_center).norm() < EPSILON);
  158. }
  159. }
  160. WHEN("Circle fit is called on the middle four points") {
  161. Vec2d result_center(0,0);
  162. result_center = Geometry::circle_center_taubin_newton(sample.cbegin()+2, sample.cbegin()+6);
  163. THEN("A center point of -6,0 is returned.") {
  164. REQUIRE((result_center - expected_center).norm() < EPSILON);
  165. }
  166. }
  167. }
  168. GIVEN("A vector of Pointfs arranged in a half-circle with approximately the same distance R from some point") {
  169. Vec2d expected_center(-3, 9);
  170. Vec2ds sample {Vec2d{6.0, 0}, Vec2d{5.1961524, 3}, Vec2d{3 ,5.1961524},
  171. Vec2d{0, 6.0},
  172. Vec2d{3, 5.1961524}, Vec2d{-5.1961524, 3}, Vec2d{-6.0, 0}};
  173. std::transform(sample.begin(), sample.end(), sample.begin(), [expected_center] (const Vec2d& a) { return a + expected_center;});
  174. WHEN("Circle fit is called on the entire array") {
  175. Vec2d result_center(0,0);
  176. result_center = Geometry::circle_center_taubin_newton(sample);
  177. THEN("A center point of 3,9 is returned.") {
  178. REQUIRE((result_center - expected_center).norm() < EPSILON);
  179. }
  180. }
  181. WHEN("Circle fit is called on the first four points") {
  182. Vec2d result_center(0,0);
  183. result_center = Geometry::circle_center_taubin_newton(sample.cbegin(), sample.cbegin()+4);
  184. THEN("A center point of 3,9 is returned.") {
  185. REQUIRE((result_center - expected_center).norm() < EPSILON);
  186. }
  187. }
  188. WHEN("Circle fit is called on the middle four points") {
  189. Vec2d result_center(0,0);
  190. result_center = Geometry::circle_center_taubin_newton(sample.cbegin()+2, sample.cbegin()+6);
  191. THEN("A center point of 3,9 is returned.") {
  192. REQUIRE((result_center - expected_center).norm() < EPSILON);
  193. }
  194. }
  195. }
  196. GIVEN("A vector of Points arranged in a half-circle with approximately the same distance R from some point") {
  197. Point expected_center { Point::new_scale(-3, 9)};
  198. Points sample {Point::new_scale(6.0, 0), Point::new_scale(5.1961524, 3), Point::new_scale(3 ,5.1961524),
  199. Point::new_scale(0, 6.0),
  200. Point::new_scale(3, 5.1961524), Point::new_scale(-5.1961524, 3), Point::new_scale(-6.0, 0)};
  201. std::transform(sample.begin(), sample.end(), sample.begin(), [expected_center] (const Point& a) { return a + expected_center;});
  202. WHEN("Circle fit is called on the entire array") {
  203. Point result_center(0,0);
  204. result_center = Geometry::circle_center_taubin_newton(sample);
  205. THEN("A center point of scaled 3,9 is returned.") {
  206. REQUIRE(result_center.coincides_with_epsilon(expected_center));
  207. }
  208. }
  209. WHEN("Circle fit is called on the first four points") {
  210. Point result_center(0,0);
  211. result_center = Geometry::circle_center_taubin_newton(sample.cbegin(), sample.cbegin()+4);
  212. THEN("A center point of scaled 3,9 is returned.") {
  213. REQUIRE(result_center.coincides_with_epsilon(expected_center));
  214. }
  215. }
  216. WHEN("Circle fit is called on the middle four points") {
  217. Point result_center(0,0);
  218. result_center = Geometry::circle_center_taubin_newton(sample.cbegin()+2, sample.cbegin()+6);
  219. THEN("A center point of scaled 3,9 is returned.") {
  220. REQUIRE(result_center.coincides_with_epsilon(expected_center));
  221. }
  222. }
  223. }
  224. }
  225. // A PU
  226. //TEST_CASE("Chained path working correctly"){
  227. // // if chained_path() works correctly, these points should be joined with no diagonal paths
  228. // // (thus 26 units long)
  229. // std::vector<Point> points = {Point{26,26},Point{52,26},Point{0,26},Point{26,52},Point{26,0},Point{0,52},Point{52,52},Point{52,0}};
  230. // std::vector<Points::size_type> indices;
  231. // Geometry::chained_path(points,indices);
  232. // for(Points::size_type i = 0; i < indices.size()-1;i++){
  233. // double dist = points.at(indices.at(i)).distance_to(points.at(indices.at(i+1)));
  234. // REQUIRE(abs(dist-26) <= EPSILON);
  235. // }
  236. //}
  237. SCENARIO("Line distances"){
  238. GIVEN("A line"){
  239. Line line{ Point{0, 0}, Point{20, 0} };
  240. THEN("Points on the line segment have 0 distance"){
  241. REQUIRE(Point{0, 0}.distance_to(line) == 0);
  242. REQUIRE(Point{20, 0}.distance_to(line) == 0);
  243. REQUIRE(Point{10, 0}.distance_to(line) == 0);
  244. }
  245. THEN("Points off the line have the appropriate distance"){
  246. REQUIRE(Point{10, 10}.distance_to(line) == 10);
  247. REQUIRE(Point{50, 0}.distance_to(line) == 30);
  248. }
  249. }
  250. }
  251. SCENARIO("Polygon convex/concave detection"){
  252. GIVEN(("A Square with dimension 100")){
  253. Slic3r::Polygon square/*new_scale*/{ std::vector<Point>{
  254. Point{100,100},
  255. Point{200,100},
  256. Point{200,200},
  257. Point{100,200}}};
  258. THEN("It has 4 convex points counterclockwise"){
  259. REQUIRE(square.concave_points(PI*4/3).size() == 0);
  260. REQUIRE(square.convex_points(PI*2/3).size() == 4);
  261. }
  262. THEN("It has 4 concave points clockwise"){
  263. square.make_clockwise();
  264. REQUIRE(square.concave_points(PI*4/3).size() == 4);
  265. REQUIRE(square.convex_points(PI*2/3).size() == 0);
  266. }
  267. }
  268. GIVEN("A Square with an extra colinearvertex"){
  269. Slic3r::Polygon square /*new_scale*/{ std::vector<Point>{
  270. Point{150,100},
  271. Point{200,100},
  272. Point{200,200},
  273. Point{100,200},
  274. Point{100,100}} };
  275. THEN("It has 4 convex points counterclockwise"){
  276. REQUIRE(square.concave_points(PI*4/3).size() == 0);
  277. REQUIRE(square.convex_points(PI*2/3).size() == 4);
  278. }
  279. }
  280. GIVEN("A Square with an extra collinear vertex in different order"){
  281. Slic3r::Polygon square /*new_scale*/{ std::vector<Point>{
  282. Point{200,200},
  283. Point{100,200},
  284. Point{100,100},
  285. Point{150,100},
  286. Point{200,100}} };
  287. THEN("It has 4 convex points counterclockwise"){
  288. REQUIRE(square.concave_points(PI*4/3).size() == 0);
  289. REQUIRE(square.convex_points(PI*2/3).size() == 4);
  290. }
  291. }
  292. GIVEN("A triangle"){
  293. Slic3r::Polygon triangle{ std::vector<Point>{
  294. Point{16000170,26257364},
  295. Point{714223,461012},
  296. Point{31286371,461008}
  297. } };
  298. THEN("it has three convex vertices"){
  299. REQUIRE(triangle.concave_points(PI*4/3).size() == 0);
  300. REQUIRE(triangle.convex_points(PI*2/3).size() == 3);
  301. }
  302. }
  303. GIVEN("A triangle with an extra collinear point"){
  304. Slic3r::Polygon triangle{ std::vector<Point>{
  305. Point{16000170,26257364},
  306. Point{714223,461012},
  307. Point{20000000,461012},
  308. Point{31286371,461012}
  309. } };
  310. THEN("it has three convex vertices"){
  311. REQUIRE(triangle.concave_points(PI*4/3).size() == 0);
  312. REQUIRE(triangle.convex_points(PI*2/3).size() == 3);
  313. }
  314. }
  315. GIVEN("A polygon with concave vertices with angles of specifically 4/3pi"){
  316. // Two concave vertices of this polygon have angle = PI*4/3, so this test fails
  317. // if epsilon is not used.
  318. Slic3r::Polygon polygon{ std::vector<Point>{
  319. Point{60246458,14802768},Point{64477191,12360001},
  320. Point{63727343,11060995},Point{64086449,10853608},
  321. Point{66393722,14850069},Point{66034704,15057334},
  322. Point{65284646,13758387},Point{61053864,16200839},
  323. Point{69200258,30310849},Point{62172547,42483120},
  324. Point{61137680,41850279},Point{67799985,30310848},
  325. Point{51399866,1905506},Point{38092663,1905506},
  326. Point{38092663,692699},Point{52100125,692699}
  327. } };
  328. THEN("the correct number of points are detected"){
  329. REQUIRE(polygon.concave_points(PI*4/3).size() == 6);
  330. REQUIRE(polygon.convex_points(PI*2/3).size() == 10);
  331. }
  332. }
  333. }
  334. TEST_CASE("Triangle Simplification does not result in less than 3 points"){
  335. Slic3r::Polygon triangle{ std::vector<Point>{
  336. Point{16000170,26257364}, Point{714223,461012}, Point{31286371,461008}
  337. } };
  338. REQUIRE(triangle.simplify(250000).at(0).points.size() == 3);
  339. }