diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index b468a21b2d..7528ce4d35 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -861,7 +861,7 @@ void PerimeterGenerator::process_classic() // detect how many perimeters must be generated for this island int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops //BBS: set the topmost and bottom most layer to be one wall - if (loop_number > 0 && ((this->object_config->only_one_wall_top && this->upper_slices == nullptr) || (this->object_config->only_one_wall_first_layer && layer_id == 0))) + if (loop_number > 0 && ((this->object_config->top_one_wall_type != TopOneWallType::None && this->upper_slices == nullptr) || (this->object_config->only_one_wall_first_layer && layer_id == 0))) loop_number = 0; ExPolygons last = union_ex(surface.expolygon.simplify_p(surface_simplify_resolution)); @@ -1008,7 +1008,7 @@ void PerimeterGenerator::process_classic() //BBS: refer to superslicer //store surface for top infill if only_one_wall_top - if (i == 0 && i != loop_number && this->object_config->only_one_wall_top && this->upper_slices != NULL) { + if (i == 0 && i != loop_number && this->object_config->top_one_wall_type == TopOneWallType::Alltop && this->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 * (config->wall_loops.value == 0 ? 0. : unscaled(double(ext_perimeter_width + perimeter_spacing * int(int(config->wall_loops.value) - int(1)))))); @@ -1272,6 +1272,39 @@ void PerimeterGenerator::process_classic() } // for each island } +//BBS: +void PerimeterGenerator::add_infill_contour_for_arachne( ExPolygons infill_contour, + int loops, + coord_t ext_perimeter_spacing, + coord_t perimeter_spacing, + coord_t min_perimeter_infill_spacing, + coord_t spacing, + bool is_inner_part) +{ + if( offset_ex(infill_contour, -float(spacing / 2.)).empty() ) + { + infill_contour.clear(); // Infill region is too small, so let's filter it out. + } + + // create one more offset to be used as boundary for fill + // we offset by half the perimeter spacing (to get to the actual infill boundary) + // and then we offset back and forth by half the infill spacing to only consider the + // non-collapsing regions + coord_t insert = (loops < 0) ? 0: ext_perimeter_spacing; + if (is_inner_part || loops > 0) + insert = perimeter_spacing; + + insert = coord_t(scale_(this->config->infill_wall_overlap.get_abs_value(unscale(insert)))); + + Polygons inner_pp; + for (ExPolygon &ex : infill_contour) + ex.simplify_p(m_scaled_resolution, &inner_pp); + + this->fill_surfaces->append(offset2_ex(union_ex(inner_pp), float(-min_perimeter_infill_spacing / 2.), float(insert + min_perimeter_infill_spacing / 2.)), stInternal); + + append(*this->fill_no_overlap, offset2_ex(union_ex(inner_pp), float(-min_perimeter_infill_spacing / 2.), float(+min_perimeter_infill_spacing / 2.))); +} + // Thanks, Cura developers, for implementing an algorithm for generating perimeters with variable width (Arachne) that is based on the paper // "A framework for adaptive width control of dense contour-parallel toolpaths in fused deposition modeling" void PerimeterGenerator::process_arachne() @@ -1308,8 +1341,9 @@ void PerimeterGenerator::process_arachne() // extra perimeters for each one for (const Surface& surface : this->slices->surfaces) { // detect how many perimeters must be generated for this island - int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops - if (loop_number > 0 && this->object_config->only_one_wall_first_layer && layer_id == 0) + int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops + if (loop_number > 0 && this->object_config->only_one_wall_first_layer && layer_id == 0 || + (this->object_config->top_one_wall_type == TopOneWallType::Topmost && this->upper_slices == nullptr)) loop_number = 0; ExPolygons last = offset_ex(surface.expolygon.simplify_p(surface_simplify_resolution), -float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.)); @@ -1334,10 +1368,76 @@ void PerimeterGenerator::process_arachne() input_params.wall_distribution_count = this->object_config->wall_distribution_count.value; } + int remain_loops = -1; + if (this->object_config->top_one_wall_type == TopOneWallType::Alltop) { + if (this->upper_slices != nullptr) + remain_loops = loop_number - 1; + + loop_number = 0; + } + Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(loop_number + 1), 0, layer_height, input_params); std::vector perimeters = wallToolPaths.getToolPaths(); loop_number = int(perimeters.size()) - 1; + //BBS: top one wall for arachne + ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour()); + ExPolygons inner_infill_contour; + + if( remain_loops >= 0 ) + { + ExPolygons the_layer_surface = infill_contour; + // BBS: get boungding box of last + BoundingBox infill_contour_box = get_extents(infill_contour); + infill_contour_box.offset(SCALED_EPSILON); + + // BBS: get the Polygons upper the polygon this layer + Polygons upper_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*this->upper_slices, infill_contour_box); + + infill_contour = diff_ex(infill_contour, upper_polygons_series_clipped); + + coord_t perimeter_width = this->perimeter_flow.scaled_width(); + //BBS: add bridge area + if (this->lower_slices != nullptr) { + BoundingBox infill_contour_box = get_extents(infill_contour); + infill_contour_box.offset(SCALED_EPSILON); + // BBS: get the Polygons below the polygon this layer + Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*this->lower_slices, infill_contour_box); + + ExPolygons bridge_area = offset_ex(diff_ex(infill_contour, lower_polygons_series_clipped), std::max(ext_perimeter_spacing, perimeter_width)); + infill_contour = diff_ex(infill_contour, bridge_area); + } + //BBS: filter small area and extend top surface a bit to hide the wall line + double min_width_top_surface = std::max(double(ext_perimeter_spacing / 4 + 10), double(perimeter_width / 4)); + infill_contour = offset2_ex(infill_contour, -min_width_top_surface, min_width_top_surface + perimeter_width); + + //BBS: get the inner surface that not export to top + ExPolygons surface_not_export_to_top = diff_ex(the_layer_surface, infill_contour); + + //BBS: get real top surface + infill_contour = intersection_ex(infill_contour, the_layer_surface); + Polygons surface_not_export_to_top_p = to_polygons(surface_not_export_to_top); + Arachne::WallToolPaths innerWallToolPaths(surface_not_export_to_top_p, ext_perimeter_spacing, perimeter_spacing, coord_t(remain_loops + 1), 0, layer_height, input_params); + + std::vector perimeters_inner = innerWallToolPaths.getToolPaths(); + remain_loops = int(perimeters_inner.size()) - 1; + + //BBS: set wall's perporsity + for (int perimeter_idx = 0; perimeter_idx < perimeters_inner.size(); perimeter_idx++) { + if (perimeters_inner[perimeter_idx].empty()) + continue; + + for (Arachne::ExtrusionLine &wall : perimeters_inner[perimeter_idx]) { + //BBS: 0 means outer wall + wall.inset_idx++; + wall.is_odd = !wall.is_odd; + } + } + perimeters.insert(perimeters.end(), perimeters_inner.begin(), perimeters_inner.end()); + + inner_infill_contour = union_ex(innerWallToolPaths.getInnerContour()); + } + #ifdef ARACHNE_DEBUG { static int iRun = 0; @@ -1503,44 +1603,17 @@ void PerimeterGenerator::process_arachne() if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions); !extrusion_coll.empty()) this->loops->append(extrusion_coll); - ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour()); const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing; - if (offset_ex(infill_contour, -float(spacing / 2.)).empty()) - infill_contour.clear(); // Infill region is too small, so let's filter it out. - // create one more offset to be used as boundary for fill - // we offset by half the perimeter spacing (to get to the actual infill boundary) - // and then we offset back and forth by half the infill spacing to only consider the - // non-collapsing regions - coord_t inset = - (loop_number < 0) ? 0 : - (loop_number == 0) ? - // one loop - ext_perimeter_spacing : - // two or more loops? - perimeter_spacing; - - inset = coord_t(scale_(this->config->infill_wall_overlap.get_abs_value(unscale(inset)))); - Polygons pp; - for (ExPolygon& ex : infill_contour) - ex.simplify_p(m_scaled_resolution, &pp); // collapse too narrow infill areas const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE)); // append infill areas to fill_surfaces - this->fill_surfaces->append( - offset2_ex( - union_ex(pp), - float(-min_perimeter_infill_spacing / 2.), - float(inset + min_perimeter_infill_spacing / 2.)), - stInternal); + add_infill_contour_for_arachne(infill_contour, loop_number, ext_perimeter_spacing, perimeter_spacing, min_perimeter_infill_spacing, spacing, false); + + //BBS: add infill_contour of top one wall part + if( !inner_infill_contour.empty() ) + add_infill_contour_for_arachne(inner_infill_contour, remain_loops, ext_perimeter_spacing, perimeter_spacing, min_perimeter_infill_spacing, spacing, true); - // BBS: get the no-overlap infill expolygons - { - append(*this->fill_no_overlap, offset2_ex( - union_ex(pp), - float(-min_perimeter_infill_spacing / 2.), - float(+min_perimeter_infill_spacing / 2.))); - } } } diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp index c772a05634..ca0f2aa8d4 100644 --- a/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -69,6 +69,8 @@ public: void process_classic(); void process_arachne(); + void add_infill_contour_for_arachne( ExPolygons infill_contour, int loops, coord_t ext_perimeter_spacing, coord_t perimeter_spacing, coord_t min_perimeter_infill_spacing, coord_t spacing, bool is_inner_part ); + double ext_mm3_per_mm() const { return m_ext_mm3_per_mm; } double mm3_per_mm() const { return m_mm3_per_mm; } double mm3_per_mm_overhang() const { return m_mm3_per_mm_overhang; } diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index c7ea9f57d6..bbea9d1980 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -777,7 +777,7 @@ static std::vector s_Preset_print_options { "detect_narrow_internal_solid_infill", "gcode_add_line_number", "enable_arc_fitting", "infill_combination", /*"adaptive_layer_height",*/ "support_bottom_interface_spacing", "enable_overhang_speed", "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed", - "initial_layer_infill_speed", "only_one_wall_top", "only_one_wall_first_layer", + "initial_layer_infill_speed", "top_one_wall_type", "only_one_wall_first_layer", "timelapse_type", "internal_bridge_support_thickness", "wall_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", "wall_distribution_count", "min_feature_size", "min_bead_width", "post_process", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 31802e9876..6f70020f8a 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -145,6 +145,14 @@ static t_config_enum_values s_keys_map_IroningType { }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(IroningType) +//BBS: +static t_config_enum_values s_keys_map_TopOneWallType { + {"not apply", int(TopOneWallType::None)}, + {"all top", int(TopOneWallType::Alltop)}, + {"topmost", int(TopOneWallType::Topmost)} +}; +CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(TopOneWallType) + //BBS static t_config_enum_values s_keys_map_WallInfillOrder { { "inner wall/outer wall/infill", int(WallInfillOrder::InnerOuterInfill) }, @@ -721,11 +729,18 @@ void PrintConfigDef::init_fff_params() def->mode = comDevelop; def->set_default_value(new ConfigOptionFloat(1)); - def = this->add("only_one_wall_top", coBool); - def->label = L("Only one wall on top surfaces"); + def = this->add("top_one_wall_type", coEnum); + def->label = L("Only one wall type"); def->category = L("Quality"); - def->tooltip = L("Use only one wall on flat top surface, to give more space to the top infill pattern"); - def->set_default_value(new ConfigOptionBool(false)); + def->tooltip = L("Use only one wall on flat top surface, to give more space to the top infill pattern. Could be applyed on topmost surface or all top surface."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("not apply"); + def->enum_values.push_back("all top"); + def->enum_values.push_back("topmost"); + def->enum_labels.push_back(L("Not apply")); + def->enum_labels.push_back(L("Top surfaces")); + def->enum_labels.push_back(L("Topmost surface")); + def->set_default_value(new ConfigOptionEnum(TopOneWallType::Alltop)); def = this->add("only_one_wall_first_layer", coBool); def->label = L("Only one wall on first layer"); @@ -4238,7 +4253,7 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va "remove_freq_sweep", "remove_bed_leveling", "remove_extrusion_calibration", "support_transition_line_width", "support_transition_speed", "bed_temperature", "bed_temperature_initial_layer", "can_switch_nozzle_type", "can_add_auxiliary_fan", "extra_flush_volume", "spaghetti_detector", "adaptive_layer_height", - "z_hop_type","nozzle_hrc","chamber_temperature" + "z_hop_type","nozzle_hrc","chamber_temperature","only_one_wall_top" }; if (ignore.find(opt_key) != ignore.end()) { diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 96a6e37776..cd699f9dd0 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -174,6 +174,13 @@ enum class PerimeterGeneratorType Arachne }; +enum class TopOneWallType +{ + None, + Alltop, + Topmost +}; + // BBS enum OverhangFanThreshold { Overhang_threshold_none = 0, @@ -304,7 +311,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ForwardCompatibilitySubstitutionRule) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PrintHostType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(AuthorizationType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PerimeterGeneratorType) - +CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(TopOneWallType) #undef CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS // Defines each and every confiuration option of Slic3r, including the properties of the GUI dialogs. @@ -704,7 +711,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionInt, wall_distribution_count)) ((ConfigOptionPercent, min_feature_size)) ((ConfigOptionPercent, min_bead_width)) - ((ConfigOptionBool, only_one_wall_top)) + ((ConfigOptionEnum, top_one_wall_type)) ((ConfigOptionBool, only_one_wall_first_layer)) // OrcaSlicer ((ConfigOptionPercent, seam_gap)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index f10532843b..f76c0e0c64 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -709,7 +709,7 @@ bool PrintObject::invalidate_state_by_config_options( } } else if ( opt_key == "wall_loops" - || opt_key == "only_one_wall_top" + || opt_key == "top_one_wall_type" || opt_key == "only_one_wall_first_layer" || opt_key == "initial_layer_line_width" || opt_key == "inner_wall_line_width" diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index af7e7f8706..06e810d5a5 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -682,8 +682,6 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co "min_feature_size", "min_bead_width", "wall_distribution_count" }) toggle_line(el, have_arachne); toggle_field("detect_thin_wall", !have_arachne); - //toggle_field("enable_overhang_speed", !have_arachne); - toggle_field("only_one_wall_top", !have_arachne); PresetBundle *preset_bundle = wxGetApp().preset_bundle; // OrcaSlicer diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index aab70e23b1..9b52351cd5 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1881,7 +1881,7 @@ void TabPrint::build() optgroup->append_single_option_line("thick_bridges"); optgroup->append_single_option_line("top_solid_infill_flow_ratio"); optgroup->append_single_option_line("initial_layer_flow_ratio"); - optgroup->append_single_option_line("only_one_wall_top"); + optgroup->append_single_option_line("top_one_wall_type"); optgroup->append_single_option_line("only_one_wall_first_layer"); optgroup->append_single_option_line("detect_overhang_wall"); optgroup->append_single_option_line("reduce_crossing_wall");