From c9ddf7315ed8f17ef43ad5e3c0ab33fd0adb32b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 5 Aug 2022 08:16:45 +0200 Subject: [PATCH] Replaced parameters wall_add_middle_threshold and wall_split_middle_threshold with automatic computation based on extrusion width. That allows computed better values in cases when the extrusion width of the first layer or for the infill differs from the extrusion width for perimeters. Parameters min_feature_size and wall_transition_length now can be set in percentages, and then they will be calculated based on nozzle size. Old profiles with default values are forced to replace old default values with new default values in percentages. --- src/libslic3r/Arachne/WallToolPaths.cpp | 37 +++++++++++------- src/libslic3r/Arachne/WallToolPaths.hpp | 5 ++- src/libslic3r/Fill/Fill.cpp | 1 + src/libslic3r/Fill/FillBase.hpp | 2 + src/libslic3r/Fill/FillConcentric.cpp | 4 +- src/libslic3r/Format/3mf.cpp | 23 ++++++++++-- src/libslic3r/PerimeterGenerator.cpp | 2 +- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 50 ++++++------------------- src/libslic3r/PrintConfig.hpp | 6 +-- src/libslic3r/PrintObject.cpp | 2 - src/slic3r/GUI/ConfigManipulation.cpp | 2 - src/slic3r/GUI/Tab.cpp | 2 - tests/libslic3r/test_arachne.cpp | 4 +- 14 files changed, 70 insertions(+), 72 deletions(-) diff --git a/src/libslic3r/Arachne/WallToolPaths.cpp b/src/libslic3r/Arachne/WallToolPaths.cpp index 11254d828..3552f6680 100644 --- a/src/libslic3r/Arachne/WallToolPaths.cpp +++ b/src/libslic3r/Arachne/WallToolPaths.cpp @@ -24,31 +24,37 @@ namespace Slic3r::Arachne { WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t bead_width_0, const coord_t bead_width_x, - const size_t inset_count, const coord_t wall_0_inset, const PrintObjectConfig &print_object_config, const PrintConfig &print_config) + const size_t inset_count, const coord_t wall_0_inset, const coordf_t layer_height, + const PrintObjectConfig &print_object_config, const PrintConfig &print_config) : outline(outline) , bead_width_0(bead_width_0) , bead_width_x(bead_width_x) , inset_count(inset_count) , wall_0_inset(wall_0_inset) + , layer_height(layer_height) , print_thin_walls(Slic3r::Arachne::fill_outline_gaps) , min_feature_size(scaled(print_object_config.min_feature_size.value)) , min_bead_width(scaled(print_object_config.min_bead_width.value)) , small_area_length(static_cast(bead_width_0) / 2.) , wall_transition_filter_deviation(scaled(print_object_config.wall_transition_filter_deviation.value)) + , wall_transition_length(scaled(print_object_config.wall_transition_length.value)) , toolpaths_generated(false) , print_object_config(print_object_config) { - if (const auto &min_bead_width_opt = print_object_config.min_bead_width; min_bead_width_opt.percent) { - assert(!print_config.nozzle_diameter.empty()); - double min_nozzle_diameter = *std::min_element(print_config.nozzle_diameter.values.begin(), print_config.nozzle_diameter.values.end()); - this->min_bead_width = scaled(min_bead_width_opt.value * 0.01 * min_nozzle_diameter); - } + assert(!print_config.nozzle_diameter.empty()); + this->min_nozzle_diameter = float(*std::min_element(print_config.nozzle_diameter.values.begin(), print_config.nozzle_diameter.values.end())); - if (const auto &wall_transition_filter_deviation_opt = print_object_config.wall_transition_filter_deviation; wall_transition_filter_deviation_opt.percent) { - assert(!print_config.nozzle_diameter.empty()); - double min_nozzle_diameter = *std::min_element(print_config.nozzle_diameter.values.begin(), print_config.nozzle_diameter.values.end()); - this->wall_transition_filter_deviation = scaled(wall_transition_filter_deviation_opt.value * 0.01 * min_nozzle_diameter); - } + if (const auto &min_feature_size_opt = print_object_config.min_feature_size; min_feature_size_opt.percent) + this->min_feature_size = scaled(min_feature_size_opt.value * 0.01 * this->min_nozzle_diameter); + + if (const auto &min_bead_width_opt = print_object_config.min_bead_width; min_bead_width_opt.percent) + this->min_bead_width = scaled(min_bead_width_opt.value * 0.01 * this->min_nozzle_diameter); + + if (const auto &wall_transition_filter_deviation_opt = print_object_config.wall_transition_filter_deviation; wall_transition_filter_deviation_opt.percent) + this->wall_transition_filter_deviation = scaled(wall_transition_filter_deviation_opt.value * 0.01 * this->min_nozzle_diameter); + + if (const auto &wall_transition_length_opt = print_object_config.wall_transition_length; wall_transition_length_opt.percent) + this->wall_transition_length = scaled(wall_transition_length_opt.value * 0.01 * this->min_nozzle_diameter); } void simplify(Polygon &thiss, const int64_t smallest_line_segment_squared, const int64_t allowed_error_distance_squared) @@ -490,9 +496,12 @@ const std::vector &WallToolPaths::generate() return toolpaths; } - const coord_t wall_transition_length = scaled(this->print_object_config.wall_transition_length.value); - const double wall_split_middle_threshold = this->print_object_config.wall_split_middle_threshold.value / 100.; // For an uneven nr. of lines: When to split the middle wall into two. - const double wall_add_middle_threshold = this->print_object_config.wall_add_middle_threshold.value / 100.; // For an even nr. of lines: When to add a new middle in between the innermost two walls. + const float external_perimeter_extrusion_width = Flow::rounded_rectangle_extrusion_width_from_spacing(unscale(bead_width_0), float(this->layer_height)); + const float perimeter_extrusion_width = Flow::rounded_rectangle_extrusion_width_from_spacing(unscale(bead_width_x), float(this->layer_height)); + + const double wall_split_middle_threshold = std::clamp(2. * unscaled(this->min_bead_width) / external_perimeter_extrusion_width - 1., 0.01, 0.99); // For an uneven nr. of lines: When to split the middle wall into two. + const double wall_add_middle_threshold = std::clamp(unscaled(this->min_bead_width) / perimeter_extrusion_width, 0.01, 0.99); // For an even nr. of lines: When to add a new middle in between the innermost two walls. + const int wall_distribution_count = this->print_object_config.wall_distribution_count.value; const size_t max_bead_count = (inset_count < std::numeric_limits::max() / 2) ? 2 * inset_count : std::numeric_limits::max(); const auto beading_strat = BeadingStrategyFactory::makeStrategy diff --git a/src/libslic3r/Arachne/WallToolPaths.hpp b/src/libslic3r/Arachne/WallToolPaths.hpp index ed62d9a98..b0bed1241 100644 --- a/src/libslic3r/Arachne/WallToolPaths.hpp +++ b/src/libslic3r/Arachne/WallToolPaths.hpp @@ -31,7 +31,7 @@ public: * \param inset_count The maximum number of parallel extrusion lines that make up the wall * \param wall_0_inset How far to inset the outer wall, to make it adhere better to other walls. */ - WallToolPaths(const Polygons& outline, coord_t bead_width_0, coord_t bead_width_x, size_t inset_count, coord_t wall_0_inset, const PrintObjectConfig &print_object_config, const PrintConfig &print_config); + WallToolPaths(const Polygons& outline, coord_t bead_width_0, coord_t bead_width_x, size_t inset_count, coord_t wall_0_inset, coordf_t layer_height, const PrintObjectConfig &print_object_config, const PrintConfig &print_config); /*! * Generates the Toolpaths @@ -110,11 +110,14 @@ private: coord_t bead_width_x; // toolpaths; //layer()->height; for (ExPolygon &expoly : surface_fill.expolygons) { // Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon. diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index b07cca25e..88f437b9d 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -61,6 +61,8 @@ struct FillParams // For Concentric infill, to switch between Classic and Arachne. bool use_arachne { false }; + // Layer height for Concentric infill with Arachne. + coordf_t layer_height { 0.f }; }; static_assert(IsTriviallyCopyable::value, "FillParams class is not POD (and it should be - see constructor)."); diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp index f692babc6..17bdfafaf 100644 --- a/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -77,8 +77,8 @@ void FillConcentric::_fill_surface_single(const FillParams ¶ms, if (params.density > 0.9999f && !params.dont_adjust) { coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / min_spacing + 1; - Polygons polygons = offset(expolygon, min_spacing / 2); - Arachne::WallToolPaths wallToolPaths(polygons, min_spacing, min_spacing, loops_count, 0, *this->print_object_config, *this->print_config); + Polygons polygons = offset(expolygon, float(min_spacing) / 2.f); + Arachne::WallToolPaths wallToolPaths(polygons, min_spacing, min_spacing, loops_count, 0, params.layer_height, *this->print_object_config, *this->print_config); std::vector loops = wallToolPaths.getToolPaths(); std::vector all_extrusions; diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 446aa38ba..bf6479ddf 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -454,6 +454,7 @@ namespace Slic3r { bool load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, bool check_version); unsigned int version() const { return m_version; } + boost::optional prusaslicer_generator_version() const { return m_prusaslicer_generator_version; } private: void _destroy_xml_parser(); @@ -3098,8 +3099,7 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv } // Perform conversions based on the config values available. -//FIXME provide a version of PrusaSlicer that stored the project file (3MF). -static void handle_legacy_project_loaded(unsigned int version_project_file, DynamicPrintConfig& config) +static void handle_legacy_project_loaded(unsigned int version_project_file, DynamicPrintConfig& config, const boost::optional& prusaslicer_generator_version) { if (! config.has("brim_separation")) { if (auto *opt_elephant_foot = config.option("elefant_foot_compensation", false); opt_elephant_foot) { @@ -3108,6 +3108,23 @@ static void handle_legacy_project_loaded(unsigned int version_project_file, Dyna opt_brim_separation->value = opt_elephant_foot->value; } } + + // In PrusaSlicer 2.5.0-alpha2 and 2.5.0-alpha3, we introduce several parameters for Arachne that depend + // on nozzle size . Later we decided to make default values for those parameters computed automatically + // until the user changes them. + if (prusaslicer_generator_version && *prusaslicer_generator_version >= *Semver::parse("2.5.0-alpha2") && *prusaslicer_generator_version <= *Semver::parse("2.5.0-alpha3")) { + if (auto *opt_wall_transition_length = config.option("wall_transition_length", false); + opt_wall_transition_length && !opt_wall_transition_length->percent && opt_wall_transition_length->value == 0.4) { + opt_wall_transition_length->percent = true; + opt_wall_transition_length->value = 100; + } + + if (auto *opt_min_feature_size = config.option("min_feature_size", false); + opt_min_feature_size && !opt_min_feature_size->percent && opt_min_feature_size->value == 0.1) { + opt_min_feature_size->percent = true; + opt_min_feature_size->value = 25; + } + } } bool is_project_3mf(const std::string& filename) @@ -3150,7 +3167,7 @@ bool load_3mf(const char* path, DynamicPrintConfig& config, ConfigSubstitutionCo _3MF_Importer importer; bool res = importer.load_model_from_file(path, *model, config, config_substitutions, check_version); importer.log_errors(); - handle_legacy_project_loaded(importer.version(), config); + handle_legacy_project_loaded(importer.version(), config, importer.prusaslicer_generator_version()); return res; } diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 03f125320..dafa850cd 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -574,7 +574,7 @@ void PerimeterGenerator::process_arachne() ExPolygons last = offset_ex(surface.expolygon.simplify_p(m_scaled_resolution), - float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.)); Polygons last_p = to_polygons(last); - Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(loop_number + 1), 0, *this->object_config, *this->print_config); + Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(loop_number + 1), 0, layer_height, *this->object_config, *this->print_config); std::vector perimeters = wallToolPaths.getToolPaths(); loop_number = int(perimeters.size()) - 1; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 4664cffc1..57386e726 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -448,7 +448,7 @@ static std::vector s_Preset_print_options { "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width", "wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits", "perimeter_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", - "wall_distribution_count", "wall_split_middle_threshold", "wall_add_middle_threshold", "min_feature_size", "min_bead_width" + "wall_distribution_count", "min_feature_size", "min_bead_width" }; static std::vector s_Preset_filament_options { diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index e38ad006c..579f1aebb 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3076,15 +3076,16 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionEnum(PerimeterGeneratorType::Arachne)); - def = this->add("wall_transition_length", coFloat); + def = this->add("wall_transition_length", coFloatOrPercent); def->label = L("Perimeter transition length"); def->category = L("Advanced"); def->tooltip = L("When transitioning between different numbers of perimeters as the part becomes " - "thinner, a certain amount of space is allotted to split or join the perimeter segments."); - def->sidetext = L("mm"); + "thinner, a certain amount of space is allotted to split or join the perimeter segments. " + "If expressed as a percentage (for example 100%), it will be computed based on the nozzle diameter."); + def->sidetext = L("mm or %"); def->mode = comExpert; def->min = 0; - def->set_default_value(new ConfigOptionFloat(0.4)); + def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); def = this->add("wall_transition_filter_deviation", coFloatOrPercent); def->label = L("Perimeter transitioning filter margin"); @@ -3123,46 +3124,17 @@ void PrintConfigDef::init_fff_params() def->min = 1; def->set_default_value(new ConfigOptionInt(1)); - def = this->add("wall_split_middle_threshold", coPercent); - def->label = L("Split middle perimeter threshold"); - def->category = L("Advanced"); - def->tooltip = L("The smallest extrusion width, as a factor of the normal extrusion width, above which the middle " - "perimeter (if there is one) will be split into two. Reduce this setting to use more, thinner " - "perimeters. Increase to use fewer, wider perimeters. Note that this applies -as if- the entire " - "shape should be filled with perimeter, so the middle here refers to the middle of the object " - "between two outer edges of the shape, even if there actually is infill or other extrusion types in " - "the print instead of the perimeter."); - def->sidetext = L("%"); - def->mode = comAdvanced; - def->min = 1; - def->max = 99; - def->set_default_value(new ConfigOptionPercent(50)); - - def = this->add("wall_add_middle_threshold", coPercent); - def->label = L("Add middle perimeter threshold"); - def->category = L("Advanced"); - def->tooltip = L("The smallest extrusion width, as a factor of the normal extrusion width, above which a middle " - "perimeter (if there wasn't one already) will be added. Reduce this setting to use more, " - "thinner perimeters. Increase to use fewer, wider perimeters. Note that this applies -as if- the " - "entire shape should be filled with perimeter, so the middle here refers to the middle of the " - "object between two outer edges of the shape, even if there actually is infill or other " - "extrusion types in the print instead of the perimeter."); - def->sidetext = L("%"); - def->mode = comAdvanced; - def->min = 1; - def->max = 99; - def->set_default_value(new ConfigOptionPercent(75)); - - def = this->add("min_feature_size", coFloat); + def = this->add("min_feature_size", coFloatOrPercent); def->label = L("Minimum feature size"); def->category = L("Advanced"); def->tooltip = L("Minimum thickness of thin features. Model features that are thinner than this value will " "not be printed, while features thicker than the Minimum feature size will be widened to " - "the Minimum perimeter width."); - def->sidetext = L("mm"); + "the Minimum perimeter width. " + "If expressed as a percentage (for example 25%), it will be computed based on the nozzle diameter."); + def->sidetext = L("mm or %"); def->mode = comExpert; def->min = 0; - def->set_default_value(new ConfigOptionFloat(0.1)); + def->set_default_value(new ConfigOptionFloatOrPercent(25, true)); def = this->add("min_bead_width", coFloatOrPercent); def->label = L("Minimum perimeter width"); @@ -4012,6 +3984,8 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va "serial_port", "serial_speed", // Introduced in some PrusaSlicer 2.3.1 alpha, later renamed or removed. "fuzzy_skin_perimeter_mode", "fuzzy_skin_shape", + // Introduced in PrusaSlicer 2.3.0-alpha2, later replaced by automatic calculation based on extrusion width. + "wall_add_middle_threshold", "wall_split_middle_threshold", }; // In PrusaSlicer 2.3.0-alpha0 the "monotonous" infill was introduced, which was later renamed to "monotonic". diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 4db685d5c..b188e499d 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -491,13 +491,11 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, slice_closing_radius)) ((ConfigOptionEnum, slicing_mode)) ((ConfigOptionEnum, perimeter_generator)) - ((ConfigOptionFloat, wall_transition_length)) + ((ConfigOptionFloatOrPercent, wall_transition_length)) ((ConfigOptionFloatOrPercent, wall_transition_filter_deviation)) ((ConfigOptionFloat, wall_transition_angle)) ((ConfigOptionInt, wall_distribution_count)) - ((ConfigOptionPercent, wall_split_middle_threshold)) - ((ConfigOptionPercent, wall_add_middle_threshold)) - ((ConfigOptionFloat, min_feature_size)) + ((ConfigOptionFloatOrPercent, min_feature_size)) ((ConfigOptionFloatOrPercent, min_bead_width)) ((ConfigOptionBool, support_material)) // Automatic supports (generated based on support_material_threshold). diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index aef501922..6ec27ea95 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -673,8 +673,6 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "wall_transition_filter_deviation" || opt_key == "wall_transition_angle" || opt_key == "wall_distribution_count" - || opt_key == "wall_split_middle_threshold" - || opt_key == "wall_add_middle_threshold" || opt_key == "min_feature_size" || opt_key == "min_bead_width") { steps.emplace_back(posSlice); diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index eb6d012dd..13cc35008 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -323,8 +323,6 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("wall_transition_filter_deviation", have_arachne); toggle_field("wall_transition_angle", have_arachne); toggle_field("wall_distribution_count", have_arachne); - toggle_field("wall_split_middle_threshold", have_arachne); - toggle_field("wall_add_middle_threshold", have_arachne); toggle_field("min_feature_size", have_arachne); toggle_field("min_bead_width", have_arachne); toggle_field("thin_walls", !have_arachne); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 2d6f3179e..c9090abd5 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1672,8 +1672,6 @@ void TabPrint::build() optgroup->append_single_option_line("clip_multipart_objects"); optgroup = page->new_optgroup(L("Arachne perimeter generator")); - optgroup->append_single_option_line("wall_add_middle_threshold"); - optgroup->append_single_option_line("wall_split_middle_threshold"); optgroup->append_single_option_line("wall_transition_angle"); optgroup->append_single_option_line("wall_transition_filter_deviation"); optgroup->append_single_option_line("wall_transition_length"); diff --git a/tests/libslic3r/test_arachne.cpp b/tests/libslic3r/test_arachne.cpp index f2c1f7c4b..991fae00e 100644 --- a/tests/libslic3r/test_arachne.cpp +++ b/tests/libslic3r/test_arachne.cpp @@ -58,7 +58,7 @@ TEST_CASE("Arachne - Closed ExtrusionLine", "[ArachneClosedExtrusionLine]") { coord_t spacing = 407079; coord_t inset_count = 8; - Arachne::WallToolPaths wallToolPaths(polygons, spacing, spacing, inset_count, 0, PrintObjectConfig::defaults(), PrintConfig::defaults()); + Arachne::WallToolPaths wallToolPaths(polygons, spacing, spacing, inset_count, 0, 0.2, PrintObjectConfig::defaults(), PrintConfig::defaults()); wallToolPaths.generate(); std::vector perimeters = wallToolPaths.getToolPaths(); @@ -91,7 +91,7 @@ TEST_CASE("Arachne - Missing perimeter - #8472", "[ArachneMissingPerimeter8472]" PrintObjectConfig print_object_config = PrintObjectConfig::defaults(); print_object_config.wall_distribution_count.setInt(3); - Arachne::WallToolPaths wallToolPaths(polygons, spacing, spacing, inset_count, 0, print_object_config, PrintConfig::defaults()); + Arachne::WallToolPaths wallToolPaths(polygons, spacing, spacing, inset_count, 0, 0.2, print_object_config, PrintConfig::defaults()); wallToolPaths.generate(); std::vector perimeters = wallToolPaths.getToolPaths();