From 18f926cfc4b29487928508eb87e17869d902d9c4 Mon Sep 17 00:00:00 2001 From: supermerill Date: Mon, 9 Jul 2018 20:01:40 +0200 Subject: [PATCH] Don't print totally unsupported perimeters. - remove outer overhangs (via a intersection with convection hull) - add bridge-able area detection (reuse the BridgeDetector but it's a bit wonky with overhangs and not very smooth) - add gui switchs --- xs/src/libslic3r/BridgeDetector.cpp | 98 +++++++++++----- xs/src/libslic3r/BridgeDetector.hpp | 4 +- xs/src/libslic3r/ClipperUtils.hpp | 8 ++ xs/src/libslic3r/ExPolygon.cpp | 54 ++++++++- xs/src/libslic3r/ExPolygon.hpp | 1 + xs/src/libslic3r/PerimeterGenerator.cpp | 145 +++++++++++++++++++----- xs/src/libslic3r/PrintConfig.cpp | 24 +++- xs/src/libslic3r/PrintConfig.hpp | 6 + xs/src/libslic3r/PrintObject.cpp | 5 +- xs/src/slic3r/GUI/Preset.cpp | 3 +- xs/src/slic3r/GUI/Tab.cpp | 9 ++ 11 files changed, 291 insertions(+), 66 deletions(-) diff --git a/xs/src/libslic3r/BridgeDetector.cpp b/xs/src/libslic3r/BridgeDetector.cpp index a5272683f..663b7ceb0 100644 --- a/xs/src/libslic3r/BridgeDetector.cpp +++ b/xs/src/libslic3r/BridgeDetector.cpp @@ -203,8 +203,8 @@ std::vector BridgeDetector::bridge_direction_candidates() const return angles; } -Polygons BridgeDetector::coverage(double angle) const -{ +Polygons BridgeDetector::coverage(double angle, bool precise) const { + if (angle == -1) angle = this->angle; @@ -213,52 +213,92 @@ Polygons BridgeDetector::coverage(double angle) const if (angle != -1) { // Get anchors, convert them to Polygons and rotate them. Polygons anchors = to_polygons(this->_anchor_regions); - polygons_rotate(anchors, PI/2.0 - angle); - + polygons_rotate(anchors, PI / 2.0 - angle); + //same for region which do not need bridging + //Polygons supported_area = diff(this->lower_slices.expolygons, this->_anchor_regions, true); + //polygons_rotate(anchors, PI / 2.0 - angle); + for (ExPolygon expolygon : this->expolygons) { // Clone our expolygon and rotate it so that we work with vertical lines. - expolygon.rotate(PI/2.0 - angle); + expolygon.rotate(PI / 2.0 - angle); // Outset the bridge expolygon by half the amount we used for detecting anchors; // we'll use this one to generate our trapezoids and be sure that their vertices // are inside the anchors and not on their contours leading to false negatives. for (ExPolygon &expoly : offset_ex(expolygon, 0.5f * float(this->spacing))) { // Compute trapezoids according to a vertical orientation Polygons trapezoids; - expoly.get_trapezoids2(&trapezoids, PI/2.0); - for (const Polygon &trapezoid : trapezoids) { + if (!precise) expoly.get_trapezoids2(&trapezoids, PI / 2); + else expoly.get_trapezoids3_half(&trapezoids, float(this->spacing)); + for (Polygon &trapezoid : trapezoids) { // not nice, we need a more robust non-numeric check + // imporvment 1: take into account when we go in the supported area. size_t n_supported = 0; - for (const Line &supported_line : intersection_ln(trapezoid.lines(), anchors)) - if (supported_line.length() >= this->spacing) - ++ n_supported; - if (n_supported >= 2) + if (!precise) { + for (const Line &supported_line : intersection_ln(trapezoid.lines(), anchors)) + if (supported_line.length() >= this->spacing) + ++n_supported; + } else { + Polygons intersects = intersection(trapezoid, anchors); + n_supported = intersects.size(); + + if (n_supported >= 2) { + // trim it to not allow to go outside of the intersections + BoundingBox center_bound = intersects[0].bounding_box(); + coord_t min_y = center_bound.center().y, max_y = center_bound.center().y; + for (Polygon &poly_bound : intersects) { + center_bound = poly_bound.bounding_box(); + if (min_y > center_bound.center().y) min_y = center_bound.center().y; + if (max_y < center_bound.center().y) max_y = center_bound.center().y; + } + coord_t min_x = trapezoid[0].x, max_x = trapezoid[0].x; + for (Point &p : trapezoid.points) { + if (min_x > p.x) min_x = p.x; + if (max_x < p.x) max_x = p.x; + } + //add what get_trapezoids3 has removed (+EPSILON) + min_x -= (this->spacing / 4 + 1); + max_x += (this->spacing / 4 + 1); + coord_t mid_x = (min_x + max_x) / 2; + for (Point &p : trapezoid.points) { + if (p.y < min_y) p.y = min_y; + if (p.y > max_y) p.y = max_y; + if (p.x > min_x && p.x < mid_x) p.x = min_x; + if (p.x < max_x && p.x > mid_x) p.x = max_x; + } + } + } + + if (n_supported >= 2) { + //add it covered.push_back(std::move(trapezoid)); + } } } } - // Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids - // instead of exact overlaps. - covered = union_(covered); - // Intersect trapezoids with actual bridge area to remove extra margins and append it to result. - polygons_rotate(covered, -(PI/2.0 - angle)); - covered = intersection(covered, to_polygons(this->expolygons)); + // Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids + // instead of exact overlaps. + covered = union_(covered); + // Intersect trapezoids with actual bridge area to remove extra margins and append it to result. + polygons_rotate(covered, -(PI/2.0 - angle)); + covered = intersection(covered, to_polygons(this->expolygons)); #if 0 - { - my @lines = map @{$_->lines}, @$trapezoids; - $_->rotate(-(PI/2 - $angle), [0,0]) for @lines; - - require "Slic3r/SVG.pm"; - Slic3r::SVG::output( - "coverage_" . rad2deg($angle) . ".svg", - expolygons => [$self->expolygon], - green_expolygons => $self->_anchor_regions, - red_expolygons => $coverage, - lines => \@lines, - ); + { + my @lines = map @{$_->lines}, @$trapezoids; + $_->rotate(-(PI/2 - $angle), [0,0]) for @lines; + + require "Slic3r/SVG.pm"; + Slic3r::SVG::output( + "coverage_" . rad2deg($angle) . ".svg", + expolygons => [$self->expolygon], + green_expolygons => $self->_anchor_regions, + red_expolygons => $coverage, + lines => \@lines, + ); } #endif } + return covered; } diff --git a/xs/src/libslic3r/BridgeDetector.hpp b/xs/src/libslic3r/BridgeDetector.hpp index 5c55276be..8c55555e1 100644 --- a/xs/src/libslic3r/BridgeDetector.hpp +++ b/xs/src/libslic3r/BridgeDetector.hpp @@ -33,7 +33,7 @@ public: BridgeDetector(const ExPolygons &_expolygons, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width); // If bridge_direction_override != 0, then the angle is used instead of auto-detect. bool detect_angle(double bridge_direction_override = 0.); - Polygons coverage(double angle = -1) const; + Polygons coverage(double angle = -1, bool precise = false) const; void unsupported_edges(double angle, Polylines* unsupported) const; Polylines unsupported_edges(double angle = -1) const; @@ -54,7 +54,7 @@ private: double coverage; double max_length; }; - +public: // Get possible briging direction candidates. std::vector bridge_direction_candidates() const; diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp index b065cfe07..90ace9f25 100644 --- a/xs/src/libslic3r/ClipperUtils.hpp +++ b/xs/src/libslic3r/ClipperUtils.hpp @@ -213,6 +213,14 @@ inline Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject, bool safety_ return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_); } +inline Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons &subject1, const Slic3r::ExPolygons &subject2, bool safety_offset_ = false) { + Polygons poly_union; + polygons_append(poly_union, to_polygons(subject1)); + polygons_append(poly_union, to_polygons(subject2)); + return _clipper_ex(ClipperLib::ctUnion, poly_union, Slic3r::Polygons(), safety_offset_); + //OR that, i don't know what is the best + //return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject1), to_polygons(subject2), safety_offset_); +} ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject, bool safety_offset_ = false); Slic3r::Polygons union_pt_chained(const Slic3r::Polygons &subject, bool safety_offset_ = false); diff --git a/xs/src/libslic3r/ExPolygon.cpp b/xs/src/libslic3r/ExPolygon.cpp index 9a5c1f783..0677a9a9a 100644 --- a/xs/src/libslic3r/ExPolygon.cpp +++ b/xs/src/libslic3r/ExPolygon.cpp @@ -539,13 +539,59 @@ ExPolygon::get_trapezoids2(Polygons* polygons) const } void -ExPolygon::get_trapezoids2(Polygons* polygons, double angle) const -{ +ExPolygon::get_trapezoids2(Polygons* polygons, double angle) const { ExPolygon clone = *this; - clone.rotate(PI/2 - angle, Point(0,0)); + clone.rotate(PI / 2 - angle, Point(0, 0)); clone.get_trapezoids2(polygons); for (Polygons::iterator polygon = polygons->begin(); polygon != polygons->end(); ++polygon) - polygon->rotate(-(PI/2 - angle), Point(0,0)); + polygon->rotate(-(PI / 2 - angle), Point(0, 0)); +} + +void +ExPolygon::get_trapezoids3_half(Polygons* polygons, float spacing) const { + + // get all points of this ExPolygon + Points pp = *this; + + if (pp.empty()) return; + + // build our bounding box + BoundingBox bb(pp); + + // get all x coordinates + int min_x = pp[0].x, max_x = pp[0].x; + std::vector xx; + for (Points::const_iterator p = pp.begin(); p != pp.end(); ++p) { + if (min_x > p->x) min_x = p->x; + if (max_x < p->x) max_x = p->x; + } + for (int x = min_x; x < max_x-spacing/2; x += spacing) { + xx.push_back(x); + } + xx.push_back(max_x); + //std::sort(xx.begin(), xx.end()); + + // find trapezoids by looping from first to next-to-last coordinate + for (std::vector::const_iterator x = xx.begin(); x != xx.end() - 1; ++x) { + coord_t next_x = *(x + 1); + if (*x == next_x) continue; + + // build rectangle + Polygon poly; + poly.points.resize(4); + poly[0].x = *x +spacing / 4; + poly[0].y = bb.min.y; + poly[1].x = next_x -spacing / 4; + poly[1].y = bb.min.y; + poly[2].x = next_x -spacing / 4; + poly[2].y = bb.max.y; + poly[3].x = *x +spacing / 4; + poly[3].y = bb.max.y; + + // intersect with this expolygon + // append results to return value + polygons_append(*polygons, intersection(poly, to_polygons(*this))); + } } // While this triangulates successfully, it's NOT a constrained triangulation diff --git a/xs/src/libslic3r/ExPolygon.hpp b/xs/src/libslic3r/ExPolygon.hpp index 94682c23f..fe6e70c4a 100644 --- a/xs/src/libslic3r/ExPolygon.hpp +++ b/xs/src/libslic3r/ExPolygon.hpp @@ -59,6 +59,7 @@ public: void get_trapezoids(Polygons* polygons, double angle) const; void get_trapezoids2(Polygons* polygons) const; void get_trapezoids2(Polygons* polygons, double angle) const; + void get_trapezoids3_half(Polygons* polygons, float spacing) const; void triangulate(Polygons* polygons) const; void triangulate_pp(Polygons* polygons) const; void triangulate_p2t(Polygons* polygons) const; diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp index 1df6d3f19..2d40c2e22 100644 --- a/xs/src/libslic3r/PerimeterGenerator.cpp +++ b/xs/src/libslic3r/PerimeterGenerator.cpp @@ -1,6 +1,8 @@ #include "PerimeterGenerator.hpp" #include "ClipperUtils.hpp" #include "ExtrusionEntityCollection.hpp" +#include "BridgeDetector.hpp" +#include "Geometry.hpp" #include #include @@ -49,16 +51,18 @@ void PerimeterGenerator::process() // we need to process each island separately because we might have different // extra perimeters for each one + int surface_idx = 0; for (const Surface &surface : this->slices->surfaces) { // detect how many perimeters must be generated for this island int loop_number = this->config->perimeters + surface.extra_perimeters - 1; // 0-indexed loops + surface_idx++; - if (this->config->only_one_perimeter_top && this->upper_slices == NULL){ loop_number = 0; } ExPolygons gaps; + //this var store infill surface removed from last to not add any more perimeters to it. ExPolygons stored; ExPolygons last = union_ex(surface.expolygon.simplify_p(SCALED_RESOLUTION)); if (loop_number >= 0) { @@ -68,6 +72,83 @@ void PerimeterGenerator::process() ThickPolylines thin_walls; // we loop one time more than needed in order to find gaps after the last perimeter was applied for (int i = 0;; ++ i) { // outer loop is 0 + + //store surface for bridge infill to avoid unsupported perimeters (but the first one, this one is always good) + if (this->config->no_perimeter_unsupported && i == this->config->min_perimeter_unsupported + && this->lower_slices != NULL && !this->lower_slices->expolygons.empty()) { + //note: i don't know where to use the safety offset or not, so if you know, please modify the block. + + //compute our unsupported surface + ExPolygons unsupported = diff_ex(last, this->lower_slices->expolygons, true); + if (!unsupported.empty()) { + ExPolygons to_draw; + //remove small overhangs + ExPolygons unsupported_filtered = offset2_ex(unsupported, -(float)(perimeter_spacing), (float)(perimeter_spacing)); + if (!unsupported_filtered.empty()) { + //to_draw.insert(to_draw.end(), last.begin(), last.end()); + //extract only the useful part of the lower layer. The safety offset is really needed here. + ExPolygons support = diff_ex(last, unsupported, true); + if (this->config->noperi_bridge_only && !unsupported.empty()) { + //only consider the part that can be bridged (really, by the bridge algorithm) + //first, separate into islands (ie, each ExPlolygon) + int numploy = 0; + //only consider the bottom layer that intersect unsupported, to be sure it's only on our island. + ExPolygonCollection lower_island(support); + BridgeDetector detector(unsupported_filtered, + lower_island, + perimeter_spacing); + if (detector.detect_angle(Geometry::deg2rad(this->config->bridge_angle.value))) { + ExPolygons bridgeable = union_ex(detector.coverage(-1, true)); + if (!bridgeable.empty()) { + //simplify to avoid most of artefacts from printing lines. + ExPolygons bridgeable_simplified; + for (ExPolygon &poly : bridgeable) { + poly.simplify(perimeter_spacing/4, &bridgeable_simplified); + } + //offset by perimeter spacing because the simplify may have reduced it a bit. + //it's not dangerous as it will be intersected by 'unsupported' later + to_draw.insert(to_draw.end(), bridgeable.begin(), bridgeable.end()); + // add overlap (perimeter_spacing/4 was good in test, ie 25%) + coord_t overlap = scale_(this->config->get_abs_value("infill_overlap", perimeter_spacing)); + unsupported_filtered = intersection_ex(unsupported_filtered, offset_ex(bridgeable_simplified, overlap)); + } else { + unsupported_filtered.clear(); + } + } else { + unsupported_filtered.clear(); + } + } else { + //only consider the part that can be 'bridged' (inside the convex hull) + // it's not as precise as the bridge detector, but it's better than nothing, and quicker. + ExPolygonCollection coll_last(support); + ExPolygon hull; + hull.contour = coll_last.convex_hull(); + unsupported_filtered = intersection_ex(unsupported_filtered, ExPolygons() = { hull }); + } + if (!unsupported_filtered.empty()) { + //to_draw.insert(to_draw.end(), detector._anchor_regions.begin(), detector._anchor_regions.end()); + //and we want at least 1 perimeter of overlap + ExPolygons bridge = unsupported_filtered; + unsupported_filtered = intersection_ex(offset_ex(unsupported_filtered, (float)(perimeter_spacing)), last); + // unsupported need to be offset_ex by -(float)(perimeter_spacing/2) for the hole to be flush + ExPolygons supported = diff_ex(last, unsupported_filtered); //offset_ex(unsupported_filtered, -(float)(perimeter_spacing / 2)), true); + ExPolygons bridge_and_support = union_ex(bridge, support); + //to_draw.insert(to_draw.end(), support.begin(), support.end()); + // make him flush with perimeter area + unsupported_filtered = intersection_ex(offset_ex(unsupported_filtered, (float)(perimeter_spacing / 2)), bridge_and_support); + // store the results + last = supported; + + //add this directly to the infill list. + // this will avoid to throw wrong offsets into a good polygons + this->fill_surfaces->append( + unsupported_filtered, + stInternal); + } + } + } + } + // We can add more perimeters if there are uncovered overhangs // improvement for future: find a way to add perimeters only where it's needed. // It's hard to do, so here is a simple version. @@ -92,23 +173,25 @@ void PerimeterGenerator::process() } // Calculate next onion shell of perimeters. - ExPolygons offsets; + //this variable stored the nexyt onion + ExPolygons next_onion; if (i == 0) { // the minimum thickness of a single loop is: // ext_width/2 + ext_spacing/2 + spacing/2 + width/2 - offsets = this->config->thin_walls ? + next_onion = this->config->thin_walls ? offset2_ex( last, - -(ext_perimeter_width / 2 + ext_min_spacing / 2 - 1), - +(ext_min_spacing / 2 - 1)) : - offset_ex(last, - ext_perimeter_width / 2); + -(float)(ext_perimeter_width / 2 + ext_min_spacing / 2 - 1), + +(float)(ext_min_spacing / 2 - 1)) : + offset_ex(last, -(float)(ext_perimeter_width / 2)); + // look for thin walls if (this->config->thin_walls) { // the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width // (actually, something larger than that still may exist due to mitering or other causes) - coord_t min_width = scale_(this->ext_perimeter_flow.nozzle_diameter / 3); + coord_t min_width = (coord_t)scale_(this->ext_perimeter_flow.nozzle_diameter / 3); - Polygons no_thin_zone = offset(offsets, (float)(ext_perimeter_width / 2)); + Polygons no_thin_zone = offset(next_onion, (float)(ext_perimeter_width / 2)); ExPolygons expp = offset2_ex( // medial axis requires non-overlapping geometry diff_ex(to_polygons(last), @@ -118,7 +201,7 @@ void PerimeterGenerator::process() // compute a bit of overlap to anchor thin walls inside the print. ExPolygons anchor = intersection_ex(to_polygons(offset_ex(expp, (float)(ext_perimeter_width / 2))), no_thin_zone, true); for (ExPolygon &ex : expp) { - ExPolygons bounds = _clipper_ex(ClipperLib::ctUnion, to_polygons(ex), to_polygons(anchor), true); + ExPolygons bounds = union_ex(ExPolygons() = { ex }, anchor, true); for (ExPolygon &bound : bounds) { if (!intersection_ex(ex, bound).empty()) { // the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop @@ -132,29 +215,32 @@ void PerimeterGenerator::process() //FIXME Is this offset correct if the line width of the inner perimeters differs // from the line width of the infill? coord_t distance = (i == 1) ? ext_perimeter_spacing2 : perimeter_spacing; - offsets = this->config->thin_walls ? + if (this->config->thin_walls){ // This path will ensure, that the perimeters do not overfill, as in // prusa3d/Slic3r GH #32, but with the cost of rounding the perimeters // excessively, creating gaps, which then need to be filled in by the not very // reliable gap fill algorithm. // Also the offset2(perimeter, -x, x) may sometimes lead to a perimeter, which is larger than // the original. - offset2_ex(last, - -(distance + min_spacing/2 - 1), - min_spacing / 2 - 1) : + next_onion = offset2_ex(last, + -(float)(distance + min_spacing / 2 - 1), + +(float)(min_spacing / 2 - 1)); + } else { // If "detect thin walls" is not enabled, this paths will be entered, which // leads to overflows, as in prusa3d/Slic3r GH #32 - offset_ex(last, - distance); + next_onion = offset_ex(last, -(float)(distance)); + } // look for gaps if (this->config->gap_fill_speed.value > 0 && this->config->fill_density.value > 0) // not using safety offset here would "detect" very narrow gaps // (but still long enough to escape the area threshold) that gap fill // won't be able to fill but we'd still remove from infill area append(gaps, diff_ex( - offset(last, -0.5*distance), - offset(offsets, 0.5 * distance + 10))); // safety offset - } - if (offsets.empty()) { + offset(last, -0.5f*distance), + offset(next_onion, 0.5f * distance + 10))); // safety offset + } + + if (next_onion.empty()) { // Store the number of loops actually generated. loop_number = i - 1; // No region left to be filled in. @@ -170,7 +256,8 @@ void PerimeterGenerator::process() break; } } - for (const ExPolygon &expolygon : offsets) { + + for (const ExPolygon &expolygon : next_onion) { contours[i].emplace_back(PerimeterGeneratorLoop(expolygon.contour, i, true)); if (! expolygon.holes.empty()) { holes[i].reserve(holes[i].size() + expolygon.holes.size()); @@ -178,23 +265,25 @@ void PerimeterGenerator::process() holes[i].emplace_back(PerimeterGeneratorLoop(hole, i, false)); } } - last = std::move(offsets); + last = std::move(next_onion); - if(i==0 && config->only_one_perimeter_top && this->upper_slices != NULL) { + //store surface for top infill if only_one_perimeter_top + if(i==0 && config->only_one_perimeter_top && this->upper_slices != NULL){ //split the polygons with top/not_top ExPolygons upper_polygons(this->upper_slices->expolygons); - ExPolygons inner_polygons = diff_ex(last, (upper_polygons), true); - ExPolygons top_polygons = diff_ex(last, inner_polygons, true); + ExPolygons top_polygons = diff_ex(last, (upper_polygons), true); + ExPolygons inner_polygons = diff_ex(last, top_polygons, true); // increase a bit the inner space to fill the frontier between last and stored. - stored = _clipper_ex(ClipperLib::ctUnion, to_polygons(stored), - to_polygons(intersection_ex(offset_ex(inner_polygons, perimeter_spacing / 2), last)), false); - last = intersection_ex(offset_ex(top_polygons, perimeter_spacing / 2), last); + stored = union_ex(stored, intersection_ex(offset_ex(top_polygons, (float)(perimeter_spacing / 2)), last)); + last = intersection_ex(offset_ex(inner_polygons, (float)(perimeter_spacing / 2)), last); } + + } - // add stored polygons - last = _clipper_ex(ClipperLib::ctUnion, to_polygons(last), to_polygons(stored), false); + // re-add stored polygons + last = union_ex(last, stored); // nest loops: holes first for (int d = 0; d <= loop_number; ++d) { diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 50ec9fa39..5ca536fcc 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1147,10 +1147,32 @@ PrintConfigDef::PrintConfigDef() def->label = L("Detect bridging perimeters"); def->category = L("Layers and Perimeters"); def->tooltip = L("Experimental option to adjust flow for overhangs (bridge flow will be used), " - "to apply bridge speed to them and enable fan."); + "to apply bridge speed to them and enable fan."); def->cli = "overhangs!"; def->default_value = new ConfigOptionBool(true); + def = this->add("no_perimeter_unsupported", coBool); + def->label = L(""); + def->category = L("Layers and Perimeters"); + def->tooltip = L("Experimental option to remove perimeters where there are nothing under and a bridged infill should be better."); + def->cli = "no-perimeter-unsupported!"; + def->default_value = new ConfigOptionBool(true); + + def = this->add("min_perimeter_unsupported", coInt); + def->label = L("Minimum perimeters"); + def->category = L("Layers and Perimeters"); + def->tooltip = L("Number of permieter exluded from this option."); + def->cli = "min-perimeter-unsupported=i"; + def->min = 0; + def->default_value = new ConfigOptionInt(0); + + def = this->add("noperi_bridge_only", coBool); + def->label = L("Only on briged area"); + def->category = L("Layers and Perimeters"); + def->tooltip = L("Only remove perimeters over area marked as 'bridge'. Can be useful to let perimeter run over overhangs, but it's not very reliable."); + def->cli = "noperi-bridge-only!"; + def->default_value = new ConfigOptionBool(true); + def = this->add("parking_pos_retraction", coFloat); def->label = L("Filament parking position"); def->tooltip = L("Distance of the extruder tip from the position where the filament is parked " diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 01fe0c093..b2b0746c9 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -411,6 +411,9 @@ public: ConfigOptionInt infill_dense_layers; ConfigOptionPercent infill_dense_density; ConfigOptionBool overhangs; + ConfigOptionBool no_perimeter_unsupported; + ConfigOptionInt min_perimeter_unsupported; + ConfigOptionBool noperi_bridge_only; ConfigOptionInt perimeter_extruder; ConfigOptionFloatOrPercent perimeter_extrusion_width; ConfigOptionFloat perimeter_speed; @@ -457,6 +460,9 @@ protected: OPT_PTR(infill_dense_layers); OPT_PTR(infill_dense_density); OPT_PTR(overhangs); + OPT_PTR(no_perimeter_unsupported); + OPT_PTR(min_perimeter_unsupported); + OPT_PTR(noperi_bridge_only); OPT_PTR(perimeter_extruder); OPT_PTR(perimeter_extrusion_width); OPT_PTR(perimeter_speed); diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 2380ca42b..34f92e39f 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -149,7 +149,10 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector& Preset::print_options() "over_bridge_flow_ratio", "clip_multipart_objects", "enforce_full_fill_volume", "external_infill_margin", "bridged_infill_margin", "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "only_one_perimeter_top", "compatible_printers", - "compatible_printers_condition", "inherits", "infill_dense_layers", "infill_dense_density" + "compatible_printers_condition", "inherits", "infill_dense_layers", "infill_dense_density", "no_perimeter_unsupported", + "min_perimeter_unsupported", "noperi_bridge_only" }; return s_opts; } diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp index 9be7a9892..d9e396f6b 100644 --- a/xs/src/slic3r/GUI/Tab.cpp +++ b/xs/src/slic3r/GUI/Tab.cpp @@ -807,6 +807,11 @@ void TabPrint::build() optgroup->append_single_option_line("avoid_crossing_perimeters"); optgroup->append_single_option_line("thin_walls"); optgroup->append_single_option_line("overhangs"); + line = { _(L("Avoid unsupported perimeters")), "" }; + line.append_option(optgroup->get_option("no_perimeter_unsupported")); + line.append_option(optgroup->get_option("min_perimeter_unsupported")); + line.append_option(optgroup->get_option("noperi_bridge_only")); + optgroup->append_line(line); optgroup = page->new_optgroup(_(L("Advanced"))); optgroup->append_single_option_line("seam_position"); @@ -1158,6 +1163,10 @@ void TabPrint::update() "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed" }) get_field(el)->toggle(have_perimeters); + bool have_no_perimeter_unsupported = have_perimeters && m_config->opt_bool("no_perimeter_unsupported"); + for (auto el : { "min_perimeter_unsupported", "noperi_bridge_only" }) + get_field(el)->toggle(have_no_perimeter_unsupported); + bool have_infill = m_config->option("fill_density")->value > 0; // infill_extruder uses the same logic as in Print::extruders() for (auto el : {"fill_pattern", "infill_every_layers", "infill_only_where_needed",