diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index dbe7e157ab1..42fd6e8ea13 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -49,6 +49,8 @@ static inline FlowRole opt_key_to_flow_role(const std::string &opt_key) return frInfill; else if (opt_key == "internal_solid_infill_line_width") return frSolidInfill; + else if (opt_key == "bridge_line_width") + return frSolidInfill; else if (opt_key == "top_surface_line_width") return frTopSolidInfill; else if (opt_key == "support_line_width") @@ -67,6 +69,26 @@ double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionFloat { assert(opt != nullptr); + auto opt_nozzle_diameters = config.option("nozzle_diameter"); + if (opt_nozzle_diameters == nullptr) + throw_on_missing_variable(opt_key, "nozzle_diameter"); + const float nozzle_diameter = float(opt_nozzle_diameters->get_at(first_printing_extruder)); + + if (opt_key == "bridge_line_width") { + if (opt->percent) { + const double bridge_width = opt->get_abs_value(nozzle_diameter); + if (bridge_width > 0.) + return bridge_width; + } else if (opt->value > 0.) { + return opt->value; + } + + opt = config.option("internal_solid_infill_line_width"); + if (opt == nullptr) + throw_on_missing_variable(opt_key, "internal_solid_infill_line_width"); + return extrusion_width("internal_solid_infill_line_width", opt, config, first_printing_extruder); + } + #if 0 // This is the logic used for skit / brim, but not for the rest of the 1st layer. if (opt->value == 0. && first_layer) { @@ -84,17 +106,13 @@ double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionFloat throw_on_missing_variable(opt_key, "line_width"); } - auto opt_nozzle_diameters = config.option("nozzle_diameter"); - if (opt_nozzle_diameters == nullptr) - throw_on_missing_variable(opt_key, "nozzle_diameter"); - if (opt->percent) { - return opt->get_abs_value(float(opt_nozzle_diameters->get_at(first_printing_extruder))); + return opt->get_abs_value(nozzle_diameter); } if (opt->value == 0.) { // If user left option to 0, calculate a sane default width. - return auto_extrusion_width(opt_key_to_flow_role(opt_key), float(opt_nozzle_diameters->get_at(first_printing_extruder))); + return auto_extrusion_width(opt_key_to_flow_role(opt_key), nozzle_diameter); } return opt->value; diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 20ded6e72c8..22e0a26898c 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -34,16 +34,26 @@ Flow LayerRegion::bridging_flow(FlowRole role, bool thick_bridge) const const PrintRegionConfig ®ion_config = region.config(); const PrintObject &print_object = *this->layer()->object(); Flow bridge_flow; + // Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will fall back to zero'th element, so everything is all right. auto nozzle_diameter = float(print_object.print()->config().nozzle_diameter.get_at(region.extruder(role) - 1)); + const ConfigOptionFloatOrPercent& bridge_width_opt = region_config.bridge_line_width; + const double bridge_width = bridge_width_opt.get_abs_value(nozzle_diameter); + const bool has_bridge_width = bridge_width > 0.; + const double bridge_flow_ratio = region_config.bridge_flow; + if (thick_bridge) { // The old Slic3r way (different from all other slicers): Use rounded extrusions. // Get the configured nozzle_diameter for the extruder associated to the flow role requested. - // Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right. - // Applies default bridge spacing. - bridge_flow = Flow::bridging_flow(float(sqrt(region_config.bridge_flow)) * nozzle_diameter, nozzle_diameter); + float thread_diameter = has_bridge_width ? float(bridge_width) : nozzle_diameter; + if (bridge_flow_ratio > 0.) + thread_diameter *= float(sqrt(bridge_flow_ratio)); + bridge_flow = Flow::bridging_flow(thread_diameter, nozzle_diameter); } else { // The same way as other slicers: Use normal extrusions. Apply bridge_flow while maintaining the original spacing. - bridge_flow = this->flow(role).with_flow_ratio(region_config.bridge_flow); + Flow base_flow = this->flow(role); + if (has_bridge_width) + base_flow = Flow(float(bridge_width), base_flow.height(), nozzle_diameter); + bridge_flow = base_flow.with_flow_ratio(bridge_flow_ratio); } return bridge_flow; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 21050ee09f3..b26d28d314a 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1097,6 +1097,7 @@ static std::vector s_Preset_print_options{ "infill_wall_overlap", "top_bottom_infill_wall_overlap", "bridge_flow", + "bridge_line_width", "internal_bridge_flow", "elefant_foot_compensation", "elefant_foot_compensation_layers", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index d53a168ec29..4ce97eb8aa7 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1540,12 +1540,12 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons* auto validate_extrusion_width = [min_nozzle_diameter, max_nozzle_diameter](const ConfigBase &config, const char *opt_key, double layer_height, std::string &err_msg) -> bool { double extrusion_width_min = config.get_abs_value(opt_key, min_nozzle_diameter); double extrusion_width_max = config.get_abs_value(opt_key, max_nozzle_diameter); - if (extrusion_width_min == 0) { - // Default "auto-generated" extrusion width is always valid. - } else if (extrusion_width_min <= layer_height) { - err_msg = L("Too small line width"); - return false; - } else if (extrusion_width_max > max_nozzle_diameter * MAX_LINE_WIDTH_MULTIPLIER) { + if (extrusion_width_min == 0) { + // Default "auto-generated" extrusion width is always valid. + } else if (extrusion_width_min <= layer_height) { + err_msg = L("Too small line width"); + return false; + } else if (extrusion_width_max > max_nozzle_diameter * MAX_LINE_WIDTH_MULTIPLIER) { err_msg = L("Too large line width"); return false; } @@ -1667,6 +1667,25 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons* for (const PrintRegion ®ion : object->all_regions()) if (!validate_extrusion_width(region.config(), opt_key, layer_height, err_msg)) return {err_msg, object, opt_key}; + + const bool allow_thin_bridge_width = object->config().thick_bridges && object->config().thick_internal_bridges; + for (const PrintRegion ®ion : object->all_regions()) { + const auto &bridge_width_opt = region.config().bridge_line_width; + for (FlowRole bridge_role : { frPerimeter, frInfill, frSolidInfill, frTopSolidInfill }) { + const double nozzle_diameter = m_config.nozzle_diameter.get_at(region.extruder(bridge_role) - 1); + const double bridge_width = bridge_width_opt.get_abs_value(nozzle_diameter); + if (bridge_width <= 0.) + continue; + if (bridge_width > nozzle_diameter) { + err_msg = L("Bridge line width must not exceed nozzle diameter"); + return { err_msg, object, "bridge_line_width" }; + } + if (!allow_thin_bridge_width && bridge_width <= layer_height) { + err_msg = L("Too small line width"); + return { err_msg, object, "bridge_line_width" }; + } + } + } } } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index a113c3057a6..4d7034f61f2 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1283,47 +1283,73 @@ void PrintConfigDef::init_fff_params() def = this->add("bridge_density", coPercent); def->label = L("External bridge density"); def->category = L("Strength"); - def->tooltip = L("Controls the density (spacing) of external bridge lines. Default is 100%.\n\n" - "Lower density external bridges can help improve reliability as there is more space for air to circulate " - "around the extruded bridge, improving its cooling speed. Minimum is 10%.\n\n" - "Higher densities can produce smoother bridge surfaces, as overlapping lines provide " - "additional support during printing. Maximum is 120%.\n" - "Note: Bridge density that is too high can cause warping or overextrusion."); + def->tooltip = L("Controls the density (spacing) of external bridge lines. Default is 100%.\n" + "Theoretically, 100% means a solid bridge, but due to the tendency of bridge extrusions to sag, 100% may not be sufficient.\n\n" + "- Higher than 100% density (Recommended Max 125%):\n" + " - Pros: Produces smoother bridge surfaces, as overlapping lines provide additional support during printing.\n" + " - Cons: Can cause overextrusion, which may reduce lower and upper surface quality and increase the risk of warping.\n\n" + "- Lower than 100% density (Min 10%):\n" + " - Pros: Can create a string-like first layer. Faster and with better cooling because there is more space for air to circulate around the extruded bridge.\n" + " - Cons: May lead to sagging and poorer surface finish.\n\n" + "Recommended range: Minimum 10% - Maximum 125%."); def->sidetext = "%"; def->min = 10; - def->max = 120; + def->max = 125; def->mode = comAdvanced; def->set_default_value(new ConfigOptionPercent(100)); def = this->add("internal_bridge_density", coPercent); def->label = L("Internal bridge density"); def->category = L("Strength"); - def->tooltip = L("Controls the density (spacing) of internal bridge lines. 100% means solid bridge. Default is 100%.\n\n" - "Lower density internal bridges can help reduce top surface pillowing and improve internal bridge reliability as there is more space for " - "air to circulate around the extruded bridge, improving its cooling speed.\n\n" - "This option works particularly well when combined with the second internal bridge over infill option, " - "further improving internal bridging structure before solid infill is extruded."); + def->tooltip = L("Controls the density (spacing) of internal bridge lines. Default is 100%. 100% means a solid internal bridge.\n\n" + "Internal bridges act as intermediate support between sparse infill and top solid infill and can strongly affect top surface quality.\n\n" + "- Higher than 100% density (Recommended Max 125%):\n" + " - Pros: Improves internal bridge strength and support under top layers, reducing sagging and improving top-surface finish.\n" + " - Cons: Increases material use and print time; excessive density may cause overextrusion and internal stresses.\n\n" + "- Lower than 100% density (Min 10%):\n" + " - Pros: Can reduce pillowing and improve cooling (more airflow through the bridge), and may speed up printing.\n" + " - Cons: May reduce internal support, increasing the risk of sagging and top surface defects.\n\n" + "This option works particularly well when combined with the second internal bridge over infill option to improve bridging further before solid infill is extruded."); def->sidetext = "%"; def->min = 10; - def->max = 100; + def->max = 125; def->mode = comAdvanced; def->set_default_value(new ConfigOptionPercent(100)); def = this->add("bridge_flow", coFloat); def->label = L("Bridge flow ratio"); def->category = L("Quality"); - def->tooltip = L("Decrease this value slightly (for example 0.9) to reduce the amount of material for bridge, to improve sag.\n\n" + def->tooltip = L("This value governs the thickness of the external (visible) bridge layer.\n" + "Values above 1.0: Increase the amount of material while maintaining line spacing. This can improve line contact and strength.\n" + "Values below 1.0: Reduce the amount of material while adjusting line spacing to maintain contact. This can improve sagging.\n\n" "The actual bridge flow used is calculated by multiplying this value with the filament flow ratio, and if set, the object's flow ratio."); def->min = 0; def->max = 2.0; def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(1)); + def = this->add("bridge_line_width", coFloatOrPercent); + def->label = L("Bridge"); + def->category = L("Quality"); + def->tooltip = L("Bridge line width is expressed either as an absolute value or as a percentage of the active nozzle diameter (percentages are computed from the nozzle diameter).\n" + "Recommended to use with a higher Bridge density or Bridge flow ratio.\n\n" + "The maximum value is 100% or the nozzle diameter.\n" + "If set to 0, the line width will match the Internal solid infill width."); + def->sidetext = L("mm or %"); + def->ratio_over = "nozzle_diameter"; + def->min = 0; + def->max = 100; + def->max_literal = 10; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(100., true)); + def = this->add("internal_bridge_flow", coFloat); def->label = L("Internal bridge flow ratio"); def->category = L("Quality"); - def->tooltip = L("This value governs the thickness of the internal bridge layer. This is the first layer over sparse infill. Decrease this value slightly (for example 0.9) to improve surface quality over sparse infill." - "\n\nThe actual internal bridge flow used is calculated by multiplying this value with the bridge flow ratio, the filament flow ratio, and if set, the object's flow ratio."); + def->tooltip = L("This value governs the thickness of the internal bridge layer. This is the first layer over sparse infill so increasing it may increase strength and upper layer quality.\n" + "Values above 1.0: Increase the amount of material while maintaining line spacing. This can improve line contact and strength.\n" + "Values below 1.0: Reduce the amount of material while adjusting line spacing to maintain contact. This can improve sagging.\n\n" + "The actual bridge flow used is calculated by multiplying this value with the filament flow ratio, and if set, the object's flow ratio."); def->min = 0; def->max = 2.0; def->mode = comAdvanced; @@ -1907,16 +1933,18 @@ void PrintConfigDef::init_fff_params() def = this->add("thick_bridges", coBool); def->label = L("Thick external bridges"); def->category = L("Quality"); - def->tooltip = L("If enabled, bridges are more reliable, can bridge longer distances, but may look worse. " - "If disabled, bridges look better but are reliable just for shorter bridged distances."); + def->tooltip = L("If enabled, bridge extrusion uses a line height equal to the nozzle diameter.\n" + "This increases bridge strength and reliability, allowing longer spans, but may worsen appearance.\n" + "If disabled, bridges may look better but are generally reliable only for shorter spans."); def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool(false)); def = this->add("thick_internal_bridges", coBool); def->label = L("Thick internal bridges"); def->category = L("Quality"); - def->tooltip = L("If enabled, thick internal bridges will be used. It's usually recommended to have this feature turned on. However, " - "consider turning it off if you are using large nozzles."); + def->tooltip = L("If enabled, internal bridge extrusion uses a line height equal to the nozzle diameter.\n" + "This increases internal bridge strength and reliability when printed over sparse infill, but may worsen appearance.\n" + "If disabled, internal bridges may look better but can be less reliable over sparse infill."); def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool(true)); @@ -10269,8 +10297,8 @@ std::map validate(const FullPrintConfig &cfg, bool und error_message.emplace("bridge_flow", L("invalid value ") + std::to_string(cfg.bridge_flow)); } - // --bridge-flow-ratio - if (cfg.bridge_flow <= 0) { + // --internal-bridge-flow-ratio + if (cfg.internal_bridge_flow <= 0) { error_message.emplace("internal_bridge_flow", L("invalid value ") + std::to_string(cfg.internal_bridge_flow)); } @@ -10328,13 +10356,18 @@ std::map validate(const FullPrintConfig &cfg, bool und // extrusion widths { double max_nozzle_diameter = 0.; + double min_nozzle_diameter = std::numeric_limits::max(); for (double dmr : cfg.nozzle_diameter.values) + { max_nozzle_diameter = std::max(max_nozzle_diameter, dmr); + min_nozzle_diameter = std::min(min_nozzle_diameter, dmr); + } const char *widths[] = { "outer_wall_line_width", "inner_wall_line_width", "sparse_infill_line_width", "internal_solid_infill_line_width", + "bridge_line_width", "top_surface_line_width", "support_line_width", "initial_layer_line_width", @@ -10342,8 +10375,13 @@ std::map validate(const FullPrintConfig &cfg, bool und "skeleton_infill_line_width"}; for (size_t i = 0; i < sizeof(widths) / sizeof(widths[i]); ++ i) { std::string key(widths[i]); - if (cfg.get_abs_value(key, max_nozzle_diameter) > MAX_LINE_WIDTH_MULTIPLIER * max_nozzle_diameter) { - error_message.emplace(key, L("too large line width ") + std::to_string(cfg.get_abs_value(key))); + double abs_width = cfg.get_abs_value(key, max_nozzle_diameter); + double allowed_max = (key == "bridge_line_width") ? min_nozzle_diameter : MAX_LINE_WIDTH_MULTIPLIER * max_nozzle_diameter; + if (abs_width > allowed_max) { + if (key == "bridge_line_width") + error_message.emplace(key, L("Bridge line width must not exceed nozzle diameter: ") + std::to_string(abs_width)); + else + error_message.emplace(key, L("too large line width ") + std::to_string(abs_width)); //return std::string("Too Large line width: ") + key; } } diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 5cf095b588f..2dabe8e8929 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1082,6 +1082,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, internal_bridge_angle)) // ORCA: Internal bridge angle override ((ConfigOptionBool, relative_bridge_angle)) // ORCA: Relative bridge angle flag ((ConfigOptionFloat, bridge_flow)) + ((ConfigOptionFloatOrPercent, bridge_line_width)) ((ConfigOptionFloat, internal_bridge_flow)) ((ConfigOptionFloat, bridge_speed)) ((ConfigOptionFloatOrPercent, internal_bridge_speed)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 732e0bb9009..830ac50f51c 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1277,6 +1277,7 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "internal_bridge_angle" // ORCA: Internal bridge angle override || opt_key == "relative_bridge_angle" // ORCA: Relative bridge angle //BBS + || opt_key == "bridge_line_width" || opt_key == "bridge_density" || opt_key == "internal_bridge_density") { steps.emplace_back(posPrepareInfill); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 9df41c954de..5955b8a3e34 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2321,13 +2321,14 @@ void TabPrint::build() optgroup = page->new_optgroup(L("Line width"), L"param_line_width"); optgroup->append_single_option_line("line_width","quality_settings_line_width"); - optgroup->append_single_option_line("initial_layer_line_width","quality_settings_line_width"); - optgroup->append_single_option_line("outer_wall_line_width","quality_settings_line_width"); - optgroup->append_single_option_line("inner_wall_line_width","quality_settings_line_width"); - optgroup->append_single_option_line("top_surface_line_width","quality_settings_line_width"); - optgroup->append_single_option_line("sparse_infill_line_width","quality_settings_line_width"); - optgroup->append_single_option_line("internal_solid_infill_line_width","quality_settings_line_width"); - optgroup->append_single_option_line("support_line_width","quality_settings_line_width"); + optgroup->append_single_option_line("initial_layer_line_width","quality_settings_line_width#first-layer"); + optgroup->append_single_option_line("outer_wall_line_width","quality_settings_line_width#outer-wall"); + optgroup->append_single_option_line("inner_wall_line_width","quality_settings_line_width#inner-wall"); + optgroup->append_single_option_line("top_surface_line_width","quality_settings_line_width#top-surface"); + optgroup->append_single_option_line("sparse_infill_line_width","quality_settings_line_width#sparse-infill"); + optgroup->append_single_option_line("internal_solid_infill_line_width","quality_settings_line_width#internal-solid-infill"); + optgroup->append_single_option_line("support_line_width","quality_settings_line_width#support"); + optgroup->append_single_option_line("bridge_line_width","quality_settings_line_width#bridge"); optgroup = page->new_optgroup(L("Seam"), L"param_seam"); optgroup->append_single_option_line("seam_position", "quality_settings_seam#seam-position"); @@ -2427,7 +2428,7 @@ void TabPrint::build() optgroup = page->new_optgroup(L("Bridging"), L"param_bridge"); optgroup->append_single_option_line("bridge_flow", "quality_settings_bridging#flow-ratio"); - optgroup->append_single_option_line("internal_bridge_flow", "quality_settings_bridging#flow-ratio"); + optgroup->append_single_option_line("internal_bridge_flow", "quality_settings_bridging#flow-ratio"); optgroup->append_single_option_line("bridge_density", "quality_settings_bridging#bridge-density"); optgroup->append_single_option_line("internal_bridge_density", "quality_settings_bridging#bridge-density"); optgroup->append_single_option_line("thick_bridges", "quality_settings_bridging#thick-bridges");