diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 28fabed8af..a09c69fc5e 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -1692,9 +1692,9 @@ void Layer::make_ironing() ironing_params.just_infill = false; // ORCA: Get filament-specific overrides if configured, otherwise use process values size_t extruder_idx = ironing_params.extruder - 1; - ironing_params.line_spacing = (!config.filament_ironing_spacing.is_nil(extruder_idx) + ironing_params.line_spacing = std::max(IRONING_SPACING_MIN, !config.filament_ironing_spacing.is_nil(extruder_idx) ? config.filament_ironing_spacing.get_at(extruder_idx) - : config.ironing_spacing); + : config.ironing_spacing.value); ironing_params.inset = (!config.filament_ironing_inset.is_nil(extruder_idx) ? config.filament_ironing_inset.get_at(extruder_idx) : config.ironing_inset); diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp index 1882f7a656..044fee042e 100644 --- a/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -22,6 +22,9 @@ void FillConcentric::_fill_surface_single( coord_t min_spacing = scale_(this->spacing) * params.multiline; coord_t distance = coord_t(min_spacing / params.density); + // A non-positive step never shrinks the region, so the inset loop below would not end. + if (min_spacing <= 0 || distance <= 0) + return; if (params.density > 0.9999f && !params.dont_adjust) { distance = this->_adjust_solid_spacing(bounding_box.size()(0), distance); @@ -108,6 +111,8 @@ void FillConcentric::_fill_surface_single(const FillParams& params, // no rotation is supported for this infill pattern Point bbox_size = expolygon.contour.bounding_box().size(); coord_t min_spacing = scaled(this->spacing); + if (min_spacing <= 0) + return; if (params.density > 0.9999f && !params.dont_adjust) { coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / min_spacing + 1; diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 679811d699..6284e5bc7a 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -178,6 +178,10 @@ enum class IroningType { Count, }; +// Smallest usable ironing line spacing. Anything tighter yields an unprintable number of lines, +// and zero stops the fillers from making progress. +constexpr double IRONING_SPACING_MIN = 0.05; + //BBS enum class WallInfillOrder { InnerOuterInfill, diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp index 8d43edb444..e9151c0c95 100644 --- a/src/libslic3r/Support/SupportParameters.hpp +++ b/src/libslic3r/Support/SupportParameters.hpp @@ -64,7 +64,7 @@ struct SupportParameters { this->ironing = object_config.support_ironing; this->ironing_flow = support_material_interface_flow.with_height(support_material_interface_flow.height() * 0.01 * object_config.support_ironing_flow.value); - this->ironing_spacing = object_config.support_ironing_spacing; + this->ironing_spacing = std::max(IRONING_SPACING_MIN, object_config.support_ironing_spacing.value); this->ironing_pattern = object_config.support_ironing_pattern; // Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um. diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 5a58ec7b6c..6bb73281a5 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -210,6 +210,30 @@ void ConfigManipulation::check_filament_max_volumetric_speed(DynamicPrintConfig } +void ConfigManipulation::check_filament_ironing_spacing(DynamicPrintConfig *config) +{ + const auto *opt = config->option("filament_ironing_spacing"); + if (opt == nullptr) + return; + std::vector values = opt->values; + bool reset = false; + for (size_t i = 0; i < values.size(); ++i) + if (!opt->is_nil(i) && values[i] < IRONING_SPACING_MIN) { + values[i] = 0.1; + reset = true; + } + if (!reset) + return; + const wxString msg_text = _(L("Ironing spacing too small\nIt has been reset to 0.1")); + MessageDialog dialog(nullptr, msg_text, "", wxICON_WARNING | wxOK); + DynamicPrintConfig new_conf = *config; + is_msg_dlg_already_exist = true; + dialog.ShowModal(); + new_conf.set_key_value("filament_ironing_spacing", new ConfigOptionFloatsNullable(values)); + apply(config, &new_conf); + is_msg_dlg_already_exist = false; +} + void ConfigManipulation::check_chamber_temperature(DynamicPrintConfig* config) { bool support_chamber_temp_control=GUI::wxGetApp().preset_bundle->printers.get_selected_preset().config.opt_bool("support_chamber_temp_control"); @@ -332,7 +356,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con } //BBS: ironing_spacing shouldn't be too small or equal to zero - if (config->opt_float("ironing_spacing") < 0.05) + if (config->opt_float("ironing_spacing") < IRONING_SPACING_MIN) { const wxString msg_text = _(L("Ironing spacing too small\nIt has been reset to 0.1")); MessageDialog dialog(nullptr, msg_text, "", wxICON_WARNING | wxOK); @@ -343,7 +367,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con apply(config, &new_conf); is_msg_dlg_already_exist = false; } - if (config->opt_float("support_ironing_spacing") < 0.05) + if (config->opt_float("support_ironing_spacing") < IRONING_SPACING_MIN) { const wxString msg_text = _(L("Ironing spacing too small\nIt has been reset to 0.1")); MessageDialog dialog(nullptr, msg_text, "", wxICON_WARNING | wxOK); diff --git a/src/slic3r/GUI/ConfigManipulation.hpp b/src/slic3r/GUI/ConfigManipulation.hpp index ac53ffb4bb..92b76643f2 100644 --- a/src/slic3r/GUI/ConfigManipulation.hpp +++ b/src/slic3r/GUI/ConfigManipulation.hpp @@ -84,6 +84,7 @@ public: void check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config); void check_adaptive_pressure_advance_model(DynamicPrintConfig* config); void check_filament_max_volumetric_speed(DynamicPrintConfig *config); + void check_filament_ironing_spacing(DynamicPrintConfig *config); void check_chamber_temperature(DynamicPrintConfig* config); void check_chamber_minimal_temperature(DynamicPrintConfig* config); bool check_layer_height(DynamicPrintConfig* config); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index bf348f856f..72974e8bac 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -4905,6 +4905,7 @@ void TabFilament::update() return; // ys_FIXME m_config_manipulation.check_filament_max_volumetric_speed(m_config); + m_config_manipulation.check_filament_ironing_spacing(m_config); m_update_cnt++; diff --git a/tests/fff_print/test_fill.cpp b/tests/fff_print/test_fill.cpp index 0ff7e65e5d..1a85cb2727 100644 --- a/tests/fff_print/test_fill.cpp +++ b/tests/fff_print/test_fill.cpp @@ -747,6 +747,61 @@ TEST_CASE("A region with ironing turned off is never ironed", "[Fill]") REQUIRE(Layer::choose_ironing_extruder(cfg, spiral_mode, /*is_topmost_layer=*/true) == -1); } +// Ironing path count and total length in mm, over the whole object. +static std::pair ironing_extent(const Print &print) +{ + size_t paths = 0; + double length = 0.; + for (const Layer *layer : print.objects().front()->layers()) + for (const LayerRegion *region : layer->regions()) + for (const ExtrusionEntity *entity : region->fills.flatten().entities) + if (ironing_role(entity->role())) { + ++paths; + length += unscale(entity->length()); + } + return {paths, length}; +} + +TEST_CASE("Ironing spacing below the minimum irons at the minimum spacing", "[Fill]") +{ + const std::string pattern = GENERATE("rectilinear", "concentric"); + const bool via_filament = GENERATE(false, true); + const double spacing = GENERATE(0., 0.001); + CAPTURE(pattern, via_filament, spacing); + + auto ironing_for = [&pattern, via_filament](double spacing) { + DynamicPrintConfig config = DynamicPrintConfig::full_print_config(); + config.set_deserialize_strict({{"ironing_type", "top"}, + {"ironing_pattern", pattern}, + {"layer_height", 0.2}}); + // The filament override replaces the process spacing, which stays at a usable value. + if (via_filament) + config.set_deserialize_strict({{"ironing_spacing", 0.1}, {"filament_ironing_spacing", spacing}}); + else + config.set_deserialize_strict({{"ironing_spacing", spacing}}); + Print print; + Slic3r::Test::init_and_process_print({Slic3r::Test::cube(20)}, print, config); + return ironing_extent(print); + }; + + const std::pair clamped = ironing_for(spacing); + const std::pair minimum = ironing_for(IRONING_SPACING_MIN); + REQUIRE(minimum.first > 0); + CHECK(clamped.first == minimum.first); + CHECK_THAT(clamped.second, Catch::Matchers::WithinRel(minimum.second, 1e-9)); +} + +TEST_CASE("Concentric fill at zero spacing returns without paths", "[Fill]") +{ + std::unique_ptr filler(Fill::new_from_type(ipConcentric)); + filler->spacing = 0.; + filler->bounding_box = BoundingBox(Point(0, 0), Point::new_scale(10, 10)); + FillParams params; + params.density = 1.f; + Surface surface(stTop, ExPolygon({Point(0, 0), Point::new_scale(10, 0), Point::new_scale(10, 10), Point::new_scale(0, 10)})); + CHECK(filler->fill_surface(&surface, params).empty()); +} + TEST_CASE("Solid infill direction offsets every layer when no template is set", "[Fill]") { auto angles_for = [](int direction) {