diff --git a/xs/src/libslic3r/BridgeDetector.cpp b/xs/src/libslic3r/BridgeDetector.cpp index f9061e405..228f9a4e6 100644 --- a/xs/src/libslic3r/BridgeDetector.cpp +++ b/xs/src/libslic3r/BridgeDetector.cpp @@ -5,24 +5,6 @@ namespace Slic3r { -class BridgeDirectionComparator { - public: - std::map dir_coverage; // angle => score - - BridgeDirectionComparator(double _extrusion_width) - : extrusion_width(_extrusion_width) - {}; - - // the best direction is the one causing most lines to be bridged (thus most coverage) - bool operator() (double a, double b) { - // Initial sort by coverage only - comparator must obey strict weak ordering - return (this->dir_coverage[a] > this->dir_coverage[b]); - }; - - private: - double extrusion_width; -}; - BridgeDetector::BridgeDetector(const ExPolygon &_expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width) : expolygon(_expolygon), lower_slices(_lower_slices), extrusion_width(_extrusion_width), @@ -59,6 +41,8 @@ BridgeDetector::BridgeDetector(const ExPolygon &_expolygon, const ExPolygonColle bool BridgeDetector::detect_angle() { + // Do nothing if the bridging region is completely in the air + // and there are no anchors available at the layer below. if (this->_edges.empty() || this->_anchors.empty()) return false; /* Outset the bridge expolygon by half the amount we used for detecting anchors; @@ -70,60 +54,65 @@ BridgeDetector::detect_angle() bridge in several directions and then sum the length of lines having both endpoints within anchors */ - // we test angles according to configured resolution - std::vector angles; - for (int i = 0; i <= PI/this->resolution; ++i) - angles.push_back(i * this->resolution); - - // we also test angles of each bridge contour + // generate the list of candidate angles + std::vector candidates; { - Polygons pp = this->expolygon; - for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) { - Lines lines = p->lines(); - for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) - angles.push_back(line->direction()); + // we test angles according to configured resolution + std::vector angles; + for (int i = 0; i <= PI/this->resolution; ++i) + angles.push_back(i * this->resolution); + + // we also test angles of each bridge contour + { + Polygons pp = this->expolygon; + for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) { + Lines lines = p->lines(); + for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) + angles.push_back(line->direction()); + } } - } - /* we also test angles of each open supporting edge - (this finds the optimal angle for C-shaped supports) */ - for (Polylines::const_iterator edge = this->_edges.begin(); edge != this->_edges.end(); ++edge) { - if (edge->first_point().coincides_with(edge->last_point())) continue; - angles.push_back(Line(edge->first_point(), edge->last_point()).direction()); - } - - // remove duplicates - double min_resolution = PI/180.0; // 1 degree - std::sort(angles.begin(), angles.end()); - for (size_t i = 1; i < angles.size(); ++i) { - if (Slic3r::Geometry::directions_parallel(angles[i], angles[i-1], min_resolution)) { - angles.erase(angles.begin() + i); - --i; + /* we also test angles of each open supporting edge + (this finds the optimal angle for C-shaped supports) */ + for (Polylines::const_iterator edge = this->_edges.begin(); edge != this->_edges.end(); ++edge) { + if (edge->first_point().coincides_with(edge->last_point())) continue; + angles.push_back(Line(edge->first_point(), edge->last_point()).direction()); } - } - /* compare first value with last one and remove the greatest one (PI) - in case they are parallel (PI, 0) */ - if (Slic3r::Geometry::directions_parallel(angles.front(), angles.back(), min_resolution)) - angles.pop_back(); - BridgeDirectionComparator bdcomp(this->extrusion_width); - std::map dir_avg_length; + // remove duplicates + double min_resolution = PI/180.0; // 1 degree + std::sort(angles.begin(), angles.end()); + for (size_t i = 1; i < angles.size(); ++i) { + if (Slic3r::Geometry::directions_parallel(angles[i], angles[i-1], min_resolution)) { + angles.erase(angles.begin() + i); + --i; + } + } + /* compare first value with last one and remove the greatest one (PI) + in case they are parallel (PI, 0) */ + if (Slic3r::Geometry::directions_parallel(angles.front(), angles.back(), min_resolution)) + angles.pop_back(); + + for (auto angle : angles) + candidates.push_back(BridgeDirection(angle)); + } + double line_increment = this->extrusion_width; bool have_coverage = false; - for (std::vector::const_iterator angle = angles.begin(); angle != angles.end(); ++angle) { + for (BridgeDirection &candidate : candidates) { Polygons my_clip_area = clip_area; ExPolygons my_anchors = this->_anchors; // rotate everything - the center point doesn't matter - for (Polygons::iterator it = my_clip_area.begin(); it != my_clip_area.end(); ++it) - it->rotate(-*angle, Point(0,0)); - for (ExPolygons::iterator it = my_anchors.begin(); it != my_anchors.end(); ++it) - it->rotate(-*angle, Point(0,0)); + for (Polygon &p : my_clip_area) + p.rotate(-candidate.angle, Point(0,0)); + for (ExPolygon &e : my_anchors) + e.rotate(-candidate.angle, Point(0,0)); // generate lines in this direction BoundingBox bb; - for (ExPolygons::const_iterator it = my_anchors.begin(); it != my_anchors.end(); ++it) - bb.merge((Points)*it); + for (const ExPolygon &e : my_anchors) + bb.merge((Points)e); Lines lines; for (coord_t y = bb.min.y; y <= bb.max.y; y += line_increment) @@ -143,44 +132,39 @@ BridgeDetector::detect_angle() std::vector lengths; double total_length = 0; - for (Lines::const_iterator line = clipped_lines.begin(); line != clipped_lines.end(); ++line) { - double len = line->length(); + for (const Line &line : clipped_lines) { + const double len = line.length(); lengths.push_back(len); total_length += len; } if (total_length) have_coverage = true; // sum length of bridged lines - bdcomp.dir_coverage[*angle] = total_length; + candidate.coverage = total_length; /* The following produces more correct results in some cases and more broken in others. TODO: investigate, as it looks more reliable than line clipping. */ // $directions_coverage{$angle} = sum(map $_->area, @{$self->coverage($angle)}) // 0; // max length of bridged lines - dir_avg_length[*angle] = !lengths.empty() - ? *std::max_element(lengths.begin(), lengths.end()) - : 0; + if (!lengths.empty()) + candidate.max_length = *std::max_element(lengths.begin(), lengths.end()); } // if no direction produced coverage, then there's no bridge direction if (!have_coverage) return false; // sort directions by coverage - most coverage first - std::sort(angles.begin(), angles.end(), bdcomp); - this->angle = angles.front(); + std::sort(candidates.begin(), candidates.end()); // if any other direction is within extrusion width of coverage, prefer it if shorter // TODO: There are two options here - within width of the angle with most coverage, or within width of the currently perferred? - double most_coverage_angle = this->angle; - for (std::vector::const_iterator angle = angles.begin() + 1; - angle != angles.end() && bdcomp.dir_coverage[most_coverage_angle] - bdcomp.dir_coverage[*angle] < this->extrusion_width; - ++angle - ) { - if (dir_avg_length[*angle] < dir_avg_length[this->angle]) { - this->angle = *angle; - } - } + size_t i_best = 0; + for (size_t i = 1; i < candidates.size() && candidates[i_best].coverage - candidates[i].coverage < this->extrusion_width; ++ i) + if (candidates[i].max_length < candidates[i_best].max_length) + i_best = i; + + this->angle = candidates[i_best].angle; if (this->angle >= PI) this->angle -= PI; diff --git a/xs/src/libslic3r/BridgeDetector.hpp b/xs/src/libslic3r/BridgeDetector.hpp index bbbd26df9..3ba054f5d 100644 --- a/xs/src/libslic3r/BridgeDetector.hpp +++ b/xs/src/libslic3r/BridgeDetector.hpp @@ -31,6 +31,19 @@ private: Polylines _edges; // Closed polygons representing the supporting areas. ExPolygons _anchors; + + class BridgeDirection { + public: + BridgeDirection(double a = -1.) : angle(a), coverage(0.), max_length(0.) {} + // the best direction is the one causing most lines to be bridged (thus most coverage) + bool operator<(const BridgeDirection &other) const { + // Initial sort by coverage only - comparator must obey strict weak ordering + return this->coverage > other.coverage; + }; + double angle; + double coverage; + double max_length; + }; }; }