From a60631b7aa1ce28b5c90518a00b8996ba6024130 Mon Sep 17 00:00:00 2001 From: supermerill Date: Fri, 19 Jun 2020 15:30:56 +0200 Subject: [PATCH] thin walls: merge path into external perimeter loop when possible. --- resources/ui_layout/print.ui | 1 + src/libslic3r/MedialAxis.cpp | 11 +- src/libslic3r/PerimeterGenerator.cpp | 172 ++++++++++++++++++++++++++- src/libslic3r/PerimeterGenerator.hpp | 6 + src/libslic3r/PrintConfig.cpp | 9 ++ src/libslic3r/PrintConfig.hpp | 2 + src/slic3r/GUI/Preset.cpp | 1 + 7 files changed, 196 insertions(+), 6 deletions(-) diff --git a/resources/ui_layout/print.ui b/resources/ui_layout/print.ui index b53751454..2761c5a52 100644 --- a/resources/ui_layout/print.ui +++ b/resources/ui_layout/print.ui @@ -34,6 +34,7 @@ group:Quality setting:thin_walls setting:width$5:thin_walls_min_width setting:width$5:thin_walls_overlap + setting:thin_walls_merge end_line group:Overhangs line:On perimeters diff --git a/src/libslic3r/MedialAxis.cpp b/src/libslic3r/MedialAxis.cpp index 41f8f8d2d..b66160416 100644 --- a/src/libslic3r/MedialAxis.cpp +++ b/src/libslic3r/MedialAxis.cpp @@ -1901,10 +1901,13 @@ thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow fl if (paths.front().first_point().coincides_with(paths.back().last_point())) { coll.append(ExtrusionLoop(paths)); } else { - //not a loop : avoid to "sort" it. - ExtrusionEntityCollection unsortable_coll(paths); - unsortable_coll.no_sort = true; - coll.append(unsortable_coll); + if (role == erThinWall){ + //thin walls : avoid to cut them, please. + ExtrusionEntityCollection unsortable_coll(paths); + unsortable_coll.no_sort = true; + coll.append(unsortable_coll); + }else //gap fill : cut them as much as you want + coll.append(paths); } } } diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index cee03371f..b35f020f0 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -717,7 +717,12 @@ void PerimeterGenerator::process() thin_walls.clear(); } } else { - entities = this->_traverse_loops(contours.front(), thin_walls); + if (this->object_config->thin_walls_merge) { + entities = this->_traverse_loops(contours.front(), ThickPolylines{}); + _merge_thin_walls(entities, thin_walls); + } else { + entities = this->_traverse_loops(contours.front(), thin_walls); + } } @@ -917,7 +922,6 @@ void PerimeterGenerator::process() } // for each island } - ExtrusionEntityCollection PerimeterGenerator::_traverse_loops( const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls) const { @@ -1056,6 +1060,170 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops( return coll_out; } +void PerimeterGenerator::_merge_thin_walls(ExtrusionEntityCollection &extrusions, ThickPolylines &thin_walls) const { + class ChangeFlow : public ExtrusionVisitor { + public: + float percent_extrusion; + std::vector paths; + virtual void use(ExtrusionPath &path) override { + path.mm3_per_mm *= percent_extrusion; + path.width *= percent_extrusion; + paths.emplace_back(std::move(path)); + } + virtual void use(ExtrusionPath3D &path3D) override { /*shouldn't happen*/ } + virtual void use(ExtrusionMultiPath &multipath) override { /*shouldn't happen*/ } + virtual void use(ExtrusionMultiPath3D &multipath) { /*shouldn't happen*/ } + virtual void use(ExtrusionLoop &loop) override { + for (ExtrusionPath &path : loop.paths) + this->use(path); + } + virtual void use(ExtrusionEntityCollection &collection) override { + for (ExtrusionEntity *entity : collection.entities) + entity->visit(*this); + } + }; + struct BestPoint { + //Point p; + ExtrusionPath *path; + size_t idx_path; + ExtrusionLoop *loop; + size_t idx_line; + Line line; + double dist; + bool from_start; + }; + //use a visitor to found the best point. + class SearchBestPoint : public ExtrusionVisitor { + public: + ThickPolyline* thin_wall; + BestPoint search_result; + size_t idx_path; + ExtrusionLoop *current_loop = nullptr; + virtual void use(ExtrusionPath &path) override { + //don't consider other thin walls. + if (path.role() == erThinWall) return; + //for each segment + Lines lines = path.polyline.lines(); + for (size_t idx_line = 0; idx_line < lines.size(); idx_line++) { + //look for nearest point + double dist = lines[idx_line].distance_to_squared(thin_wall->points.front()); + if (dist < search_result.dist) { + search_result.path = &path; + search_result.idx_path = idx_path; + search_result.idx_line = idx_line; + search_result.line = lines[idx_line]; + search_result.dist = dist; + search_result.from_start = true; + search_result.loop = current_loop; + } + dist = lines[idx_line].distance_to_squared(thin_wall->points.back()); + if (dist < search_result.dist) { + search_result.path = &path; + search_result.idx_path = idx_path; + search_result.idx_line = idx_line; + search_result.line = lines[idx_line]; + search_result.dist = dist; + search_result.from_start = false; + search_result.loop = current_loop; + } + } + } + virtual void use(ExtrusionPath3D &path3D) override { /*shouldn't happen*/ } + virtual void use(ExtrusionMultiPath &multipath) override { /*shouldn't happen*/ } + virtual void use(ExtrusionMultiPath3D &multipath) { /*shouldn't happen*/ } + virtual void use(ExtrusionLoop &loop) override { + ExtrusionLoop * last_loop = current_loop; + current_loop = &loop; + //for each extrusion path + idx_path = 0; + for (ExtrusionPath &path : loop.paths) { + this->use(path); + idx_path++; + } + current_loop = last_loop; + } + virtual void use(ExtrusionEntityCollection &collection) override { + collection.no_sort = false; + //for each loop? (or other collections) + for (ExtrusionEntity *entity : collection.entities) + entity->visit(*this); + } + }; + //max dist to branch: ~half external periemeter width + coord_t max_width = this->ext_perimeter_flow.scaled_width(); + SearchBestPoint searcher; + ThickPolylines not_added; + //search the best extusion/point to branch into + //for each thin wall + int idx = 0; + for (ThickPolyline &tw : thin_walls) { + searcher.thin_wall = &tw; + searcher.search_result.dist = max_width; + searcher.search_result.dist *= searcher.search_result.dist; + searcher.search_result.path = nullptr; + searcher.use(extrusions); + idx++; + //now insert thin wall if it has a point + //it found a segment + if (searcher.search_result.path != nullptr) { + if (!searcher.search_result.from_start) + tw.reverse(); + //get the point + Point point = tw.points.front().projection_onto(searcher.search_result.line); + //we have to create 3 paths: 1: thinwall extusion, 2: thinwall return, 3: end of the path + //create new path : end of the path + Polyline poly_after; + poly_after.points.push_back(point); + poly_after.points.insert(poly_after.points.end(), + searcher.search_result.path->polyline.points.begin() + searcher.search_result.idx_line + 1, + searcher.search_result.path->polyline.points.end()); + searcher.search_result.path->polyline.points.erase( + searcher.search_result.path->polyline.points.begin() + searcher.search_result.idx_line + 1, + searcher.search_result.path->polyline.points.end()); + searcher.search_result.loop->paths[searcher.search_result.idx_path].polyline.points.push_back(point); + searcher.search_result.loop->paths.insert(searcher.search_result.loop->paths.begin() + 1 + searcher.search_result.idx_path, + ExtrusionPath(poly_after, *searcher.search_result.path)); + //create thin wall path exttrusion + ExtrusionEntityCollection tws = thin_variable_width({ tw }, erThinWall, this->ext_perimeter_flow); + ChangeFlow change_flow; + if (tws.entities.size() == 1 && tws.entities[0]->is_loop()) { + //loop, just add it + change_flow.percent_extrusion = 1; + change_flow.use(tws); + //add move back + + searcher.search_result.loop->paths.insert(searcher.search_result.loop->paths.begin() + 1 + searcher.search_result.idx_path, + change_flow.paths.begin(), change_flow.paths.end()); + //add move to + + } else { + //first add the return path + ExtrusionEntityCollection tws_second = tws; + tws_second.reverse(); + change_flow.percent_extrusion = 0.1; + change_flow.use(tws_second); + for (ExtrusionPath &path : change_flow.paths) + path.reverse(); + //std::reverse(change_flow.paths.begin(), change_flow.paths.end()); + searcher.search_result.loop->paths.insert(searcher.search_result.loop->paths.begin() + 1 + searcher.search_result.idx_path, + change_flow.paths.begin(), change_flow.paths.end()); + //add the real extrusion path + change_flow.percent_extrusion = 0.9; + change_flow.paths = std::vector(); + change_flow.use(tws); + searcher.search_result.loop->paths.insert(searcher.search_result.loop->paths.begin() + 1 + searcher.search_result.idx_path, + change_flow.paths.begin(), change_flow.paths.end()); + } + } else { + not_added.push_back(tw); + } + } + + //now add thinwalls that have no anchor (make them reversable) + ExtrusionEntityCollection tws = thin_variable_width(not_added, erThinWall, this->ext_perimeter_flow); + extrusions.append(tws.entities); +} + PerimeterIntersectionPoint PerimeterGenerator::_get_nearest_point(const PerimeterGeneratorLoops &children, ExtrusionLoop &myPolylines, const coord_t dist_cut, const coord_t max_dist) const { //find best points of intersections diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp index 853f99176..363e4d492 100644 --- a/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -98,9 +98,15 @@ private: double _mm3_per_mm_overhang; Polygons _lower_slices_p; + // transform loops into ExtrusionEntityCollection, adding also thin walls into it. ExtrusionEntityCollection _traverse_loops(const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls) const; + // try to merge thin walls to a current periemter exrusion or just add it to the end of the list. + void _merge_thin_walls(ExtrusionEntityCollection &extrusions, ThickPolylines &thin_walls) const; + // like _traverse_loops but with merging all periemter into one continuous loop ExtrusionLoop _traverse_and_join_loops(const PerimeterGeneratorLoop &loop, const PerimeterGeneratorLoops &childs, const Point entryPoint) const; + // sub-function of _traverse_and_join_loops, transform a single loop as a cut extrusion to be merged with an other one. ExtrusionLoop _extrude_and_cut_loop(const PerimeterGeneratorLoop &loop, const Point entryPoint, const Line &direction = Line(Point(0,0),Point(0,0))) const; + // sub-function of _traverse_and_join_loops, find the good splot to cut a loop to be able to join it with an other one PerimeterIntersectionPoint _get_nearest_point(const PerimeterGeneratorLoops &children, ExtrusionLoop &myPolylines, const coord_t dist_cut, const coord_t max_dist) const; }; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 035411558..f91a54b31 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3174,6 +3174,15 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->set_default_value(new ConfigOptionFloatOrPercent(50, true)); + def = this->add("thin_walls_merge", coBool); + def->label = L("merging with perimeters"); + def->full_label = L("Thin wall merge"); + def->category = OptionCategory::perimeter; + def->tooltip = L("Allow the external periemter to merge the thin wals int he path. !!! IF you disable this setting, please explain me why (via help->report issue)" + " because I'm going to DELETE this setting next release, as i don't see why someone may want to disable it."); + def->mode = comExpert; + def->set_default_value(new ConfigOptionBool(true)); + def = this->add("thin_walls_speed", coFloat); def->label = L("Thin walls"); def->full_label = L("Thin walls speed"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 8467e5481..14bc04266 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -541,6 +541,7 @@ public: ConfigOptionInt support_material_threshold; ConfigOptionBool support_material_with_sheath; ConfigOptionFloatOrPercent support_material_xy_spacing; + ConfigOptionBool thin_walls_merge; ConfigOptionFloat xy_size_compensation; ConfigOptionFloat xy_inner_size_compensation; ConfigOptionBool wipe_into_objects; @@ -599,6 +600,7 @@ protected: OPT_PTR(support_material_xy_spacing); OPT_PTR(support_material_threshold); OPT_PTR(support_material_with_sheath); + OPT_PTR(thin_walls_merge); OPT_PTR(xy_size_compensation); OPT_PTR(xy_inner_size_compensation); OPT_PTR(wipe_into_objects); diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index afabac225..847dbdd26 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -542,6 +542,7 @@ const std::vector& Preset::print_options() "milling_post_process", "milling_extra_size", "milling_speed", + "thin_walls_merge", }; return s_opts; }