From 6da0abcd8f97d9256165112160560d64e1f6664b Mon Sep 17 00:00:00 2001 From: Vovodroid <> Date: Tue, 18 Apr 2023 16:36:05 +0300 Subject: [PATCH] Merge PR "Only one perimeter on top/first layer" https://github.com/prusa3d/PrusaSlicer/pull/10648 --- src/libslic3r/ClipperUtils.cpp | 12 ++ src/libslic3r/ClipperUtils.hpp | 1 + src/libslic3r/LayerRegion.cpp | 3 + src/libslic3r/PerimeterGenerator.cpp | 222 +++++++++++++++++++++++++- src/libslic3r/PerimeterGenerator.hpp | 5 + src/libslic3r/Preset.cpp | 5 +- src/libslic3r/PrintConfig.cpp | 26 +++ src/libslic3r/PrintConfig.hpp | 4 + src/libslic3r/PrintObject.cpp | 4 + src/libslic3r/libslic3r.h | 5 + src/slic3r/GUI/ConfigManipulation.cpp | 6 +- src/slic3r/GUI/Plater.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 6 + tests/fff_print/test_perimeters.cpp | 1 + 14 files changed, 296 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 1c2e1b9600b..d8d3b1d5afc 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -183,6 +183,18 @@ namespace ClipperUtils { out.end()); return out; } + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons &src, const BoundingBox &bbox) + { + Polygons out; + out.reserve(number_polygons(src)); + for (const ExPolygon &p : src) { + Polygons temp = clip_clipper_polygons_with_subject_bbox(p, bbox); + out.insert(out.end(), temp.begin(), temp.end()); + } + + out.erase(std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) {return polygon.empty(); }), out.end()); + return out; + } } static ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree &&polytree) diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index 454ceb74c7f..f399fb6e3b5 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -335,6 +335,7 @@ namespace ClipperUtils { [[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox); [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const Polygons &src, const BoundingBox &bbox); [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox); + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons &src, const BoundingBox &bbox); } // offset Polygons diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 4b612627967..accac4bd114 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -111,6 +111,7 @@ void LayerRegion::make_perimeters( // Cummulative sum of polygons over all the regions. const ExPolygons *lower_slices = this->layer()->lower_layer ? &this->layer()->lower_layer->lslices : nullptr; + const ExPolygons *upper_slices = this->layer()->upper_layer ? &this->layer()->upper_layer->lslices : nullptr; // Cache for offsetted lower_slices Polygons lower_layer_polygons_cache; @@ -124,6 +125,7 @@ void LayerRegion::make_perimeters( params, surface, lower_slices, + upper_slices, lower_layer_polygons_cache, // output: m_perimeters, @@ -135,6 +137,7 @@ void LayerRegion::make_perimeters( params, surface, lower_slices, + upper_slices, lower_layer_polygons_cache, // output: m_perimeters, diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 9f9647b37f0..f806c4f98f6 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -1083,6 +1083,7 @@ void PerimeterGenerator::process_arachne( const Parameters ¶ms, const Surface &surface, const ExPolygons *lower_slices, + const ExPolygons *upper_slices, // Cache: Polygons &lower_slices_polygons_cache, // Output: @@ -1115,11 +1116,71 @@ void PerimeterGenerator::process_arachne( // extra perimeters for each one // detect how many perimeters must be generated for this island int loop_number = params.config.perimeters + surface.extra_perimeters - 1; // 0-indexed loops + + if (params.config.only_one_perimeter_first && params.layer_id == 0) + loop_number = 0; + + // SuperSlicer: set the topmost layer to be one perimeter + if (loop_number > 0 && params.config.only_one_perimeter_top && upper_slices == nullptr) + loop_number = 0; + ExPolygons last = offset_ex(surface.expolygon.simplify_p(params.scaled_resolution), - float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.)); - Polygons last_p = to_polygons(last); + //Orca + std::vector out_shell; + ExPolygons top_fills; + if (loop_number > 0 && params.config.only_one_perimeter_top && !surface.is_bridge() && upper_slices != nullptr) { + // Check if current layer has surfaces that are not covered by upper layer (i.e., top surfaces) + ExPolygons non_top_polygons; + ExPolygons fill_clip; + + split_top_surfaces(params, lower_slices, upper_slices, last, top_fills, non_top_polygons, fill_clip); + + if (top_fills.empty()) { + // No top surfaces, no special handling needed + } else { + // First we slice the outer shell + Polygons last_p = to_polygons(last); + Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(1), + 0, params.layer_height, params.object_config, params.print_config); + out_shell = wallToolPaths.getToolPaths(); + // Make sure infill not overlap with wall + top_fills = intersection_ex(top_fills, wallToolPaths.getInnerContour()); + + if (!top_fills.empty()) { + // Then get the inner part that needs more walls + last = intersection_ex(non_top_polygons, wallToolPaths.getInnerContour()); + loop_number--; + } else { + // Give up the outer shell because we don't have any meaningful top surface + out_shell.clear(); + } + } + } + + Polygons last_p = to_polygons(last); Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(loop_number + 1), 0, params.layer_height, params.object_config, params.print_config); std::vector perimeters = wallToolPaths.getToolPaths(); + + //Orca + if (!out_shell.empty()) { + // Combine outer shells + size_t inset_offset = 0; + for (auto &p : out_shell) { + for (auto &l : p) { + if (l.inset_idx + 1 > inset_offset) { + inset_offset = l.inset_idx + 1; + } + } + } + for (auto &p : perimeters) { + for (auto &l : p) { + l.inset_idx += inset_offset; + } + } + perimeters.insert(perimeters.begin(), out_shell.begin(), out_shell.end()); + } + loop_number = int(perimeters.size()) - 1; #ifdef ARACHNE_DEBUG @@ -1302,6 +1363,11 @@ void PerimeterGenerator::process_arachne( float(- min_perimeter_infill_spacing / 2.), float(inset + min_perimeter_infill_spacing / 2.)); + // append infill areas to fill_surfaces + if (!top_fills.empty()) { + infill_areas = union_ex(infill_areas, offset_ex(top_fills, double(inset))); + } + if (lower_slices != nullptr && params.config.overhangs && params.config.extra_perimeters_on_overhangs && params.config.perimeters > 0 && params.layer_id > params.object_config.raft_layers) { // Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material @@ -1329,6 +1395,7 @@ void PerimeterGenerator::process_classic( const Parameters ¶ms, const Surface &surface, const ExPolygons *lower_slices, + const ExPolygons *upper_slices, // Cache: Polygons &lower_slices_polygons_cache, // Output: @@ -1376,8 +1443,19 @@ void PerimeterGenerator::process_classic( // extra perimeters for each one // detect how many perimeters must be generated for this island int loop_number = params.config.perimeters + surface.extra_perimeters - 1; // 0-indexed loops + + if (params.config.only_one_perimeter_first && params.layer_id == 0) + loop_number = 0; + + // SuperSlicer: set the topmost layer to be one perimeter + if (loop_number > 0 && params.config.only_one_perimeter_top && upper_slices == nullptr) + loop_number = 0; + ExPolygons last = union_ex(surface.expolygon.simplify_p(params.scaled_resolution)); ExPolygons gaps; + ExPolygons top_fills; + ExPolygons fill_clip; + if (loop_number >= 0) { // In case no perimeters are to be generated, loop_number will equal to -1. std::vector contours(loop_number+1); // depth => loops @@ -1467,7 +1545,51 @@ void PerimeterGenerator::process_classic( } } } + last = std::move(offsets); + + // SuperSlicer: store surface for top infill if only_one_perimeter_top + if (i == 0 && i != loop_number && params.config.only_one_perimeter_top && upper_slices != NULL) { + //split the polygons with top/not_top + //get the offset from solid surface anchor + coord_t offset_top_surface = scale_(1.5 * (params.config.perimeters.value == 0 ? 0. : unscaled(double(ext_perimeter_width + perimeter_spacing * int(int(params.config.perimeters.value) - int(1)))))); + // if possible, try to not push the extra perimeters inside the sparse infill + if (offset_top_surface > 0.9 * (params.config.perimeters.value <= 1 ? 0. : (perimeter_spacing * (params.config.perimeters.value - 1)))) + offset_top_surface -= coord_t(0.9 * (params.config.perimeters.value <= 1 ? 0. : (perimeter_spacing * (params.config.perimeters.value - 1)))); + else + offset_top_surface = 0; + //don't takes into account too thin areas + double min_width_top_surface = std::max(double(ext_perimeter_spacing / 2 + 10), scale_d(params.config.min_width_top_surface.get_abs_value(unscaled(perimeter_width)))); + ExPolygons grown_upper_slices = offset_ex(*upper_slices, min_width_top_surface); + //set the clip to a virtual "second perimeter" + fill_clip = offset_ex(last, -double(ext_perimeter_spacing)); + // get the real top surface + ExPolygons top_polygons = diff_ex(last, grown_upper_slices, ApplySafetyOffset::Yes); + + //get the not-top surface, from the "real top" but enlarged by external_infill_margin (and the min_width_top_surface we removed a bit before) + ExPolygons inner_polygons = diff_ex(last, offset_ex(top_polygons, offset_top_surface + min_width_top_surface + //also remove the ext_perimeter_spacing/2 width because we are faking the external perimeter, and we will remove ext_perimeter_spacing2 + - double(ext_perimeter_spacing / 2)), ApplySafetyOffset::Yes); + + // get the enlarged top surface, by using inner_polygons instead of upper_slices, and clip it for it to be exactly the polygons to fill. + top_polygons = diff_ex(fill_clip, inner_polygons, ApplySafetyOffset::Yes); + // increase by half peri the inner space to fill the frontier between last and stored. + top_fills = union_ex(top_fills, top_polygons); + //set the clip to the external wall but go back inside by infill_extrusion_width/2 to be sure the extrusion won't go outside even with a 100% overlap. + fill_clip = offset_ex(last, double(ext_perimeter_spacing / 2) - params.config.infill_extrusion_width.get_abs_value(params.solid_infill_flow.nozzle_diameter()) / 2); + last = intersection_ex(inner_polygons, last); + //{ + // std::stringstream stri; + // stri << this->layer->id() << "_1_"<< i <<"_only_one_peri"<< ".svg"; + // SVG svg(stri.str()); + // svg.draw(to_polylines(top_fills), "green"); + // svg.draw(to_polylines(inner_polygons), "yellow"); + // svg.draw(to_polylines(top_polygons), "cyan"); + // svg.draw(to_polylines(oldLast), "orange"); + // svg.draw(to_polylines(last), "red"); + // svg.Close(); + //} + } if (i == loop_number && (! has_gap_fill || params.config.fill_density.value == 0)) { // The last run of this loop is executed to collect gaps for gap fill. // As the gap fill is either disabled or not @@ -1582,8 +1704,11 @@ void PerimeterGenerator::process_classic( // two or more loops? perimeter_spacing / 2; // only apply infill overlap if we actually have one perimeter - if (inset > 0) - inset -= coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale(inset + solid_infill_spacing / 2)))); + coord_t infill_peri_overlap = 0; + if (inset > 0) { + infill_peri_overlap = coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale(inset + solid_infill_spacing / 2)))); + inset -= infill_peri_overlap; + } // simplify infill contours according to resolution Polygons pp; for (ExPolygon &ex : last) @@ -1597,6 +1722,11 @@ void PerimeterGenerator::process_classic( float(- inset - min_perimeter_infill_spacing / 2.), float(min_perimeter_infill_spacing / 2.)); + ExPolygons top_infill_areas = intersection_ex(fill_clip, offset_ex(top_fills, double(ext_perimeter_spacing / 2))); + if (!top_fills.empty()) { + infill_areas = union_ex(infill_areas, offset_ex(top_infill_areas, double(infill_peri_overlap))); + } + if (lower_slices != nullptr && params.config.overhangs && params.config.extra_perimeters_on_overhangs && params.config.perimeters > 0 && params.layer_id > params.object_config.raft_layers) { // Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material @@ -1619,4 +1749,90 @@ void PerimeterGenerator::process_classic( append(out_fill_expolygons, std::move(infill_areas)); } +//Orca +void PerimeterGenerator::split_top_surfaces(const Parameters ¶ms,const ExPolygons *lower_slices,const ExPolygons *upper_slices,const ExPolygons &orig_polygons, ExPolygons &top_fills, + ExPolygons &non_top_polygons, ExPolygons &fill_clip) { + // other perimeters + coord_t perimeter_width = params.perimeter_flow.scaled_width(); + coord_t perimeter_spacing = params.perimeter_flow.scaled_spacing(); + + // external perimeters + coord_t ext_perimeter_width = params.ext_perimeter_flow.scaled_width(); + coord_t ext_perimeter_spacing = params.ext_perimeter_flow.scaled_spacing(); + + bool has_gap_fill = params.config.gap_fill_enabled; + + // split the polygons with top/not_top + // get the offset from solid surface anchor + coord_t offset_top_surface = ext_perimeter_width; + + // don't takes into account too thin areas + // skip if the exposed area is smaller than "min_width_top_surface" + double min_width_top_surface = std::max(double(ext_perimeter_spacing / 2 + 10), scale_(params.config.min_width_top_surface.get_abs_value(unscale_(perimeter_width)))); + + Polygons grown_upper_slices = offset(*upper_slices, min_width_top_surface); + + // get boungding box of last + BoundingBox last_box = get_extents(orig_polygons); + last_box.offset(SCALED_EPSILON); + + // get the Polygons upper the polygon this layer + Polygons upper_polygons_series_clipped = + ClipperUtils::clip_clipper_polygons_with_subject_bbox(grown_upper_slices, last_box); + + // set the clip to a virtual "second perimeter" + fill_clip = offset_ex(orig_polygons, -double(ext_perimeter_spacing)); + // get the real top surface + ExPolygons grown_lower_slices; + ExPolygons bridge_checker; + auto nozzle_diameter = params.print_config.nozzle_diameter.get_at(params.config.perimeter_extruder); + // Check whether surface be bridge or not + if (lower_slices != NULL) { + // BBS: get the Polygons below the polygon this layer + Polygons lower_polygons_series_clipped = + ClipperUtils::clip_clipper_polygons_with_subject_bbox(*lower_slices, last_box); + double bridge_offset = std::max(double(ext_perimeter_spacing), (double(perimeter_width))); + // SoftFever: improve bridging + const float bridge_margin = + std::min(float(scale_(BRIDGE_INFILL_MARGIN)), float(scale_(nozzle_diameter * BRIDGE_INFILL_MARGIN / 0.4))); + bridge_checker = offset_ex(diff_ex(orig_polygons, lower_polygons_series_clipped, ApplySafetyOffset::Yes), + 1.5 * bridge_offset + bridge_margin + perimeter_spacing / 2); + } + ExPolygons delete_bridge = diff_ex(orig_polygons, bridge_checker, ApplySafetyOffset::Yes); + + ExPolygons top_polygons = diff_ex(delete_bridge, upper_polygons_series_clipped, ApplySafetyOffset::Yes); + // get the not-top surface, from the "real top" but enlarged by external_infill_margin (and the + // min_width_top_surface we removed a bit before) + ExPolygons temp_gap = diff_ex(top_polygons, fill_clip); + ExPolygons inner_polygons = + diff_ex(orig_polygons, + offset_ex(top_polygons, offset_top_surface + min_width_top_surface - double(ext_perimeter_spacing / 2)), + ApplySafetyOffset::Yes); + // get the enlarged top surface, by using inner_polygons instead of upper_slices, and clip it for it to be exactly + // the polygons to fill. + top_polygons = diff_ex(fill_clip, inner_polygons, ApplySafetyOffset::Yes); + // increase by half peri the inner space to fill the frontier between last and stored. + top_fills = union_ex(top_fills, top_polygons); + //set the clip to the external wall but go back inside by infill_extrusion_width/2 to be sure the extrusion won't go outside even with a 100% overlap. + double infill_spacing_unscaled = params.config.infill_extrusion_width.get_abs_value(nozzle_diameter); + if (infill_spacing_unscaled == 0) infill_spacing_unscaled = Flow::auto_extrusion_width(frInfill, nozzle_diameter); + fill_clip = offset_ex(orig_polygons, double(ext_perimeter_spacing / 2) - scale_(infill_spacing_unscaled / 2)); + // ExPolygons oldLast = last; + + non_top_polygons = intersection_ex(inner_polygons, orig_polygons); + if (has_gap_fill) + non_top_polygons = union_ex(non_top_polygons, temp_gap); + //{ + // std::stringstream stri; + // stri << this->layer_id << "_1_"<< i <<"_only_one_peri"<< ".svg"; + // SVG svg(stri.str()); + // svg.draw(to_polylines(top_fills), "green"); + // svg.draw(to_polylines(inner_polygons), "yellow"); + // svg.draw(to_polylines(top_polygons), "cyan"); + // svg.draw(to_polylines(oldLast), "orange"); + // svg.draw(to_polylines(last), "red"); + // svg.Close(); + //} +} + } diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp index 0555327b79b..1fc9e0c6ec5 100644 --- a/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -76,6 +76,7 @@ void process_classic( const Parameters ¶ms, const Surface &surface, const ExPolygons *lower_slices, + const ExPolygons *upper_slices, // Cache: Polygons &lower_slices_polygons_cache, // Output: @@ -91,6 +92,7 @@ void process_arachne( const Parameters ¶ms, const Surface &surface, const ExPolygons *lower_slices, + const ExPolygons *upper_slices, // Cache: Polygons &lower_slices_polygons_cache, // Output: @@ -103,6 +105,9 @@ void process_arachne( ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, float tolerance, float merge_tolerance); +void split_top_surfaces(const Parameters ¶ms, const ExPolygons *lower_slices,const ExPolygons *upper_slices, const ExPolygons &orig_polygons, ExPolygons &top_fills, + ExPolygons &non_top_polygons, ExPolygons &fill_clip); + } // namespace PerimeterGenerator } // namespace Slic3r diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 1da25d5dfc9..afaf9f4c718 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -437,6 +437,7 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config) static std::vector s_Preset_print_options { "layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "slicing_mode", + "only_one_perimeter_first", "top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness", "extra_perimeters", "extra_perimeters_on_overhangs", "avoid_crossing_curled_overhangs", "avoid_crossing_perimeters", "thin_walls", "overhangs", "seam_position","staggered_inner_seams", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern", @@ -476,7 +477,9 @@ static std::vector s_Preset_print_options { "wipe_tower_width", "wipe_tower_cone_angle", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth", "wipe_tower_extruder", "wipe_tower_no_sparse_layers", "wipe_tower_extra_flow", "wipe_tower_extra_spacing", "compatible_printers", "compatible_printers_condition", "inherits", "perimeter_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", - "wall_distribution_count", "min_feature_size", "min_bead_width" + "wall_distribution_count", "min_feature_size", "min_bead_width", + // SuperSlicer + "only_one_perimeter_top", "min_width_top_surface", }; static std::vector s_Preset_filament_options { diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 1d76a61c601..c767ca864d6 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -604,6 +604,32 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(1)); + def = this->add("only_one_perimeter_first", coBool); + def->label = L("on first layer"); + def->category = L("Layers and Perimeters"); + def->tooltip = L("Print one perimeter on first layer to leave more space for infill. Could be usefull for small details and better look."); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("only_one_perimeter_top", coBool); + def->label = L("On top surfaces"); + def->category = L("Layers and Perimeters"); + def->tooltip = L("Use only one perimeter on flat top surface, to give more space to the top infill pattern"); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("min_width_top_surface", coFloatOrPercent); + def->label = L("Minimum top width for infill"); + def->category = L("Layers and Perimeters"); + def->tooltip = L("If a top surface has to be printed and it's partially covered by another layer, it won't be considered at a top layer where its width is below this value." + " This can be useful to not let the 'one perimeter on top' trigger on surface that should be covered only by perimeters." + " This value can be a mm or a % of the perimeter extrusion width." + "\nWarning: If enabled, artifacts can be created is you have some thin features on the next layer, like letters. Set this setting to 0 to remove these artifacts."); + def->sidetext = L("mm or %"); + def->ratio_over = "perimeter_extrusion_width"; + def->min = 0; + def->max_literal = 15; + def->mode = comExpert; + def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); + def = this->add("bridge_speed", coFloat); def->label = L("Bridges"); def->category = L("Speed"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index cd8046e7bef..7315b47f2c5 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -666,6 +666,10 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, top_solid_min_thickness)) ((ConfigOptionFloatOrPercent, top_solid_infill_speed)) ((ConfigOptionBool, wipe_into_infill)) + ((ConfigOptionBool, only_one_perimeter_first)) + // SuperSlicer + ((ConfigOptionBool, only_one_perimeter_top)) + ((ConfigOptionFloatOrPercent, min_width_top_surface)) ) PRINT_CONFIG_CLASS_DEFINE( diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index ba1b33127b5..e18328c9bfc 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -712,8 +712,12 @@ bool PrintObject::invalidate_state_by_config_options( steps.emplace_back(posSupportSpotsSearch); // Brim is printed below supports, support invalidates brim and skirt. steps.emplace_back(posSupportMaterial); + } else if ( + opt_key == "only_one_perimeter_top") { + steps.emplace_back(posPerimeters); } else if ( opt_key == "perimeters" + || opt_key == "only_one_perimeter_first" || opt_key == "extra_perimeters" || opt_key == "extra_perimeters_on_overhangs" || opt_key == "first_layer_extrusion_width" diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 4b39b4bc401..47b13b31a74 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -68,6 +68,7 @@ static constexpr double EPSILON = 1e-4; // int32_t fits an interval of (-2147.48mm, +2147.48mm) // with int64_t we don't have to worry anymore about the size of the int. static constexpr double SCALING_FACTOR = 0.000001; +static constexpr double UNSCALING_FACTOR = 1000000; // 1 / SCALING_FACTOR; <- linux has some problem compiling this constexpr static constexpr double PI = 3.141592653589793238; // When extruding a closed loop, the loop is interrupted and shortened a bit to reduce the seam. static constexpr double LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER = 0.15; @@ -77,9 +78,11 @@ static constexpr double INSET_OVERLAP_TOLERANCE = 0.4; // 3mm ring around the top / bottom / bridging areas. //FIXME This is quite a lot. static constexpr double EXTERNAL_INFILL_MARGIN = 3.; +static constexpr double BRIDGE_INFILL_MARGIN = 1.; //FIXME Better to use an inline function with an explicit return type. //inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); } #define scale_(val) ((val) / SCALING_FACTOR) +#define unscale_(val) ((val) * SCALING_FACTOR) #define SCALED_EPSILON scale_(EPSILON) @@ -107,6 +110,8 @@ using deque = template inline T unscale(Q v) { return T(v) * T(SCALING_FACTOR); } +inline coordf_t scale_d(double v) { return coordf_t(v * UNSCALING_FACTOR); } + enum Axis { X=0, Y, diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index d378788bd72..19198f0caf4 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -240,9 +240,12 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) bool have_perimeters = config->opt_int("perimeters") > 0; for (auto el : { "extra_perimeters","extra_perimeters_on_overhangs", "thin_walls", "overhangs", "seam_position","staggered_inner_seams", "external_perimeters_first", "external_perimeter_extrusion_width", - "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "enable_dynamic_overhang_speeds"}) + "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "enable_dynamic_overhang_speeds", + "only_one_perimeter_first", "only_one_perimeter_top"}) toggle_field(el, have_perimeters); + toggle_field("min_width_top_surface", have_perimeters && config->opt_bool("only_one_perimeter_top")); + for (size_t i = 0; i < 4; i++) { toggle_field("overhang_speed_" + std::to_string(i), config->opt_bool("enable_dynamic_overhang_speeds")); } @@ -368,6 +371,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("min_feature_size", have_arachne); toggle_field("min_bead_width", have_arachne); toggle_field("thin_walls", !have_arachne); + } void ConfigManipulation::toggle_print_sla_options(DynamicPrintConfig* config) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3114fbcc75e..4bf9095bc4a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2134,7 +2134,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) "extruder_colour", "filament_colour", "material_colour", "max_print_height", "printer_model", "printer_notes", "printer_technology", // These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor. "layer_height", "first_layer_height", "min_layer_height", "max_layer_height", - "brim_width", "perimeters", "perimeter_extruder", "fill_density", "infill_extruder", "top_solid_layers", + "brim_width", "perimeters", "only_one_perimeter_first", "perimeter_extruder", "fill_density", "infill_extruder", "top_solid_layers", "support_material", "support_material_extruder", "support_material_interface_extruder", "support_material_contact_distance", "support_material_bottom_contact_distance", "raft_layers" })) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 894b04fbf9f..2ee3d9ead1a 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1472,6 +1472,12 @@ void TabPrint::build() optgroup->append_line(line); optgroup = page->new_optgroup(L("Quality (slower slicing)")); + line = { L("Only one perimeter"), "" }; + line.label_path = category_path + "only-one-perimeter-top-bottom"; + line.append_option(optgroup->get_option("only_one_perimeter_first")); + line.append_option(optgroup->get_option("only_one_perimeter_top")); + line.append_option(optgroup->get_option("min_width_top_surface")); + optgroup->append_line(line); optgroup->append_single_option_line("extra_perimeters", category_path + "extra-perimeters-if-needed"); optgroup->append_single_option_line("extra_perimeters_on_overhangs", category_path + "extra-perimeters-on-overhangs"); optgroup->append_single_option_line("avoid_crossing_curled_overhangs", category_path + "avoid-crossing-curled-overhangs"); diff --git a/tests/fff_print/test_perimeters.cpp b/tests/fff_print/test_perimeters.cpp index 4fa344d86cc..c154f5a0c20 100644 --- a/tests/fff_print/test_perimeters.cpp +++ b/tests/fff_print/test_perimeters.cpp @@ -65,6 +65,7 @@ SCENARIO("Perimeter nesting", "[Perimeters]") perimeter_generator_params, surface, nullptr, + nullptr, // cache: lower_layer_polygons_cache, // output: