diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index da584fe62..277ac7824 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -31,6 +31,9 @@ public: virtual void reverse() = 0; virtual const Point& first_point() const = 0; virtual const Point& last_point() const = 0; + // Returns an approximately middle point of a path, loop or an extrusion collection. + // Used to get a sample point of an extrusion or extrusion collection, which is possibly deep inside its island. + virtual const Point& middle_point() const = 0; // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. virtual void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const = 0; @@ -81,6 +84,7 @@ public: void reverse() override { this->polyline.reverse(); } const Point& first_point() const override { return this->polyline.points.front(); } const Point& last_point() const override { return this->polyline.points.back(); } + const Point& middle_point() const override { return this->polyline.points[this->polyline.size() / 2]; } size_t size() const { return this->polyline.size(); } bool empty() const { return this->polyline.empty(); } bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); } @@ -153,6 +157,7 @@ public: void reverse() override; const Point& first_point() const override { return this->paths.front().polyline.points.front(); } const Point& last_point() const override { return this->paths.back().polyline.points.back(); } + const Point& middle_point() const override { auto &path = this->paths[this->paths.size() / 2]; return path.polyline.points[path.polyline.size() / 2]; } size_t size() const { return this->paths.size(); } bool empty() const { return this->paths.empty(); } double length() const override; @@ -204,6 +209,7 @@ public: void reverse() override; const Point& first_point() const override { return this->paths.front().polyline.points.front(); } const Point& last_point() const override { assert(this->first_point() == this->paths.back().polyline.points.back()); return this->first_point(); } + const Point& middle_point() const override { auto& path = this->paths[this->paths.size() / 2]; return path.polyline.points[path.polyline.size() / 2]; } Polygon polygon() const; double length() const override; bool split_at_vertex(const Point &point, const double scaled_epsilon = scaled(0.001)); diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index 0c029a1d5..676bdd891 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -102,6 +102,7 @@ public: void reverse() override; const Point& first_point() const override { return this->entities.front()->first_point(); } const Point& last_point() const override { return this->entities.back()->last_point(); } + const Point& middle_point() const override { return this->entities[this->entities.size() / 2]->middle_point(); } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override; diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 36716dde9..f86dd5635 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -578,7 +578,7 @@ void Layer::sort_perimeters_into_islands( // Take a sample deep inside its island if available. Infills are usually quite far from the island boundary. for (uint32_t iexpoly : fill_expolygons_ranges[islice]) if (const ExPolygon &expoly = fill_expolygons[iexpoly]; ! expoly.empty()) { - sample = expoly.contour.points.front(); + sample = expoly.contour.points[expoly.contour.points.size() / 2]; sample_set = true; break; } @@ -589,12 +589,12 @@ void Layer::sort_perimeters_into_islands( if (ee.is_collection()) { for (const ExtrusionEntity *ee2 : dynamic_cast(ee).entities) if (! ee2->role().is_external()) { - sample = ee2->first_point(); + sample = ee2->middle_point(); sample_set = true; goto loop_end; } } else if (! ee.role().is_external()) { - sample = ee.first_point(); + sample = ee.middle_point(); sample_set = true; break; } @@ -603,12 +603,12 @@ void Layer::sort_perimeters_into_islands( if (! sample_set) { if (! extrusions.second.empty()) { // If there is no inner perimeter, take a sample of some gap fill extrusion. - sample = this_layer_region.thin_fills().entities[*extrusions.second.begin()]->first_point(); + sample = this_layer_region.thin_fills().entities[*extrusions.second.begin()]->middle_point(); sample_set = true; } if (! sample_set && ! extrusions.first.empty()) { // As a last resort, take a sample of some external perimeter. - sample = this_layer_region.perimeters().entities[*extrusions.first.begin()]->first_point(); + sample = this_layer_region.perimeters().entities[*extrusions.first.begin()]->middle_point(); sample_set = true; } } @@ -835,6 +835,15 @@ void Layer::sort_perimeters_into_islands( d2min = d2; lslice_idx_min = lslice_idx; } + if (lslice_idx_min == -1) { + // This should not happen, but Arachne seems to produce a perimeter point far outside its source contour. + // As a last resort, find the closest source contours to the sample point. + for (int lslice_idx = int(lslices_ex.size()) - 1; lslice_idx >= 0; -- lslice_idx) + if (double d2 = (lslices[lslice_idx].point_projection(it_source_slice->second) - it_source_slice->second).cast().squaredNorm(); d2 < d2min) { + d2min = d2; + lslice_idx_min = lslice_idx; + } + } assert(lslice_idx_min != -1); insert_into_island(lslice_idx_min, it_source_slice->first); }