BridgeDetector.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. ///|/ Copyright (c) Prusa Research 2016 - 2021 Vojtěch Bubník @bubnikv
  2. ///|/ Copyright (c) Slic3r 2014 - 2016 Alessandro Ranellucci @alranel
  3. ///|/ Copyright (c) 2015 Maksim Derbasov @ntfshard
  4. ///|/
  5. ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
  6. ///|/
  7. #include "BridgeDetector.hpp"
  8. #include "ClipperUtils.hpp"
  9. #include "Geometry.hpp"
  10. #include <algorithm>
  11. namespace Slic3r {
  12. BridgeDetector::BridgeDetector(ExPolygon _expolygon,
  13. const ExPolygons &_lower_slices,
  14. coord_t _extrusion_spacing,
  15. coord_t _precision,
  16. int layer_idx)
  17. :
  18. // The original infill polygon, not inflated.
  19. expolygons(expolygons_owned),
  20. // All surfaces of the object supporting this region.
  21. lower_slices(_lower_slices),
  22. spacing(_extrusion_spacing),
  23. precision(_precision),
  24. layer_id(layer_idx)
  25. {
  26. this->expolygons_owned.push_back(std::move(_expolygon));
  27. initialize();
  28. }
  29. BridgeDetector::BridgeDetector(const ExPolygons &_expolygons,
  30. const ExPolygons &_lower_slices,
  31. coord_t _extrusion_spacing,
  32. coord_t _precision,
  33. int layer_idx)
  34. :
  35. // The original infill polygon, not inflated.
  36. expolygons(_expolygons),
  37. // All surfaces of the object supporting this region.
  38. lower_slices(_lower_slices),
  39. spacing(_extrusion_spacing),
  40. precision(_precision),
  41. layer_id(layer_idx)
  42. {
  43. initialize();
  44. }
  45. void BridgeDetector::initialize()
  46. {
  47. // 2 degrees stepping
  48. this->resolution = PI/(90);
  49. // output angle not known
  50. this->angle = -1.;
  51. // Outset our bridge by an arbitrary amout; we'll use this outer margin for detecting anchors.
  52. Polygons grown = offset(this->expolygons, float(this->spacing * 0.5), ClipperLib::JoinType::jtMiter);
  53. //remove bits that shoudln't be here, but are due to the grow + clip
  54. //get the unsupported out-of-part section (if any)
  55. ExPolygons union_lower_slices = union_safety_offset_ex(this->lower_slices);
  56. ExPolygons part_areas = union_lower_slices;
  57. append(part_areas, this->expolygons);
  58. part_areas = union_safety_offset_ex(part_areas);
  59. ExPolygons out_of_part = diff_ex(grown, part_areas);
  60. // then grow it
  61. out_of_part = offset_ex(out_of_part, float(this->spacing * 0.55), ClipperLib::JoinType::jtMiter);
  62. // remove it from grow
  63. grown = diff(grown, out_of_part);
  64. //finish growing
  65. grown = offset(grown, float(this->spacing * 0.55f), ClipperLib::JoinType::jtMiter, 6);
  66. // Detect possible anchoring edges of this bridging region.
  67. // Detect what edges lie on lower slices by turning bridge contour and holes
  68. // into polylines and then clipping them with each lower slice's contour.
  69. // Currently _edges are only used to set a candidate direction of the bridge (see bridge_direction_candidates()).
  70. Polygons contours;
  71. contours.reserve(this->lower_slices.size());
  72. for (const ExPolygon &expoly : this->lower_slices)
  73. contours.push_back(expoly.contour);
  74. this->_edges = intersection_pl(to_polylines(grown), contours);
  75. #ifdef SLIC3R_DEBUG
  76. printf(" bridge has %zu support(s)\n", this->_edges.size());
  77. #endif
  78. // detect anchors as intersection between our bridge expolygon and the lower slices
  79. // safety offset required to avoid Clipper from detecting empty intersection while Boost actually found some edges
  80. this->_anchor_regions = intersection_ex(grown, union_lower_slices);
  81. /*
  82. if (0) {
  83. require "Slic3r/SVG.pm";
  84. Slic3r::SVG::output("bridge.svg",
  85. expolygons => [ $self->expolygon ],
  86. red_expolygons => $self->lower_slices,
  87. polylines => $self->_edges,
  88. );
  89. }
  90. */
  91. }
  92. bool BridgeDetector::detect_angle(double bridge_direction_override /* = -1*/)
  93. {
  94. if (this->_edges.empty() || this->_anchor_regions.empty())
  95. // The bridging region is completely in the air, there are no anchors available at the layer below.
  96. return false;
  97. std::vector<BridgeDirection> candidates;
  98. if (bridge_direction_override < 0.) {
  99. candidates = bridge_direction_candidates();
  100. } else
  101. candidates.emplace_back(BridgeDirection(bridge_direction_override));
  102. /* Outset the bridge expolygon by half the amount we used for detecting anchors;
  103. we'll use this one to clip our test lines and be sure that their endpoints
  104. are inside the anchors and not on their contours leading to false negatives. */
  105. Polygons clip_area = offset(this->expolygons, 0.5f * float(this->spacing));
  106. // union with offseted anchor before un-offset to get the good clip area with anchor added.
  107. ExPolygons unoffset_clip = offset_ex(this->_anchor_regions, 0.5f * float(this->spacing));
  108. for (Polygon &poly : clip_area) {
  109. unoffset_clip.emplace_back(poly);
  110. }
  111. unoffset_clip = union_ex(unoffset_clip);
  112. unoffset_clip = offset_ex(unoffset_clip, -0.5f * float(this->spacing));
  113. // now clip the clip with not-offset merged anchor + expolygons, so it's enlarged only inside the anchor.
  114. clip_area = intersection(unoffset_clip, clip_area);
  115. /* we'll now try several directions using a rudimentary visibility check:
  116. bridge in several directions and then sum the length of lines having both
  117. endpoints within anchors */
  118. bool have_coverage = false;
  119. for (size_t i_angle = 0; i_angle < candidates.size(); ++ i_angle)
  120. {
  121. const double angle = candidates[i_angle].angle;
  122. Lines lines;
  123. {
  124. // Get an oriented bounding box around _anchor_regions.
  125. BoundingBox bbox = get_extents_rotated(this->_anchor_regions, - angle);
  126. // Cover the region with line segments.
  127. lines.reserve((bbox.max.y() - bbox.min.y() + this->spacing - SCALED_EPSILON) / this->spacing);
  128. double s = sin(angle);
  129. double c = cos(angle);
  130. // As The lines be spaced half the line width from the edge
  131. // FIXME: some of the test cases may fail. Need to adjust the test cases
  132. for (coord_t y = bbox.min.y() + this->spacing / 2; y <= bbox.max.y(); y += this->spacing)
  133. //for (coord_t y = bbox.min.y(); y <= bbox.max.y(); y += this->spacing) //this is the old version
  134. lines.push_back(Line(
  135. Point((coord_t)round(c * bbox.min.x() - s * y), (coord_t)round(c * y + s * bbox.min.x())),
  136. Point((coord_t)round(c * bbox.max.x() - s * y), (coord_t)round(c * y + s * bbox.max.x()))));
  137. }
  138. //create boundingbox for anchor regions
  139. std::vector<BoundingBox> anchor_bb;
  140. for (ExPolygon& poly : this->_anchor_regions) {
  141. anchor_bb.emplace_back(poly.contour.bounding_box());
  142. }
  143. //compute stat on line with anchors, and their lengths.
  144. BridgeDirection& bridge_dir_candidate = candidates[i_angle];
  145. std::vector<coordf_t> dist_anchored;
  146. {
  147. Lines clipped_lines = intersection_ln(lines, clip_area);
  148. for (size_t i = 0; i < clipped_lines.size(); ++i) {
  149. // this can be called 100 000 time per detect_angle, please optimise
  150. const Line &line = clipped_lines[i];
  151. bool good_line = false;
  152. bool fake_bridge = false;
  153. coordf_t len = line.length();
  154. // check if the line isn't too long
  155. if (good_line && max_bridge_length > 0 && len > max_bridge_length) {
  156. good_line = false;
  157. } else {
  158. // is anchored?
  159. size_t line_a_anchor_idx = -1;
  160. size_t line_b_anchor_idx = -1;
  161. for (int i = 0; i < _anchor_regions.size(); ++i) {
  162. ExPolygon & poly = this->_anchor_regions[i];
  163. BoundingBox &polybb = anchor_bb[i];
  164. if (polybb.contains(line.a) &&
  165. poly.contains(
  166. line.a)) { // using short-circuit evaluation to test boundingbox and only then the other
  167. line_a_anchor_idx = i;
  168. }
  169. if (polybb.contains(line.b) &&
  170. poly.contains(
  171. line.b)) { // using short-circuit evaluation to test boundingbox and only then the other
  172. line_b_anchor_idx = i;
  173. }
  174. if (line_a_anchor_idx < clipped_lines.size() && line_b_anchor_idx < clipped_lines.size())
  175. break;
  176. }
  177. // check if the anchor search has been successful
  178. // note: this 'if' isn't very effective (culls ~ 10% on a benchy) but it's almost free to compute
  179. if ((line_a_anchor_idx < clipped_lines.size()) & (line_b_anchor_idx < clipped_lines.size())) {
  180. good_line = true;
  181. // test if it's not a fake bridge
  182. if (line_a_anchor_idx == line_b_anchor_idx) {
  183. fake_bridge = true;
  184. // check that the line go out of the anchor into the briding area
  185. // don't call intersection_ln here, as even if we succeed to limit the number of
  186. // candidates to ~100, here we can have hundreds of lines, so that means dozen of
  187. // thousands of calls (or more)! add some points (at least the middle) to test, it's quick
  188. Point middle_point = line.midpoint();
  189. for (int i = 0; i < _anchor_regions.size(); ++i) {
  190. ExPolygon & poly = this->_anchor_regions[i];
  191. BoundingBox &polybb = anchor_bb[i];
  192. if (!polybb.contains(middle_point) ||
  193. !poly.contains(middle_point)) { // using short-circuit evaluation to test
  194. // boundingbox and only then the other
  195. fake_bridge = false;
  196. goto stop_fake_bridge_test;
  197. }
  198. }
  199. // try with rigth & left
  200. if (fake_bridge && len > this->spacing * 4) {
  201. Vector normal = line.normal();
  202. normal.normalize();
  203. normal *= coordf_t(spacing / 2);
  204. Point middle_point_right = line.midpoint() + normal;
  205. Point middle_point_left = line.midpoint() - normal;
  206. for (int i = 0; i < _anchor_regions.size(); ++i) {
  207. ExPolygon & poly = this->_anchor_regions[i];
  208. BoundingBox &polybb = anchor_bb[i];
  209. if (!poly.contains(middle_point_right) || !poly.contains(middle_point_left)) {
  210. fake_bridge = false;
  211. goto stop_fake_bridge_test;
  212. }
  213. }
  214. }
  215. // if still bad, the line is long enough to warrant two more test point? (1/2000 on a benchy)
  216. if (fake_bridge && len > this->spacing * 10) {
  217. // now test with to four more points
  218. Vector normal = line.normal();
  219. normal.normalize();
  220. normal *= coordf_t(spacing / 2);
  221. Points pts;
  222. pts.push_back((line.a + middle_point) / 2 + normal);
  223. pts.push_back((line.a + middle_point) / 2 - normal);
  224. pts.push_back((line.b + middle_point) / 2 + normal);
  225. pts.push_back((line.b + middle_point) / 2 - normal);
  226. for (int i = 0; i < _anchor_regions.size(); ++i) {
  227. ExPolygon & poly = this->_anchor_regions[i];
  228. BoundingBox &polybb = anchor_bb[i];
  229. for (Point &pt : pts)
  230. if (!polybb.contains(pt) ||
  231. !poly.contains(pt)) { // using short-circuit evaluation to test
  232. // boundingbox and only then the other
  233. fake_bridge = false;
  234. goto stop_fake_bridge_test;
  235. }
  236. }
  237. }
  238. // If the line is still bad and is a long one, use the more costly intersection_ln. This
  239. // case is rare enough to swallow the cost. (1/10000 on a benchy)
  240. if (fake_bridge && len > this->spacing * 40) {
  241. // now test with intersection_ln
  242. Lines lines = intersection_ln(line, to_polygons(this->_anchor_regions));
  243. // if < 2, not anchored at both end
  244. fake_bridge = lines.size() < 2;
  245. }
  246. }
  247. }
  248. stop_fake_bridge_test: ;
  249. }
  250. if (good_line && fake_bridge)
  251. bridge_dir_candidate.nb_lines_fake_bridge++;
  252. if (good_line) {
  253. // This line could be anchored at both side and goes over the void to bridge it in its middle.
  254. //store stats
  255. if (!fake_bridge) {
  256. bridge_dir_candidate.total_length_anchored += len;
  257. bridge_dir_candidate.max_length_anchored = std::max(bridge_dir_candidate.max_length_anchored, len);
  258. }
  259. bridge_dir_candidate.nb_lines_anchored++;
  260. dist_anchored.push_back(len);
  261. } else {
  262. // this line could NOT be anchored.
  263. bridge_dir_candidate.total_length_free += len;
  264. bridge_dir_candidate.max_length_free = std::max(bridge_dir_candidate.max_length_free, len);
  265. bridge_dir_candidate.nb_lines_free++;
  266. }
  267. }
  268. }
  269. if (bridge_dir_candidate.total_length_anchored == 0. || bridge_dir_candidate.nb_lines_anchored == 0) {
  270. continue;
  271. } else {
  272. have_coverage = true;
  273. // compute median
  274. if (!dist_anchored.empty()) {
  275. std::sort(dist_anchored.begin(), dist_anchored.end());
  276. bridge_dir_candidate.median_length_anchor = dist_anchored[dist_anchored.size() / 2];
  277. }
  278. // size is 20%
  279. }
  280. }
  281. // if no direction produced coverage, then there's no bridge direction ?
  282. if (!have_coverage) {
  283. //try again to choose the least worse
  284. // use only poly contour angles
  285. if (bridge_direction_override == 0.) {
  286. candidates = bridge_direction_candidates(true);
  287. } else
  288. candidates.emplace_back(BridgeDirection(bridge_direction_override));
  289. for (size_t i_angle = 0; i_angle < candidates.size(); ++i_angle)
  290. {
  291. const double angle = candidates[i_angle].angle;
  292. //use the whole polygon
  293. Lines lines;
  294. {
  295. // Get an oriented bounding box around _anchor_regions.
  296. BoundingBox bbox = get_extents_rotated(clip_area, -angle);
  297. // Cover the region with line segments.
  298. lines.reserve((bbox.max.y() - bbox.min.y() + this->spacing - SCALED_EPSILON) / this->spacing);
  299. double s = sin(angle);
  300. double c = cos(angle);
  301. // The lines be spaced half the line width from the edge
  302. for (coord_t y = bbox.min.y() + this->spacing / 2; y <= bbox.max.y(); y += this->spacing)
  303. lines.push_back(Line(
  304. Point((coord_t)round(c * bbox.min.x() - s * y), (coord_t)round(c * y + s * bbox.min.x())),
  305. Point((coord_t)round(c * bbox.max.x() - s * y), (coord_t)round(c * y + s * bbox.max.x()))));
  306. }
  307. //compute stat on line with anchors, and their lengths.
  308. BridgeDirection& c = candidates[i_angle];
  309. std::vector<coordf_t> dist_anchored;
  310. {
  311. Lines clipped_lines = intersection_ln(lines, clip_area);
  312. for (size_t i = 0; i < clipped_lines.size(); ++i) {
  313. const Line& line = clipped_lines[i];
  314. if (expolygons_contain(this->_anchor_regions, line.a) || expolygons_contain(this->_anchor_regions, line.b)) {
  315. // This line has one anchor (or is totally anchored)
  316. coordf_t len = line.length();
  317. //store stats
  318. c.total_length_anchored += len;
  319. c.max_length_anchored = std::max(c.max_length_anchored, len);
  320. c.nb_lines_anchored++;
  321. dist_anchored.push_back(len);
  322. } else {
  323. // this line could NOT be anchored.
  324. coordf_t len = line.length();
  325. c.total_length_free += len;
  326. c.max_length_free = std::max(c.max_length_free, len);
  327. c.nb_lines_free++;
  328. }
  329. }
  330. }
  331. if (c.total_length_anchored == 0. || c.nb_lines_anchored == 0) {
  332. continue;
  333. } else {
  334. have_coverage = true;
  335. // compute median
  336. if (!dist_anchored.empty()) {
  337. std::sort(dist_anchored.begin(), dist_anchored.end());
  338. c.median_length_anchor = dist_anchored[dist_anchored.size() / 2];
  339. }
  340. // size is 20%
  341. }
  342. }
  343. }
  344. // if no direction produced coverage, then there's no bridge direction
  345. if (!have_coverage)
  346. return false;
  347. //compute global stat (max & min median & max length)
  348. std::vector<coordf_t> all_median_length;
  349. std::vector<coordf_t> all_max_length;
  350. for (BridgeDirection &c : candidates) {
  351. all_median_length.push_back(c.median_length_anchor);
  352. all_max_length.push_back(c.max_length_anchored);
  353. }
  354. std::sort(all_median_length.begin(), all_median_length.end());
  355. std::sort(all_max_length.begin(), all_max_length.end());
  356. coordf_t median_max_length = all_max_length[all_max_length.size() / 2];
  357. coordf_t min_max_length = all_max_length.front();
  358. coordf_t max_max_length = all_max_length.back();
  359. coordf_t median_median_length = all_median_length[all_median_length.size() / 2];
  360. coordf_t min_median_length = all_median_length.front();
  361. coordf_t max_median_length = all_median_length.back();
  362. //compute individual score
  363. for (BridgeDirection& c : candidates) {
  364. c.coverage = 0;
  365. //ratio_anchored is 70% of the score
  366. double ratio_anchored = c.total_length_anchored / (c.total_length_anchored + c.total_length_free);
  367. c.coverage = 70 * ratio_anchored;
  368. //median is 15% (and need to invert it)
  369. double ratio_median = 1 - double(c.median_length_anchor - min_median_length) / (double)std::max(1., max_median_length - min_median_length);
  370. c.coverage += 15 * ratio_median;
  371. //max is 15 % (and need to invert it)
  372. double ratio_max = 1 - double(c.max_length_anchored - min_max_length) / (double)std::max(1., max_max_length - min_max_length);
  373. c.coverage += 15 * ratio_max;
  374. //bonus for perimeter dir
  375. if (c.along_perimeter_length > 0)
  376. c.coverage += 5;
  377. }
  378. // if any other direction is within extrusion width of coverage, prefer it if shorter
  379. // shorter = shorter max length, or if in espilon (10) range, the shorter mean length.
  380. // TODO: There are two options here - within width of the angle with most coverage, or within width of the currently perferred?
  381. size_t i_best = 0;
  382. for (size_t i = 1; i < candidates.size(); ++ i)
  383. if (candidates[i].coverage > candidates[i_best].coverage)
  384. i_best = i;
  385. else if (candidates[i].coverage > candidates[i_best].coverage)
  386. i_best = i;
  387. this->angle = candidates[i_best].angle;
  388. if (this->angle >= PI)
  389. this->angle -= PI;
  390. #ifdef SLIC3R_DEBUG
  391. printf(" Optimal infill angle is %d degrees\n", (int)Slic3r::Geometry::rad2deg(this->angle));
  392. #endif
  393. return true;
  394. }
  395. std::vector<BridgeDetector::BridgeDirection> BridgeDetector::bridge_direction_candidates(bool only_from_polygon) const
  396. {
  397. std::vector<BridgeDirection> angles;
  398. // we test angles according to configured resolution
  399. if (!only_from_polygon)
  400. for (int i = 0; i <= PI/this->resolution; ++i)
  401. angles.emplace_back(i * this->resolution);
  402. // we also test angles of each bridge contour
  403. {
  404. Lines lines = to_lines(this->expolygons);
  405. //if many lines, only takes the bigger ones.
  406. float mean_sqr_size = 0;
  407. if (lines.size() > 200) {
  408. for (int i = 0; i < 200; i++) {
  409. mean_sqr_size += (float)lines[i].a.distance_to_square(lines[i].b);
  410. }
  411. mean_sqr_size /= 200;
  412. for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
  413. float dist_sqr = line->a.distance_to_square(line->b);
  414. if (dist_sqr > mean_sqr_size)
  415. angles.emplace_back(line->direction(), dist_sqr);
  416. }
  417. }else
  418. for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line)
  419. angles.emplace_back(line->direction(), line->a.distance_to_square(line->b));
  420. }
  421. /* we also test angles of each open supporting edge
  422. (this finds the optimal angle for C-shaped supports) */
  423. for (const Polyline &edge : this->_edges)
  424. if (edge.first_point() != edge.last_point())
  425. angles.emplace_back(Line(edge.first_point(), edge.last_point()).direction());
  426. // remove duplicates
  427. std::sort(angles.begin(), angles.end(), [](const BridgeDirection& bt1, const BridgeDirection& bt2) { return bt1.angle < bt2.angle; });
  428. //first delete angles too close to an angle from a perimeter
  429. for (size_t i = 1; i < angles.size(); ++i) {
  430. if (angles[i - 1].along_perimeter_length > 0 && angles[i].along_perimeter_length == 0)
  431. if (Slic3r::Geometry::directions_parallel(angles[i].angle, angles[i - 1].angle, this->resolution)) {
  432. angles.erase(angles.begin() + i);
  433. --i;
  434. continue;
  435. }
  436. if (angles[i].along_perimeter_length > 0 && angles[i - 1].along_perimeter_length == 0)
  437. if (Slic3r::Geometry::directions_parallel(angles[i].angle, angles[i - 1].angle, this->resolution)) {
  438. angles.erase(angles.begin() + (i-1));
  439. --i;
  440. continue;
  441. }
  442. }
  443. //then delete angle to close to each other (high resolution)
  444. double min_resolution = this->resolution / 8;
  445. for (size_t i = 1; i < angles.size(); ++i) {
  446. if (Slic3r::Geometry::directions_parallel(angles[i].angle, angles[i - 1].angle, min_resolution)) {
  447. // keep the longest of the two.
  448. if (angles[i].along_perimeter_length < angles[i - 1].along_perimeter_length) {
  449. angles.erase(angles.begin() + i);
  450. --i;
  451. } else {
  452. angles.erase(angles.begin() + (i-1));
  453. --i;
  454. }
  455. }
  456. }
  457. //then, if too much angles, delete more
  458. while (angles.size() > 200) {
  459. min_resolution *= 2;
  460. for (size_t i = 1; i < angles.size(); ++i) {
  461. if (Slic3r::Geometry::directions_parallel(angles[i].angle, angles[i - 1].angle, min_resolution)) {
  462. // keep the longest of the two.
  463. if (angles[i].along_perimeter_length < angles[i - 1].along_perimeter_length) {
  464. angles.erase(angles.begin() + i);
  465. --i;
  466. } else {
  467. angles.erase(angles.begin() + (i - 1));
  468. --i;
  469. }
  470. }
  471. }
  472. }
  473. /* compare first value with last one and remove the greatest one (PI)
  474. in case they are parallel (PI, 0) */
  475. if (angles.size() > 1 && Slic3r::Geometry::directions_parallel(angles.front().angle, angles.back().angle, min_resolution))
  476. angles.pop_back();
  477. return angles;
  478. }
  479. /*
  480. static void get_trapezoids(const ExPolygon &expoly, Polygons* polygons) const
  481. {
  482. ExPolygons expp;
  483. expp.push_back(expoly);
  484. boost::polygon::get_trapezoids(*polygons, expp);
  485. }
  486. void ExPolygon::get_trapezoids(ExPolygon clone, Polygons* polygons, double angle) const
  487. {
  488. clone.rotate(PI/2 - angle, Point(0,0));
  489. clone.get_trapezoids(polygons);
  490. for (Polygons::iterator polygon = polygons->begin(); polygon != polygons->end(); ++polygon)
  491. polygon->rotate(-(PI/2 - angle), Point(0,0));
  492. }
  493. */
  494. void get_lines(const ExPolygon& expoly, std::vector<Line> &lines, coord_t spacing, int layer_id, ExPolygons anchorage)
  495. {
  496. // get all points of this ExPolygon
  497. Points pp = to_points(expoly);
  498. if (pp.empty()) return;
  499. // build our bounding box
  500. BoundingBox bb(pp);
  501. // get all x coordinates
  502. coord_t min_x = pp[0].x(), max_x = pp[0].x();
  503. std::vector<coord_t> xx;
  504. for (Points::const_iterator p = pp.begin(); p != pp.end(); ++p) {
  505. if (min_x > p->x()) min_x = p->x();
  506. if (max_x < p->x()) max_x = p->x();
  507. }
  508. for (coord_t x = min_x; x < max_x - (spacing / 2); x += spacing) {
  509. xx.push_back(x);
  510. }
  511. xx.push_back(max_x);
  512. //std::sort(xx.begin(), xx.end());
  513. // find trapezoids by looping from first to next-to-last coordinate
  514. coord_t prev_x = xx.front() - SCALED_EPSILON;
  515. for (std::vector<coord_t>::const_iterator x = xx.begin(); x != xx.end(); ++x) {
  516. if (*x == prev_x) continue;
  517. prev_x = *x;
  518. lines.emplace_back(Point(*x, bb.min(1) - spacing / 2), Point(*x, bb.max(1) + spacing / 2));
  519. assert(lines.back().a.x() == lines.back().b.x());
  520. assert(lines.back().a.y() < lines.back().b.y());
  521. }
  522. }
  523. Polygons BridgeDetector::coverage(double angle) const
  524. {
  525. if (angle == -1)
  526. angle = this->angle;
  527. Polygons covered;
  528. const coord_t covered_offset = this->precision / 2 + SCALED_EPSILON / 2;
  529. if (angle != -1) {
  530. // Get anchors, convert them to Polygons and rotate them.
  531. ExPolygons anchors = this->_anchor_regions;
  532. expolygons_rotate(anchors, PI / 2.0 - angle);
  533. for (ExPolygon unsupported : this->expolygons) {
  534. // Clone our expolygon and rotate it so that we work with vertical lines.
  535. unsupported.rotate(PI / 2.0 - angle);
  536. // Outset the bridge expolygon by half the amount we used for detecting anchors;
  537. // we'll use this one to generate our trapezoids and be sure that their vertices
  538. // are inside the anchors and not on their contours leading to false negatives.
  539. ExPolygons unsupported_bigger = offset_ex(unsupported, 0.5f * float(this->spacing));
  540. assert(unsupported_bigger.size() == 1); // growing don't split
  541. ExPolygons small_anchors = intersection_ex({unsupported_bigger.front()}, anchors);
  542. unsupported_bigger = small_anchors;
  543. unsupported_bigger.push_back(unsupported);
  544. unsupported_bigger = union_safety_offset_ex(unsupported_bigger);
  545. // now unsupported_bigger is unsupported but with a little extra inside the anchors
  546. // clean it up if needed (remove bits unlinked to 'unsupported'
  547. if (unsupported_bigger.size() > 1) {
  548. double biggest_area = 0;
  549. for (auto it = unsupported_bigger.begin(); it != unsupported_bigger.end(); ++it) {
  550. biggest_area = std::max(biggest_area, it->area());
  551. }
  552. auto it = unsupported_bigger.begin();
  553. while (it != unsupported_bigger.end()) {
  554. if (it->area() >= biggest_area - 1) {
  555. ++it;
  556. } else {
  557. it = unsupported_bigger.erase(it);
  558. }
  559. }
  560. }
  561. assert(unsupported_bigger.size() == 1);
  562. {
  563. std::vector<Line> support_lines;
  564. get_lines(unsupported_bigger.front(), support_lines, this->precision, layer_id, anchors);
  565. std::vector<Lines> lines_checked;
  566. for (Line &line : support_lines) {
  567. lines_checked.emplace_back();
  568. // intersection to have the printable lines
  569. Polylines pls = intersection_pl({Polyline{line.a, line.b}}, unsupported_bigger);
  570. for (Polyline &pl : pls) {
  571. // you can't add point with a cut
  572. assert(pl.size() == 2);
  573. // check if the line is anchored
  574. bool has_a = false, has_b = false;
  575. for (ExPolygon anchor : anchors) {
  576. has_a = has_a || anchor.contains(pl.front());
  577. has_b = has_b || anchor.contains(pl.back());
  578. }
  579. // not both in anchor: bad. discard.
  580. if (!has_a || !has_b)
  581. continue;
  582. lines_checked.back().emplace_back(pl.front(), pl.back());
  583. }
  584. }
  585. assert(lines_checked.size() == support_lines.size());
  586. // create polygons inflated by covered_offset from good lines
  587. for (Lines &lines : lines_checked) {
  588. Polygon p;
  589. for (Line &l : lines) {
  590. covered.emplace_back(Points{Point(l.a.x() - covered_offset, l.a.y() - covered_offset),
  591. Point(l.a.x() - covered_offset, l.b.y() + covered_offset),
  592. Point(l.a.x() + covered_offset, l.b.y() + covered_offset),
  593. Point(l.a.x() + covered_offset, l.a.y() - covered_offset)});
  594. }
  595. }
  596. }
  597. }
  598. // Unite the polygons created from lines
  599. covered = union_(covered);
  600. // unoffset the polygons, so it doesn't expand into un-printable areas
  601. covered = offset(covered, -covered_offset);
  602. // Intersect trapezoids with actual bridge area to remove extra margins and append it to result.
  603. polygons_rotate(covered, -(PI / 2.0 - angle));
  604. }
  605. return covered;
  606. }
  607. /* This method returns the bridge edges (as polylines) that are not supported
  608. but would allow the entire bridge area to be bridged with detected angle
  609. if supported too */
  610. void BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const
  611. {
  612. if (angle == -1) angle = this->angle;
  613. if (angle == -1) return;
  614. Polygons grown_lower = offset(this->lower_slices, float(this->spacing));
  615. for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly) {
  616. // get unsupported bridge edges (both contour and holes)
  617. Lines unsupported_lines = to_lines(diff_pl(to_polylines(*it_expoly), grown_lower));
  618. /* Split into individual segments and filter out edges parallel to the bridging angle
  619. TODO: angle tolerance should probably be based on segment length and flow width,
  620. so that we build supports whenever there's a chance that at least one or two bridge
  621. extrusions would be anchored within such length (i.e. a slightly non-parallel bridging
  622. direction might still benefit from anchors if long enough)
  623. double angle_tolerance = PI / 180.0 * 5.0; */
  624. for (const Line &line : unsupported_lines)
  625. if (! Slic3r::Geometry::directions_parallel(line.direction(), angle)) {
  626. unsupported->emplace_back(Polyline());
  627. unsupported->back().points.emplace_back(line.a);
  628. unsupported->back().points.emplace_back(line.b);
  629. }
  630. }
  631. /*
  632. if (0) {
  633. require "Slic3r/SVG.pm";
  634. Slic3r::SVG::output(
  635. "unsupported_" . rad2deg($angle) . ".svg",
  636. expolygons => [$self->expolygon],
  637. green_expolygons => $self->_anchor_regions,
  638. red_expolygons => union_ex($grown_lower),
  639. no_arrows => 1,
  640. polylines => \@bridge_edges,
  641. red_polylines => $unsupported,
  642. );
  643. }
  644. */
  645. }
  646. Polylines BridgeDetector::unsupported_edges(double angle) const {
  647. Polylines pp;
  648. this->unsupported_edges(angle, &pp);
  649. return pp;
  650. }
  651. }