test_geometry.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  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. #include <libslic3r/SVG.hpp>
  10. using namespace Slic3r;
  11. TEST_CASE("Polygon::contains works properly", ""){
  12. // this test was failing on Windows (GH #1950)
  13. Slic3r::Polygon polygon( Points{
  14. Point{207802834,-57084522},
  15. Point{196528149,-37556190},
  16. Point{173626821,-25420928},
  17. Point{171285751,-21366123},
  18. Point{118673592,-21366123},
  19. Point{116332562,-25420928},
  20. Point{93431208,-37556191},
  21. Point{82156517,-57084523},
  22. Point{129714478,-84542120},
  23. Point{160244873,-84542120}
  24. } );
  25. Point point{ 95706562, -57294774 };
  26. REQUIRE(polygon.contains(point));
  27. }
  28. SCENARIO("Intersections of line segments"){
  29. GIVEN("Integer coordinates"){
  30. Line line1{ Point::new_scale(5,15),Point::new_scale(30,15) };
  31. Line line2{ Point::new_scale(10,20), Point::new_scale(10,10) };
  32. THEN("The intersection is valid"){
  33. Point point;
  34. line1.intersection(line2,&point);
  35. REQUIRE(Point::new_scale(10,15) == point);
  36. }
  37. }
  38. GIVEN("Scaled coordinates"){
  39. Line line1{ Point::new_scale(73.6310778185108, 371.74239268924), Point::new_scale(73.6310778185108, 501.74239268924) };
  40. Line line2{ Point::new_scale(75, 437.9853), Point::new_scale(62.7484, 440.4223) };
  41. THEN("There is still an intersection"){
  42. Point point;
  43. REQUIRE(line1.intersection(line2,&point));
  44. }
  45. }
  46. }
  47. inline void my_clip_clipper_polygon_with_subject_bbox_templ(const Points &src, const BoundingBox &bbox, Points &out)
  48. {
  49. out.clear();
  50. const size_t cnt = src.size();
  51. if (cnt < 3)
  52. return;
  53. enum class Side {
  54. Left = 1,
  55. Right = 2,
  56. Top = 4,
  57. Bottom = 8
  58. };
  59. auto sides = [bbox](const Point &p) {
  60. return int(p.x() < bbox.min.x()) * int(Side::Left) +
  61. int(p.x() > bbox.max.x()) * int(Side::Right) +
  62. int(p.y() < bbox.min.y()) * int(Side::Bottom) +
  63. int(p.y() > bbox.max.y()) * int(Side::Top);
  64. };
  65. int sides_prev = sides(src.back());
  66. int sides_this = sides(src.front());
  67. const size_t last = cnt - 1;
  68. for (size_t i = 0; i < last; ++ i) {
  69. int sides_next = sides(src[i + 1]);
  70. if (// This point is inside. Take it.
  71. sides_this == 0 ||
  72. // Either this point is outside and previous or next is inside, or
  73. // the edge possibly cuts corner of the bounding box.
  74. (sides_prev & sides_this & sides_next) == 0) {
  75. out.emplace_back(src[i]);
  76. sides_prev = sides_this;
  77. } else {
  78. // All the three points (this, prev, next) are outside at the same side.
  79. // Ignore this point.
  80. }
  81. sides_this = sides_next;
  82. }
  83. // Never produce just a single point output polygon.
  84. if (! out.empty())
  85. if (int sides_next = sides(out.front());
  86. // The last point is inside. Take it.
  87. sides_this == 0 ||
  88. // Either this point is outside and previous or next is inside, or
  89. // the edge possibly cuts corner of the bounding box.
  90. (sides_prev & sides_this & sides_next) == 0)
  91. out.emplace_back(src.back());
  92. //assert(out.size() > 2 || out.empty());
  93. }
  94. SCENARIO("clip_clipper_polygon_with_subject_bbox"){
  95. BoundingBox bb(Point::new_scale(0,0),Point::new_scale(10,10));
  96. GIVEN("inside"){
  97. Slic3r::Polygon poly({Point::new_scale(3,1),Point::new_scale(6,2),Point::new_scale(4,8)});
  98. THEN("The intersection is valid"){
  99. Slic3r::Polygon result = ClipperUtils::clip_clipper_polygon_with_subject_bbox(poly, bb);
  100. REQUIRE(poly == result);
  101. }
  102. }
  103. GIVEN("both"){
  104. Slic3r::Polygon poly({Point::new_scale(-15,1),Point::new_scale(1,1),Point::new_scale(9,1),Point::new_scale(15,1),
  105. Point::new_scale(9,7),Point::new_scale(7,9),Point::new_scale(5,11),
  106. Point::new_scale(3,9),Point::new_scale(1,7)});
  107. THEN("The intersection is valid"){
  108. Slic3r::Polygon result = ClipperUtils::clip_clipper_polygon_with_subject_bbox(poly, bb);
  109. //Slic3r::Polygon poly_result({Point::new_scale(1,1),Point::new_scale(9,1),
  110. //Point::new_scale(9,7),Point::new_scale(7,9),
  111. //Point::new_scale(3,9),Point::new_scale(1,7)});
  112. REQUIRE(poly == result);
  113. }
  114. }
  115. GIVEN("outside but crossing"){
  116. Slic3r::Polygon poly({Point::new_scale(-3,-1),Point::new_scale(6,-2),Point::new_scale(4,11)});
  117. THEN("The intersection is valid"){
  118. Slic3r::Polygon result = ClipperUtils::clip_clipper_polygon_with_subject_bbox(poly, bb);
  119. REQUIRE(poly == result);
  120. }
  121. }
  122. GIVEN("outside, including the bb inside"){
  123. Slic3r::Polygon poly({Point::new_scale(-3,-3),Point::new_scale(20,-3),Point::new_scale(20,20),Point::new_scale(-3,20)});
  124. THEN("The intersection is valid"){
  125. Slic3r::Polygon result = ClipperUtils::clip_clipper_polygon_with_subject_bbox(poly, bb);
  126. REQUIRE(poly == result);
  127. }
  128. }
  129. GIVEN("outside not crossing"){
  130. Slic3r::Polygon poly({Point::new_scale(-3,-1),Point::new_scale(-6,-2),Point::new_scale(-4,-11)});
  131. THEN("The intersection is none"){
  132. Slic3r::Polygon result = ClipperUtils::clip_clipper_polygon_with_subject_bbox(poly, bb);
  133. REQUIRE(result.empty());
  134. }
  135. }
  136. GIVEN("weird thing"){
  137. Slic3r::Polygon polytest({Point{-5253696,6941803},Point{-5390322,7004051},Point{-5529994,7112838},Point{-5642583,7262245},Point{-5708854,7426076},Point{-5731772,7600991},Point{-5710375,7774699},Point{-5643429,7942791},Point{-3001108,11534999},Point{-2782155,11687851},Point{-2602580,11739919},Point{-2335818,11727897},Point{-2161635,11659878},Point{-1955359,11486297},Point{-1830017,11244692},Point{-1806677,10973499},Point{-1888105,10716510},Point{-4515353,7139500},Point{-4638040,7031082},Point{-4773007,6958051},Point{-4924657,6915497},Point{-5069276,6907438}});
  138. BoundingBox boxtest(Point{-1854941, 7335228}, Point{4734351, 9668157});
  139. Slic3r::Polygon result = ClipperUtils::clip_clipper_polygon_with_subject_bbox(polytest, boxtest);
  140. //Slic3r::Polygon result;
  141. //my_clip_clipper_polygon_with_subject_bbox_templ(polytest.points, boxtest, result.points);
  142. //::Slic3r::SVG svg("weird.svg");
  143. //svg.draw(boxtest.polygon(), "grey");
  144. //svg.draw(polytest.split_at_first_point(), "blue", scale_t(0.05));
  145. //for(Point pt : result.points)
  146. // svg.draw(pt, "green", scale_t(0.03));
  147. //svg.Close();
  148. //REQUIRE(result.size() > 2);
  149. REQUIRE(result.empty());
  150. }
  151. }
  152. /*
  153. Tests for unused methods still written in perl
  154. {
  155. my $polygon = Slic3r::Polygon->new(
  156. [45919000, 515273900], [14726100, 461246400], [14726100, 348753500], [33988700, 315389800],
  157. [43749700, 343843000], [45422300, 352251500], [52362100, 362637800], [62748400, 369577600],
  158. [75000000, 372014700], [87251500, 369577600], [97637800, 362637800], [104577600, 352251500],
  159. [107014700, 340000000], [104577600, 327748400], [97637800, 317362100], [87251500, 310422300],
  160. [82789200, 309534700], [69846100, 294726100], [254081000, 294726100], [285273900, 348753500],
  161. [285273900, 461246400], [254081000, 515273900],
  162. );
  163. # this points belongs to $polyline
  164. # note: it's actually a vertex, while we should better check an intermediate point
  165. my $point = Slic3r::Point->new(104577600, 327748400);
  166. local $Slic3r::Geometry::epsilon = 1E-5;
  167. is_deeply Slic3r::Geometry::polygon_segment_having_point($polygon, $point)->pp,
  168. [ [107014700, 340000000], [104577600, 327748400] ],
  169. 'polygon_segment_having_point';
  170. }
  171. {
  172. auto point = Point{736310778.185108, 5017423926.8924};
  173. auto line = Line(Point{(long int} 627484000, (long int) 3695776000), Point{(long int} 750000000, (long int)3720147000));
  174. //is Slic3r::Geometry::point_in_segment($point, $line), 0, 'point_in_segment';
  175. }
  176. // Possible to delete
  177. {
  178. //my $p1 = [10, 10];
  179. //my $p2 = [10, 20];
  180. //my $p3 = [10, 30];
  181. //my $p4 = [20, 20];
  182. //my $p5 = [0, 20];
  183. THEN("Points in a line give the correct angles"){
  184. //is Slic3r::Geometry::angle3points($p2, $p3, $p1), PI(), 'angle3points';
  185. //is Slic3r::Geometry::angle3points($p2, $p1, $p3), PI(), 'angle3points';
  186. }
  187. THEN("Left turns give the correct angle"){
  188. //is Slic3r::Geometry::angle3points($p2, $p4, $p3), PI()/2, 'angle3points';
  189. //is Slic3r::Geometry::angle3points($p2, $p1, $p4), PI()/2, 'angle3points';
  190. }
  191. THEN("Right turns give the correct angle"){
  192. //is Slic3r::Geometry::angle3points($p2, $p3, $p4), PI()/2*3, 'angle3points';
  193. //is Slic3r::Geometry::angle3points($p2, $p1, $p5), PI()/2*3, 'angle3points';
  194. }
  195. //my $p1 = [30, 30];
  196. //my $p2 = [20, 20];
  197. //my $p3 = [10, 10];
  198. //my $p4 = [30, 10];
  199. //is Slic3r::Geometry::angle3points($p2, $p1, $p3), PI(), 'angle3points';
  200. //is Slic3r::Geometry::angle3points($p2, $p1, $p4), PI()/2*3, 'angle3points';
  201. //is Slic3r::Geometry::angle3points($p2, $p1, $p1), 2*PI(), 'angle3points';
  202. }
  203. SCENARIO("polygon_is_convex works"){
  204. GIVEN("A square of dimension 10"){
  205. //my $cw_square = [ [0,0], [0,10], [10,10], [10,0] ];
  206. THEN("It is not convex clockwise"){
  207. //is polygon_is_convex($cw_square), 0, 'cw square is not convex';
  208. }
  209. THEN("It is convex counter-clockwise"){
  210. //is polygon_is_convex([ reverse @$cw_square ]), 1, 'ccw square is convex';
  211. }
  212. }
  213. GIVEN("A concave polygon"){
  214. //my $convex1 = [ [0,0], [10,0], [10,10], [0,10], [0,6], [4,6], [4,4], [0,4] ];
  215. THEN("It is concave"){
  216. //is polygon_is_convex($convex1), 0, 'concave polygon';
  217. }
  218. }
  219. }*/
  220. TEST_CASE("Creating a polyline generates the obvious lines"){
  221. auto polyline = Slic3r::Polyline();
  222. polyline.points = Points({Point::new_scale(0, 0), Point::new_scale(10, 0), Point::new_scale(20, 0)});
  223. REQUIRE(polyline.lines().at(0).a == Point::new_scale(0,0));
  224. REQUIRE(polyline.lines().at(0).b == Point::new_scale(10,0));
  225. REQUIRE(polyline.lines().at(1).a == Point::new_scale(10,0));
  226. REQUIRE(polyline.lines().at(1).b == Point::new_scale(20,0));
  227. }
  228. TEST_CASE("Splitting a Polygon generates a polyline correctly"){
  229. Slic3r::Polygon polygon = Slic3r::Polygon({Point::new_scale(0, 0), Point::new_scale(10, 0), Point::new_scale(5, 5)});
  230. Slic3r::Polyline split = polygon.split_at_index(1);
  231. REQUIRE(split.points.size() == 4);
  232. REQUIRE(split.points[0]==Point::new_scale(10,0));
  233. REQUIRE(split.points[1]==Point::new_scale(5,5));
  234. REQUIRE(split.points[2]==Point::new_scale(0,0));
  235. REQUIRE(split.points[3]==Point::new_scale(10,0));
  236. }
  237. TEST_CASE("Bounding boxes are scaled appropriately"){
  238. Slic3r::BoundingBox bb(Points{Point::new_scale(0, 1), Point::new_scale(10, 2), Point::new_scale(20, 2)});
  239. bb.scale(2);
  240. REQUIRE(bb.min == Point::new_scale(0,2));
  241. REQUIRE(bb.max == Point::new_scale(40,4));
  242. }
  243. TEST_CASE("Offseting a line generates a polygon correctly"){
  244. Slic3r::Polyline tmp(Points{{10,10},{20,10} });
  245. Slic3r::Polygon area = offset(tmp, scale_d(5)).at(0);
  246. REQUIRE(area.area() == Slic3r::Polygon({Point::new_scale(10,5),Point::new_scale(20,5),Point::new_scale(20,15),Point::new_scale(10,15)}).area());
  247. }
  248. SCENARIO("Circle Fit, TaubinFit with Newton's method") {
  249. GIVEN("A vector of Pointfs arranged in a half-circle with approximately the same distance R from some point") {
  250. Vec2d expected_center(-6, 0);
  251. 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}};
  252. std::transform(sample.begin(), sample.end(), sample.begin(), [expected_center] (const Vec2d& a) { return a + expected_center;});
  253. WHEN("Circle fit is called on the entire array") {
  254. Vec2d result_center(0,0);
  255. result_center = Geometry::circle_center_taubin_newton(sample);
  256. THEN("A center point of -6,0 is returned.") {
  257. REQUIRE((result_center - expected_center).norm() < EPSILON);
  258. }
  259. }
  260. WHEN("Circle fit is called on the first four points") {
  261. Vec2d result_center(0,0);
  262. result_center = Geometry::circle_center_taubin_newton(sample.cbegin(), sample.cbegin()+4);
  263. THEN("A center point of -6,0 is returned.") {
  264. REQUIRE((result_center - expected_center).norm() < EPSILON);
  265. }
  266. }
  267. WHEN("Circle fit is called on the middle four points") {
  268. Vec2d result_center(0,0);
  269. result_center = Geometry::circle_center_taubin_newton(sample.cbegin()+2, sample.cbegin()+6);
  270. THEN("A center point of -6,0 is returned.") {
  271. REQUIRE((result_center - expected_center).norm() < EPSILON);
  272. }
  273. }
  274. }
  275. GIVEN("A vector of Pointfs arranged in a half-circle with approximately the same distance R from some point") {
  276. Vec2d expected_center(-3, 9);
  277. Vec2ds sample {Vec2d{6.0, 0}, Vec2d{5.1961524, 3}, Vec2d{3 ,5.1961524},
  278. Vec2d{0, 6.0},
  279. Vec2d{3, 5.1961524}, Vec2d{-5.1961524, 3}, Vec2d{-6.0, 0}};
  280. std::transform(sample.begin(), sample.end(), sample.begin(), [expected_center] (const Vec2d& a) { return a + expected_center;});
  281. WHEN("Circle fit is called on the entire array") {
  282. Vec2d result_center(0,0);
  283. result_center = Geometry::circle_center_taubin_newton(sample);
  284. THEN("A center point of 3,9 is returned.") {
  285. REQUIRE((result_center - expected_center).norm() < EPSILON);
  286. }
  287. }
  288. WHEN("Circle fit is called on the first four points") {
  289. Vec2d result_center(0,0);
  290. result_center = Geometry::circle_center_taubin_newton(sample.cbegin(), sample.cbegin()+4);
  291. THEN("A center point of 3,9 is returned.") {
  292. REQUIRE((result_center - expected_center).norm() < EPSILON);
  293. }
  294. }
  295. WHEN("Circle fit is called on the middle four points") {
  296. Vec2d result_center(0,0);
  297. result_center = Geometry::circle_center_taubin_newton(sample.cbegin()+2, sample.cbegin()+6);
  298. THEN("A center point of 3,9 is returned.") {
  299. REQUIRE((result_center - expected_center).norm() < EPSILON);
  300. }
  301. }
  302. }
  303. GIVEN("A vector of Points arranged in a half-circle with approximately the same distance R from some point") {
  304. Point expected_center { Point::new_scale(-3, 9)};
  305. Points sample {Point::new_scale(6.0, 0), Point::new_scale(5.1961524, 3), Point::new_scale(3 ,5.1961524),
  306. Point::new_scale(0, 6.0),
  307. Point::new_scale(3, 5.1961524), Point::new_scale(-5.1961524, 3), Point::new_scale(-6.0, 0)};
  308. std::transform(sample.begin(), sample.end(), sample.begin(), [expected_center] (const Point& a) { return a + expected_center;});
  309. WHEN("Circle fit is called on the entire array") {
  310. Point result_center(0,0);
  311. result_center = Geometry::circle_center_taubin_newton(sample);
  312. THEN("A center point of scaled 3,9 is returned.") {
  313. REQUIRE(result_center.coincides_with_epsilon(expected_center));
  314. }
  315. }
  316. WHEN("Circle fit is called on the first four points") {
  317. Point result_center(0,0);
  318. result_center = Geometry::circle_center_taubin_newton(sample.cbegin(), sample.cbegin()+4);
  319. THEN("A center point of scaled 3,9 is returned.") {
  320. REQUIRE(result_center.coincides_with_epsilon(expected_center));
  321. }
  322. }
  323. WHEN("Circle fit is called on the middle four points") {
  324. Point result_center(0,0);
  325. result_center = Geometry::circle_center_taubin_newton(sample.cbegin()+2, sample.cbegin()+6);
  326. THEN("A center point of scaled 3,9 is returned.") {
  327. REQUIRE(result_center.coincides_with_epsilon(expected_center));
  328. }
  329. }
  330. }
  331. }
  332. // A PU
  333. //TEST_CASE("Chained path working correctly"){
  334. // // if chained_path() works correctly, these points should be joined with no diagonal paths
  335. // // (thus 26 units long)
  336. // std::vector<Point> points = {Point::new_scale(26,26),Point::new_scale(52,26),Point::new_scale(0,26),Point::new_scale(26,52),Point::new_scale(26,0),Point::new_scale(0,52),Point::new_scale(52,52),Point::new_scale(52,0)};
  337. // std::vector<Points::size_type> indices;
  338. // Geometry::chained_path(points,indices);
  339. // for(Points::size_type i = 0; i < indices.size()-1;i++){
  340. // double dist = points.at(indices.at(i)).distance_to(points.at(indices.at(i+1)));
  341. // REQUIRE(abs(dist-26) <= EPSILON);
  342. // }
  343. //}
  344. SCENARIO("Line distances"){
  345. GIVEN("A line"){
  346. Line line{ Point::new_scale(0, 0), Point::new_scale(20, 0) };
  347. THEN("Points on the line segment have 0 distance"){
  348. REQUIRE(line.distance_to(Point::new_scale(0, 0)) == 0);
  349. REQUIRE(line.distance_to(Point::new_scale(20, 0)) == 0);
  350. REQUIRE(line.distance_to(Point::new_scale(10, 0)) == 0);
  351. }
  352. THEN("Points off the line have the appropriate distance"){
  353. REQUIRE(line.distance_to(Point::new_scale(10, 10)) == scale_t(10));
  354. REQUIRE(line.distance_to(Point::new_scale(50, 0)) == scale_t(30));
  355. }
  356. }
  357. }
  358. SCENARIO("Polygon convex/concave detection"){
  359. GIVEN(("A Square with dimension 100")){
  360. Slic3r::Polygon square/*new_scale*/( Points{
  361. Point::new_scale(100,100),
  362. Point::new_scale(200,100),
  363. Point::new_scale(200,200),
  364. Point::new_scale(100,200)});
  365. THEN("It has 4 convex points counterclockwise"){
  366. REQUIRE(square.concave_points(PI*4/3).size() == 0);
  367. REQUIRE(square.convex_points(PI*2/3).size() == 4);
  368. }
  369. THEN("It has 4 concave points clockwise"){
  370. square.make_clockwise();
  371. REQUIRE(square.concave_points(PI*4/3).size() == 4);
  372. REQUIRE(square.convex_points(PI*2/3).size() == 0);
  373. }
  374. }
  375. GIVEN("A Square with an extra colinearvertex"){
  376. Slic3r::Polygon square /*new_scale*/( Points{
  377. Point::new_scale(150,100),
  378. Point::new_scale(200,100),
  379. Point::new_scale(200,200),
  380. Point::new_scale(100,200),
  381. Point::new_scale(100,100)} );
  382. THEN("It has 4 convex points counterclockwise"){
  383. REQUIRE(square.concave_points(PI*4/3).size() == 0);
  384. REQUIRE(square.convex_points(PI*2/3).size() == 4);
  385. }
  386. }
  387. GIVEN("A Square with an extra collinear vertex in different order"){
  388. Slic3r::Polygon square /*new_scale*/( Points{
  389. Point::new_scale(200,200),
  390. Point::new_scale(100,200),
  391. Point::new_scale(100,100),
  392. Point::new_scale(150,100),
  393. Point::new_scale(200,100)} );
  394. THEN("It has 4 convex points counterclockwise"){
  395. REQUIRE(square.concave_points(PI*4/3).size() == 0);
  396. REQUIRE(square.convex_points(PI*2/3).size() == 4);
  397. }
  398. }
  399. GIVEN("A triangle"){
  400. Slic3r::Polygon triangle( Points{
  401. Point{16000170,26257364},
  402. Point{714223,461012},
  403. Point{31286371,461008}
  404. } );
  405. THEN("it has three convex vertices"){
  406. REQUIRE(triangle.concave_points(PI*4/3).size() == 0);
  407. REQUIRE(triangle.convex_points(PI*2/3).size() == 3);
  408. }
  409. }
  410. GIVEN("A triangle with an extra collinear point"){
  411. Slic3r::Polygon triangle( Points{
  412. Point{16000170,26257364},
  413. Point{714223,461012},
  414. Point{20000000,461012},
  415. Point{31286371,461012}
  416. } );
  417. THEN("it has three convex vertices"){
  418. REQUIRE(triangle.concave_points(PI*4/3).size() == 0);
  419. REQUIRE(triangle.convex_points(PI*2/3).size() == 3);
  420. }
  421. }
  422. GIVEN("A polygon with concave vertices with angles of specifically 4/3pi"){
  423. // Two concave vertices of this polygon have angle = PI*4/3, so this test fails
  424. // if epsilon is not used.
  425. Slic3r::Polygon polygon( Points{
  426. Point{60246458,14802768},Point{64477191,12360001},
  427. Point{63727343,11060995},Point{64086449,10853608},
  428. Point{66393722,14850069},Point{66034704,15057334},
  429. Point{65284646,13758387},Point{61053864,16200839},
  430. Point{69200258,30310849},Point{62172547,42483120},
  431. Point{61137680,41850279},Point{67799985,30310848},
  432. Point{51399866,1905506},Point{38092663,1905506},
  433. Point{38092663,692699},Point{52100125,692699}
  434. } );
  435. THEN("the correct number of points are detected"){
  436. REQUIRE(polygon.concave_points(PI*4/3).size() == 6);
  437. REQUIRE(polygon.convex_points(PI*2/3).size() == 10);
  438. }
  439. }
  440. }
  441. TEST_CASE("Triangle Simplification does not result in less than 3 points"){
  442. Slic3r::Polygon triangle( Points{
  443. Point{16000170,26257364}, Point{714223,461012}, Point{31286371,461008}
  444. } );
  445. REQUIRE(triangle.simplify(250000).at(0).points.size() == 3);
  446. }