From b3157c5b1e5203f2822330e09e6e8574a78ccec6 Mon Sep 17 00:00:00 2001 From: supermerill Date: Fri, 1 Mar 2019 19:46:52 +0100 Subject: [PATCH] no perimeters on bridged area : algo changed, more options and less settings. --- src/libslic3r/BridgeDetector.cpp | 8 +- src/libslic3r/BridgeDetector.hpp | 2 +- src/libslic3r/Layer.hpp | 2 +- src/libslic3r/LayerRegion.cpp | 23 ++- src/libslic3r/PerimeterGenerator.cpp | 285 +++++++++++++++++++-------- src/libslic3r/PrintConfig.cpp | 67 +++---- src/libslic3r/PrintConfig.hpp | 30 ++- src/libslic3r/PrintObject.cpp | 20 +- src/slic3r/GUI/Field.cpp | 2 + src/slic3r/GUI/GUI.cpp | 2 + src/slic3r/GUI/OptionsGroup.cpp | 3 + src/slic3r/GUI/Preset.cpp | 3 +- src/slic3r/GUI/Tab.cpp | 13 +- 13 files changed, 297 insertions(+), 163 deletions(-) diff --git a/src/libslic3r/BridgeDetector.cpp b/src/libslic3r/BridgeDetector.cpp index 6fa2ea649..c1a38bdd2 100644 --- a/src/libslic3r/BridgeDetector.cpp +++ b/src/libslic3r/BridgeDetector.cpp @@ -35,7 +35,7 @@ BridgeDetector::BridgeDetector( void BridgeDetector::initialize() { // 5 degrees stepping - this->resolution = PI/36.0; + this->resolution = PI/(36.0*5); // output angle not known this->angle = -1.; @@ -235,10 +235,10 @@ Polygons BridgeDetector::coverage(double angle, bool precise) const { 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; if (!precise) { + // not nice, we need a more robust non-numeric check + // imporvment 1: take into account when we go in the supported area. for (const Line &supported_line : intersection_ln(trapezoid.lines(), anchors)) if (supported_line.length() >= this->spacing) ++n_supported; @@ -286,7 +286,7 @@ Polygons BridgeDetector::coverage(double angle, bool precise) const { 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)); + //covered = intersection(covered, to_polygons(this->expolygons)); #if 0 { my @lines = map @{$_->lines}, @$trapezoids; diff --git a/src/libslic3r/BridgeDetector.hpp b/src/libslic3r/BridgeDetector.hpp index 0c08546ac..e79a619b6 100644 --- a/src/libslic3r/BridgeDetector.hpp +++ b/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, bool precise = false) const; + Polygons coverage(double angle = -1, bool precise = true) const; void unsupported_edges(double angle, Polylines* unsupported) const; Polylines unsupported_edges(double angle = -1) const; diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 0f75cd39a..609f680fe 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -63,7 +63,7 @@ public: Flow flow(FlowRole role, bool bridge = false, double width = -1) const; void slices_to_fill_surfaces_clipped(); void prepare_fill_surfaces(); - void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces); + void make_perimeters(SurfaceCollection &slices, SurfaceCollection* fill_surfaces); void process_external_surfaces(const Layer* lower_layer); double infill_area_threshold() const; diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index c2957cb67..2f656aaf8 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -30,6 +30,7 @@ Flow LayerRegion::flow(FlowRole role, bool bridge, double width) const // Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces. void LayerRegion::slices_to_fill_surfaces_clipped() { + //if (this->region()->config().no_perimeter_full_bridge) return; // Note: this method should be idempotent, but fill_surfaces gets modified // in place. However we're now only using its boundaries (which are invariant) // so we're safe. This guarantees idempotence of prepare_infill() also in case @@ -45,11 +46,14 @@ void LayerRegion::slices_to_fill_surfaces_clipped() this->fill_surfaces.surfaces.clear(); for (auto const& entry : polygons_by_surface) { if (!entry.second.empty()) - this->fill_surfaces.append(intersection_ex(entry.second, fill_boundaries), entry.first); + //if (entry.first & stModBridge == stModBridge && this->region()->config().no_perimeter_full_bridge) + // this->fill_surfaces.append(entry.second, entry.first); + //else + this->fill_surfaces.append(intersection_ex(entry.second, fill_boundaries), entry.first); } } -void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces) +void LayerRegion::make_perimeters(SurfaceCollection &slices, SurfaceCollection* fill_surfaces) { this->perimeters.clear(); this->thin_fills.clear(); @@ -91,16 +95,17 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec void LayerRegion::process_external_surfaces(const Layer* lower_layer) { + + coord_t max_margin = 0; + if ((this->region()->config().perimeters > 0)) { + max_margin = scale_(this->flow(frExternalPerimeter).width) + this->flow(frPerimeter).scaled_spacing() * (this->region()->config().perimeters.value - 1); + } const Surfaces &surfaces = this->fill_surfaces.surfaces; const bool has_infill = this->region()->config().fill_density.value > 0.; - coord_t margin = scale_(this->region()->config().external_infill_margin.getFloat()); - coord_t margin_bridged = scale_(this->region()->config().bridged_infill_margin.getFloat()); + coord_t margin = scale_(this->region()->config().external_infill_margin.get_abs_value(unscaled(max_margin))); + coord_t margin_bridged = scale_(this->region()->config().bridged_infill_margin.get_abs_value(this->flow(frExternalPerimeter).width)); //if no infill, reduce the margin for everything to only the perimeter if (!has_infill) { - coord_t max_margin = 0; - if ((this->region()->config().perimeters > 0)) { - max_margin = scale_(this->flow(frExternalPerimeter).width) + this->flow(frPerimeter).scaled_spacing() * (this->region()->config().perimeters.value - 1); - } margin = std::min(margin, max_margin); margin_bridged = std::min(margin_bridged, max_margin); } @@ -284,7 +289,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) if (bd.detect_angle(Geometry::deg2rad(this->region()->config().bridge_angle.value))) { bridges[idx_last].bridge_angle = bd.angle; if (this->layer()->object()->config().support_material) { - polygons_append(this->bridged, bd.coverage()); + polygons_append(this->bridged, intersection(bd.coverage(), to_polygons(initial))); this->unsupported_bridge_edges.append(bd.unsupported_edges()); } } diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index b35cba2ea..7ef38e33d 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -76,7 +76,202 @@ 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) { + Surfaces all_surfaces = this->slices->surfaces; + + //store surface for bridge infill to avoid unsupported perimeters (but the first one, this one is always good) + + if (this->config->no_perimeter_unsupported_algo != npuaNone + && this->lower_slices != NULL && !this->lower_slices->expolygons.empty()) { + + for (surface_idx = 0; surface_idx < all_surfaces.size(); surface_idx++) { + Surface *surface = &all_surfaces[surface_idx]; + ExPolygons last = union_ex(surface->expolygon.simplify_p(SCALED_RESOLUTION)); + //compute our unsupported surface + ExPolygons unsupported = diff_ex(last, this->lower_slices->expolygons, true); + if (!unsupported.empty()) { + //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 (!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()) { + //check if we get everything or just the bridgeable area + if (this->config->no_perimeter_unsupported_algo == npuaNoPeri || this->config->no_perimeter_unsupported_algo == npuaFilled) { + //we bridge everything, even the not-bridgeable bits + for (size_t i = 0; i < unsupported_filtered.size();) { + ExPolygon &poly_unsupp = *(unsupported_filtered.begin() + i); + Polygons contour_simplified = poly_unsupp.contour.simplify(perimeter_spacing); + ExPolygon poly_unsupp_bigger = poly_unsupp; + Polygons contour_bigger = offset(poly_unsupp_bigger.contour, perimeter_spacing); + if (contour_bigger.size() == 1) poly_unsupp_bigger.contour = contour_bigger[0]; + + //check convex, has some bridge, not overhang + if (contour_simplified.size() == 1 && contour_bigger.size() == 1 && contour_simplified[0].concave_points().size() == 0 + && intersection_ex(bridgeable, { poly_unsupp }).size() > 0 + && diff_ex({ poly_unsupp_bigger }, last, true).size() == 0) { + //ok, keep it + i++; + } else { + unsupported_filtered.erase(unsupported_filtered.begin() + i); + } + } + unsupported_filtered = intersection_ex(last, + offset2_ex(unsupported_filtered, (float)-perimeter_spacing / 2, (float)perimeter_spacing * 3 / 2)); + if (this->config->no_perimeter_unsupported_algo == npuaFilled) { + for (ExPolygon &expol : unsupported_filtered) { + //check if the holes won't be covered by the upper layer + //TODO: if we want to do that, we must modify the geometry before making perimeters. + //if (this->upper_slices != nullptr && !this->upper_slices->expolygons.empty()) { + // for (Polygon &poly : expol.holes) poly.make_counter_clockwise(); + // float perimeterwidth = this->config->perimeters == 0 ? 0 : (this->ext_perimeter_flow.scaled_width() + (this->config->perimeters - 1) + this->perimeter_flow.scaled_spacing()); + // std::cout << "test upper slices with perimeterwidth=" << perimeterwidth << "=>" << offset_ex(this->upper_slices->expolygons, -perimeterwidth).size(); + // if (intersection(Polygons() = { expol.holes }, to_polygons(offset_ex(this->upper_slices->expolygons, -this->ext_perimeter_flow.scaled_width() / 2))).empty()) { + // std::cout << " EMPTY§"; + // expol.holes.clear(); + // } else { + // } + // std::cout << "\n"; + //} else { + expol.holes.clear(); + //} + + //detect inside volume + for (size_t surface_idx_other = 0; surface_idx_other < all_surfaces.size(); surface_idx_other++) { + if (surface_idx == surface_idx_other) continue; + if (intersection_ex(ExPolygons() = { expol }, ExPolygons() = { all_surfaces[surface_idx_other].expolygon }).size() > 0) { + //this means that other_surf was inside an expol holes + //as we removed them, we need to add a new one + ExPolygons new_poly = offset2_ex(all_surfaces[surface_idx_other].expolygon, -(float)perimeter_spacing * 2, (float)perimeter_spacing); + if (new_poly.size() == 1) { + all_surfaces[surface_idx_other].expolygon = new_poly[0]; + expol.holes.push_back(new_poly[0].contour); + expol.holes.back().make_clockwise(); + } else { + for (size_t idx = 0; idx < new_poly.size(); idx++) { + Surface new_surf = all_surfaces[surface_idx_other]; + new_surf.expolygon = new_poly[idx]; + all_surfaces.push_back(new_surf); + expol.holes.push_back(new_poly[idx].contour); + expol.holes.back().make_clockwise(); + } + all_surfaces.erase(all_surfaces.begin() + surface_idx_other); + if (surface_idx_other < surface_idx) { + surface_idx--; + surface = &all_surfaces[surface_idx]; + } + surface_idx_other--; + } + } + } + } + + } + //TODO: add other polys as holes inside this one (-margin) + } else if (this->config->no_perimeter_unsupported_algo == npuaBridgesOverhangs || this->config->no_perimeter_unsupported_algo == npuaBridges){ + //simplify to avoid most of artefacts from printing lines. + ExPolygons bridgeable_simplified; + for (ExPolygon &poly : bridgeable) { + poly.simplify(perimeter_spacing, &bridgeable_simplified); + } + bridgeable_simplified = offset2_ex(bridgeable_simplified, -ext_perimeter_width, ext_perimeter_width); + //bridgeable_simplified = intersection_ex(bridgeable_simplified, unsupported_filtered); + //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 + //FIXME: add overlap in this->fill_surfaces->append + //FIXME: it overlap inside unsuppported not-bridgeable area! + double overlap = scale_(this->config->get_abs_value("infill_overlap", unscale(perimeter_spacing))); + + //bridgeable_simplified = offset2_ex(bridgeable_simplified, (float)-perimeter_spacing, (float)perimeter_spacing * 2); + //ExPolygons unbridgeable = offset_ex(diff_ex(unsupported, bridgeable_simplified), perimeter_spacing * 3 / 2); + //ExPolygons unbridgeable = intersection_ex(unsupported, diff_ex(unsupported_filtered, offset_ex(bridgeable_simplified, ext_perimeter_width / 2))); + //unbridgeable = offset2_ex(unbridgeable, -ext_perimeter_width, ext_perimeter_width); + + + if (this->config->no_perimeter_unsupported_algo == npuaBridges) { + ExPolygons unbridgeable = unsupported_filtered; + for (ExPolygon &expol : unbridgeable)expol.holes.clear(); + unbridgeable = diff_ex(unbridgeable, bridgeable_simplified); + unbridgeable = offset2_ex(unbridgeable, -ext_perimeter_width*2, ext_perimeter_width*2); + ExPolygons bridges_temp = intersection_ex(last, diff_ex(unsupported_filtered, unbridgeable)); + //remove the overhangs section form the surface polygons + last = diff_ex(last, unsupported_filtered); + //ExPolygons no_bridge = diff_ex(offset_ex(unbridgeable, ext_perimeter_width * 3 / 2), last); + //bridges_temp = diff_ex(bridges_temp, no_bridge); + unsupported_filtered = diff_ex(offset_ex(bridges_temp, ext_perimeter_width * 3 / 2), offset_ex(unbridgeable, ext_perimeter_width*2, jtSquare)); + } else { + ExPolygons unbridgeable = intersection_ex(unsupported, diff_ex(unsupported_filtered, offset_ex(bridgeable_simplified, ext_perimeter_width / 2))); + unbridgeable = offset2_ex(unbridgeable, -ext_perimeter_width, ext_perimeter_width); + unsupported_filtered = unbridgeable; + + ////put the bridge area inside the unsupported_filtered variable + //unsupported_filtered = intersection_ex(last, + // diff_ex( + // offset_ex(bridgeable_simplified, (float)perimeter_spacing / 2), + // unbridgeable + // ) + // ); + } + } + } else { + unsupported_filtered.clear(); + } + } else { + unsupported_filtered.clear(); + } + } + + if (!unsupported_filtered.empty()) { + + + //add this directly to the infill list. + // this will avoid to throw wrong offsets into a good polygons + this->fill_surfaces->append( + unsupported_filtered, + stPosInternal | stDensSparse); + + // store the results + last = diff_ex(last, unsupported_filtered, true); + //remove "thin air" polygons (note: it assumes that all polygons below will be extruded) + for (int i = 0; i < last.size(); i++) { + if (intersection_ex(support, ExPolygons() = { last[i] }).empty()) { + this->fill_surfaces->append( + ExPolygons() = { last[i] }, + stPosInternal | stDensSparse); + last.erase(last.begin() + i); + i--; + } + } + } + } + } + if (last.size() == 0) { + all_surfaces.erase(all_surfaces.begin() + surface_idx); + surface_idx--; + } else { + surface->expolygon = last[0]; + for (size_t idx = 1; idx < last.size(); idx++) { + Surface new_surf = *surface; + new_surf.expolygon = last[idx]; + all_surfaces.push_back(new_surf); + } + } + } + } + + surface_idx = 0; + for (const Surface &surface : all_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++; @@ -132,87 +327,7 @@ void PerimeterGenerator::process() // 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()) { - - //compute our unsupported surface - ExPolygons unsupported = diff_ex(last, this->lower_slices->expolygons, true); - if (!unsupported.empty()) { - //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/2, &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 - //FIXME: add overlap in this->fill_surfaces->append - // add overlap (perimeter_spacing/4 was good in test, ie 25%) - double overlap = scale_(this->config->get_abs_value("infill_overlap", unscale(perimeter_spacing))); - unsupported_filtered = intersection_ex(unsupported_filtered, offset_ex(bridgeable_simplified, (float) 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()) { - //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); - // remove from the bridge & support the small imperfections of the union - ExPolygons bridge_and_support = offset2_ex(union_ex(bridge, support, true), (float)perimeter_spacing / 2, (float)-perimeter_spacing / 2); - // make him flush with perimeter area - unsupported_filtered = intersection_ex(offset_ex(unsupported_filtered, (float)(perimeter_spacing / 2)), bridge_and_support); - - //add this directly to the infill list. - // this will avoid to throw wrong offsets into a good polygons - this->fill_surfaces->append( - unsupported_filtered, - stPosInternal | stDensSparse); - - // store the results - last = diff_ex(last, unsupported_filtered, true); - //remove "thin air" polygons - for (int i = 0; i < last.size();i++) { - if (intersection_ex(support, ExPolygons() = { last[i] }).empty()) { - this->fill_surfaces->append( - ExPolygons() = { last[i] }, - stPosInternal | stDensSparse); - last.erase(last.begin() + i); - i--; - } - } - } - } - } - } + // We can add more perimeters if there are uncovered overhangs // improvement for future: find a way to add perimeters only where it's needed. @@ -515,7 +630,7 @@ void PerimeterGenerator::process() // only apply infill overlap if we actually have one perimeter coord_t overlap = 0; if (inset > 0) { - overlap = scale_(this->config->get_abs_value("infill_overlap", unscale(inset + solid_infill_spacing / 2))); + overlap = (coord_t)scale_(this->config->get_abs_value("infill_overlap", unscale(inset + solid_infill_spacing / 2))); } // simplify infill contours according to resolution Polygons not_filled_p; @@ -1205,10 +1320,10 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop, outer_end->extruder_id = -1; }*/ //add paths into my_loop => after that all ref are wrong! - for (int32_t i = travel_path_end.size() - 1; i >= 0; i--) { + for (size_t i = travel_path_end.size() - 1; i >= 0; i--) { my_loop.paths.insert(my_loop.paths.begin() + nearest.idx_polyline_outter + child_paths_size + 1, travel_path_end[i]); } - for (int32_t i = travel_path_begin.size() - 1; i >= 0; i--) { + for (size_t i = travel_path_begin.size() - 1; i >= 0; i--) { my_loop.paths.insert(my_loop.paths.begin() + nearest.idx_polyline_outter + 1, travel_path_begin[i]); } } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index f7071c8d9..031ed75d3 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -497,27 +497,27 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->default_value = new ConfigOptionBool(true); - def = this->add("external_infill_margin", coFloat); + def = this->add("external_infill_margin", coFloatOrPercent); def->label = L("Default"); def->full_label = L("Default infill margin"); def->category = L("Infill"); - def->tooltip = L("This parameter grows the top/bottom/solid layers by the specified MM to anchor them into the part. Put 0 to deactivate it."); + def->tooltip = L("This parameter grows the top/bottom/solid layers by the specified MM to anchor them into the part. Put 0 to deactivate it. Can be a % of the width of the perimeters."); def->sidetext = L("mm"); - def->cli = "top-layer-anchor=f"; + def->cli = "top-layer-anchor=s"; def->min = 0; def->mode = comExpert; - def->default_value = new ConfigOptionFloat(1.5); + def->default_value = new ConfigOptionFloatOrPercent(150, true); - def = this->add("bridged_infill_margin", coFloat); + def = this->add("bridged_infill_margin", coFloatOrPercent); def->label = L("Bridged"); def->full_label = L("Bridge margin"); def->category = L("Infill"); - def->tooltip = L("This parameter grows the bridged solid infill layers by the specified MM to anchor them into the part. Put 0 to deactivate it."); + def->tooltip = L("This parameter grows the bridged solid infill layers by the specified MM to anchor them into the part. Put 0 to deactivate it. Can be a % of the width of the external perimeter."); def->sidetext = L("mm"); - def->cli = "bridged-layer-anchor=f"; + def->cli = "bridged-layer-anchor=s"; def->min = 0; def->mode = comExpert; - def->default_value = new ConfigOptionFloat(2); + def->default_value = new ConfigOptionFloatOrPercent(200, true); def = this->add("external_perimeter_extrusion_width", coFloatOrPercent); def->label = L("External perimeters"); @@ -1254,7 +1254,7 @@ void PrintConfigDef::init_fff_params() def->full_label = ("Dense infill algorithm"); def->tooltip = L("Choose the way the dense layer is lay out." " The automatic option let it try to draw the smallest surface with only strait lines inside the sparse infill." - " The anchored just enlarge a bit (by bridged anchor) the surfaces that need a better support."); + " The anchored just enlarge a bit (by 'Default infill margin') the surfaces that need a better support."); def->cli = "infill-dense-algo=s"; def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("automatic"); @@ -1696,32 +1696,29 @@ void PrintConfigDef::init_fff_params() def->cli = "overhangs!"; def->mode = comAdvanced; def->default_value = new ConfigOptionBool(true); - - def = this->add("no_perimeter_unsupported", coBool); - def->label = L(""); - def->full_label = ("No perimeters on bridge areas"); - def->category = L("Layers and Perimeters"); - def->tooltip = L("Experimental option to remove perimeters where there is nothing under it and where a bridged infill should be better. Computationally intensive!"); - def->cli = "no-perimeter-unsupported!"; - def->mode = comExpert; - def->default_value = new ConfigOptionBool(false); - - def = this->add("min_perimeter_unsupported", coInt); - def->label = L("Minimum perimeters"); - def->category = L("Layers and Perimeters"); - def->tooltip = L("Number of perimeters exluded from this option."); - def->cli = "min-perimeter-unsupported=i"; - def->min = 0; - def->mode = comExpert; - def->default_value = new ConfigOptionInt(0); - - def = this->add("noperi_bridge_only", coBool); - def->label = L("Only on bridged areas"); - def->category = L("Layers and Perimeters"); - def->tooltip = L("Only remove perimeters over areas marked as 'bridge'. Can be useful to let perimeter run over overhangs, but it's not very reliable."); - def->cli = "noperi-bridge-only!"; - def->mode = comExpert; - def->default_value = new ConfigOptionBool(true); + + def = this->add("no_perimeter_unsupported_algo", coEnum); + def->label = L("No perimeters on bridge areas"); + def->tooltip = L("Experimental option to remove perimeters where there is nothing under it and where a bridged infill should be better. " + "\nRemove perimeters: remove the unsupported periemter, let the bridge area as-is." + "\nKeep only bridges: remove the unsupported periemter, kep only bridges that end in solid area." + "\nKeep bridges and overhangs: remove the unsupported periemter, keep only bridges that end in solid area, fill the rest with overhang perimeters+bridges." + "\nFill the voids with bridges: remove the unsupported periemter, draw bridges over the whole hole. !! can lead to problems with overhangs shape like /\\, consider carefully before using this option!" + "\n!!Computationally intensive!!. "); + def->cli = "no-perimeter-unsupported-algo=s"; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("none"); + def->enum_values.push_back("noperi"); + def->enum_values.push_back("bridges"); + def->enum_values.push_back("bridgesoverhangs"); + def->enum_values.push_back("filled"); + def->enum_labels.push_back(L("Disabled")); + def->enum_labels.push_back(L("Remove perimeters")); + def->enum_labels.push_back(L("Keep only bridges")); + def->enum_labels.push_back(L("Keep bridges and overhangs")); + def->enum_labels.push_back(L("Fill the voids with bridges")); + def->mode = comAdvanced; + def->default_value = new ConfigOptionEnum(npuaNone); def = this->add("parking_pos_retraction", coFloat); def->label = L("Filament parking position"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 5fcc83a21..6da297e11 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -68,7 +68,11 @@ enum FilamentType { }; enum DenseInfillAlgo { - dfaAutomatic, dfaAutoNotFull, dfaEnlarged, + dfaNone, dfaAutomatic, dfaAutoNotFull, dfaEnlarged, +}; + +enum NoPerimeterUnsupportedAlgo { + npuaNone, npuaNoPeri, npuaBridges, npuaBridgesOverhangs, npuaFilled, }; enum SupportZDistanceType { @@ -208,6 +212,7 @@ template<> inline const t_config_enum_values& ConfigOptionEnum::ge template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { static const t_config_enum_values keys_map = { + { "none", dfaNone }, { "automatic", dfaAutomatic }, { "autosmall", dfaAutoNotFull }, { "enlarged", dfaEnlarged } @@ -215,6 +220,17 @@ template<> inline const t_config_enum_values& ConfigOptionEnum: return keys_map; } +template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { + static const t_config_enum_values keys_map = { + { "none", npuaNone }, + { "noperi", npuaNoPeri }, + { "bridges", npuaBridges }, + { "bridgesoverhangs", npuaBridgesOverhangs }, + { "filled", npuaFilled } + }; + return keys_map; +} + template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { static const t_config_enum_values keys_map = { { "filament", zdFilament }, @@ -544,8 +560,8 @@ public: ConfigOptionFloat bridge_speed; ConfigOptionBool ensure_vertical_shell_thickness; ConfigOptionBool enforce_full_fill_volume; - ConfigOptionFloat external_infill_margin; - ConfigOptionFloat bridged_infill_margin; + ConfigOptionFloatOrPercent external_infill_margin; + ConfigOptionFloatOrPercent bridged_infill_margin; ConfigOptionFloatOrPercent external_perimeter_extrusion_width; ConfigOptionFloatOrPercent external_perimeter_speed; ConfigOptionBool external_perimeters_first; @@ -569,9 +585,7 @@ public: ConfigOptionBool infill_first; // Detect bridging perimeters ConfigOptionBool overhangs; - ConfigOptionBool no_perimeter_unsupported; - ConfigOptionInt min_perimeter_unsupported; - ConfigOptionBool noperi_bridge_only; + ConfigOptionEnum no_perimeter_unsupported_algo; ConfigOptionInt perimeter_extruder; ConfigOptionFloatOrPercent perimeter_extrusion_width; ConfigOptionFloat perimeter_speed; @@ -629,9 +643,7 @@ protected: OPT_PTR(infill_dense_algo); OPT_PTR(infill_first); OPT_PTR(overhangs); - OPT_PTR(no_perimeter_unsupported); - OPT_PTR(min_perimeter_unsupported); - OPT_PTR(noperi_bridge_only); + OPT_PTR(no_perimeter_unsupported_algo); OPT_PTR(perimeter_extruder); OPT_PTR(perimeter_extrusion_width); OPT_PTR(perimeter_speed); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index f75b5dd57..d168b37e8 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -476,9 +476,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vectorregion()->config().infill_dense_algo == dfaEnlarged) { //expand the area a bit - intersect = offset_ex(intersect, (float)scale_(layerm->region()->config().bridged_infill_margin)); + intersect = offset_ex(intersect, (float)scale_(layerm->region()->config().external_infill_margin.get_abs_value( + region->config().perimeters == 0 ? 0 : (layerm->flow(frExternalPerimeter).width + layerm->flow(frPerimeter).spacing() * (region->config().perimeters - 1))))); } else if (layerm->region()->config().infill_dense_algo == dfaAutoNotFull || layerm->region()->config().infill_dense_algo == dfaAutomatic){ @@ -900,7 +899,9 @@ void PrintObject::detect_surfaces_type() // collapse very narrow parts (using the safety offset in the diff is not enough) float offset = layerm->flow(frExternalPerimeter).scaled_width() / 10.f; - Polygons layerm_slices_surfaces = to_polygons(layerm->slices.surfaces); + // no_perimeter_full_bridge allow to put bridges where there are nothing, hence adding area to silce, that's why we need to start from the result of PerimeterGenerator. + // i don't see the need tostart from layerm->slices.surfaces, but i'll keep that "in case of". + Polygons layerm_slices_surfaces = layerm->region()->config().no_perimeter_unsupported_algo == npuaFilled ? to_polygons(layerm->fill_surfaces) : to_polygons(layerm->slices.surfaces); // find top surfaces (difference between current surfaces // of current layer and upper one) @@ -933,11 +934,16 @@ void PrintObject::detect_surfaces_type() offset2_ex(diff(layerm_slices_surfaces, lower_slices, true), -offset, offset), surface_type_bottom_other); #else + ExPolygons lower_slices = lower_layer->slices.expolygons; + //if we added new surfaces, we can use them as support + /*if (layerm->region()->config().no_perimeter_full_bridge) { + lower_slices = union_ex(lower_slices, lower_layer->get_region(idx_region)->fill_surfaces); + }*/ // Any surface lying on the void is a true bottom bridge (an overhang) surfaces_append( bottom, offset2_ex( - diff(layerm_slices_surfaces, to_polygons(lower_layer->slices), true), + diff(layerm_slices_surfaces, to_polygons(lower_slices), true), -offset, offset), surface_type_bottom_other); // if user requested internal shells, we need to identify surfaces @@ -949,7 +955,7 @@ void PrintObject::detect_surfaces_type() bottom, offset2_ex( diff( - intersection(layerm_slices_surfaces, to_polygons(lower_layer->slices)), // supported + intersection(layerm_slices_surfaces, to_polygons(lower_slices)), // supported to_polygons(lower_layer->get_region(idx_region)->slices.surfaces), true), -offset, offset), diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 94a407dc1..e74e0f01c 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -761,6 +761,8 @@ boost::any& Choice::get_value() m_value = static_cast(ret_enum); else if (m_opt_id.compare("infill_dense_algo") == 0) m_value = static_cast(ret_enum); + else if (m_opt_id.compare("no_perimeter_unsupported_algo") == 0) + m_value = static_cast(ret_enum); else if (m_opt_id.compare("wipe_advanced_algo") == 0) m_value = static_cast(ret_enum); else if (m_opt_id.compare("support_material_contact_distance_type") == 0) diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 093a9601c..4c5d7430a 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -226,6 +226,8 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); else if (opt_key.compare("infill_dense_algo") == 0) config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); + else if (opt_key.compare("no_perimeter_unsupported_algo") == 0) + config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); else if (opt_key.compare("wipe_advanced_algo") == 0) config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); else if (opt_key.compare("support_material_contact_distance_type") == 0) diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 71398e8a7..2bd2206e5 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -560,6 +560,9 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config else if (opt_key.compare("infill_dense_algo") == 0){ ret = static_cast(config.option>(opt_key)->value); } + else if (opt_key.compare("no_perimeter_unsupported_algo") == 0){ + ret = static_cast(config.option>(opt_key)->value); + } else if (opt_key.compare("wipe_advanced_algo") == 0){ ret = static_cast(config.option>(opt_key)->value); } diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 510d046d2..ca6c8512f 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -355,7 +355,8 @@ const std::vector& Preset::print_options() "elefant_foot_compensation", "xy_size_compensation", "hole_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", "single_extruder_multi_material_priming", "compatible_printers", "compatible_printers_condition", "inherits", - "infill_dense", "infill_dense_algo", "no_perimeter_unsupported", "min_perimeter_unsupported", "noperi_bridge_only", + "infill_dense", "infill_dense_algo", + "no_perimeter_unsupported_algo", "support_material_solid_first_layer" , "exact_last_layer_height" , "perimeter_loop" diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 371d9435d..b939683a3 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -951,12 +951,8 @@ void TabPrint::build() line.append_option(optgroup->get_option("thin_walls_min_width")); line.append_option(optgroup->get_option("thin_walls_overlap")); optgroup->append_line(line); - 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->append_single_option_line("overhangs"); + optgroup->append_single_option_line("no_perimeter_unsupported_algo"); optgroup = page->new_optgroup(_(L("Advanced"))); optgroup->append_single_option_line("remove_small_gaps"); @@ -1390,11 +1386,6 @@ void TabPrint::update() for (auto el : { "thin_walls_min_width", "thin_walls_overlap" }) get_field(el)->toggle(m_config->opt_bool("thin_walls")); get_field("perimeter_loop_seam")->toggle(m_config->opt_bool("perimeter_loop")); - //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",