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();