From a718a7606a9c6cc13a39119885a5a1c132f8b3db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 24 Oct 2024 11:06:15 +0200 Subject: [PATCH 1/2] SPE-2491: Basic support for printing with different nozzle diameters on tool-changer printers. Add an option that automatically changes all extrusion widths to zero, which will automatically calculate extrusion width based on nozzle diameters. Allow using wipe tower with different nozzle diameters and show just warning about it. Add an option for infill combination that automatically calculates the number of layers based on nozzle diameter. --- src/libslic3r/Preset.cpp | 1 + src/libslic3r/Print.cpp | 18 ++++-- src/libslic3r/PrintConfig.cpp | 25 ++++++++ src/libslic3r/PrintConfig.hpp | 3 + src/libslic3r/PrintObject.cpp | 34 +++++++---- src/slic3r/GUI/ConfigManipulation.cpp | 81 ++++++++++++++++++++++++-- src/slic3r/GUI/NotificationManager.hpp | 2 + src/slic3r/GUI/Plater.cpp | 7 ++- src/slic3r/GUI/Tab.cpp | 4 +- 9 files changed, 151 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index e18bd7b951..090ea06741 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -510,6 +510,7 @@ static std::vector s_Preset_print_options { "perimeter_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", "wall_distribution_count", "min_feature_size", "min_bead_width", "top_one_perimeter_type", "only_one_perimeter_first_layer", + "automatic_extrusion_widths", "automatic_infill_combination", "automatic_infill_combination_max_layer_height", }; static std::vector s_Preset_filament_options { diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index f7f15cd49c..92c8d49628 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -283,6 +283,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n steps.emplace_back(psSkirtBrim); } else if (opt_key == "avoid_crossing_curled_overhangs") { osteps.emplace_back(posEstimateCurledExtrusions); + } else if (opt_key == "automatic_extrusion_widths") { + osteps.emplace_back(posPerimeters); } else { // for legacy, if we can't handle this option let's invalidate all steps //FIXME invalidate all steps of all objects as well? @@ -627,15 +629,19 @@ std::string Print::validate(std::vector* warnings) const if (this->has_wipe_tower() && ! m_objects.empty()) { // Make sure all extruders use same diameter filament and have the same nozzle diameter // EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments - double first_nozzle_diam = m_config.nozzle_diameter.get_at(extruders.front()); + double first_nozzle_diam = m_config.nozzle_diameter.get_at(extruders.front()); double first_filament_diam = m_config.filament_diameter.get_at(extruders.front()); + + bool allow_nozzle_diameter_differ_warning = (warnings != nullptr); for (const auto& extruder_idx : extruders) { - double nozzle_diam = m_config.nozzle_diameter.get_at(extruder_idx); + double nozzle_diam = m_config.nozzle_diameter.get_at(extruder_idx); double filament_diam = m_config.filament_diameter.get_at(extruder_idx); - if (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam - || std::abs((filament_diam-first_filament_diam)/first_filament_diam) > 0.1) - return _u8L("The wipe tower is only supported if all extruders have the same nozzle diameter " - "and use filaments of the same diameter."); + if (allow_nozzle_diameter_differ_warning && (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam)) { + allow_nozzle_diameter_differ_warning = false; + warnings->emplace_back("_WIPE_TOWER_NOZZLE_DIAMETER_DIFFER"); + } else if (std::abs((filament_diam - first_filament_diam) / first_filament_diam) > 0.1) { + return _u8L("The wipe tower is only supported if all extruders use filaments of the same diameter."); + } } if (m_config.gcode_flavor != gcfRepRapSprinter && m_config.gcode_flavor != gcfRepRapFirmware && diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 1d6aef73ce..85841e87e7 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -527,6 +527,31 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionEnum(ArcFittingType::Disabled)); + def = this->add("automatic_extrusion_widths", coBool); + def->label = L("Automatic extrusion widths calculation"); + def->category = L("Extrusion Width"); + def->tooltip = L("Automatically calculates extrusion widths based on the nozzle diameter of the currently used extruder. " + "This setting is essential for printing with different nozzle diameters."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("automatic_infill_combination", coBool); + def->label = L("Automatic infill combination"); + def->category = L("Infill"); + def->tooltip = L("This feature automatically combine infill of several layers and speeds up your print by extruding thicker " + "infill layers while preserving thin perimeters, thus accuracy."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("automatic_infill_combination_max_layer_height", coFloatOrPercent); + def->label = L("Automatic infill combination - Max layer height"); + def->category = L("Infill"); + def->tooltip = L("Maximum layer height for combining infill when automatic infill combining is enabled. " + "Maximum layer height could be specified either as an absolute in millimeters value or as a percentage of nozzle diameter. " + "For printing with different nozzle diameters, it is recommended to use percentage value over absolute value."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(100., true)); + // Maximum extruder temperature, bumped to 1500 to support printing of glass. const int max_temp = 1500; def = this->add("avoid_crossing_curled_overhangs", coBool); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 93bb6747be..dac7561bdd 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -685,6 +685,8 @@ PRINT_CONFIG_CLASS_DEFINE( PRINT_CONFIG_CLASS_DEFINE( PrintRegionConfig, + ((ConfigOptionBool, automatic_infill_combination)) + ((ConfigOptionFloatOrPercent, automatic_infill_combination_max_layer_height)) ((ConfigOptionFloat, bridge_angle)) ((ConfigOptionInt, bottom_solid_layers)) ((ConfigOptionFloat, bottom_solid_min_thickness)) @@ -898,6 +900,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( PrintConfig, (MachineEnvelopeConfig, GCodeConfig), + ((ConfigOptionBool, automatic_extrusion_widths)) ((ConfigOptionBool, avoid_crossing_curled_overhangs)) ((ConfigOptionBool, avoid_crossing_perimeters)) ((ConfigOptionFloatOrPercent, avoid_crossing_perimeters_max_detour)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index bd0542ca6a..f9c5727b44 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -808,6 +808,8 @@ bool PrintObject::invalidate_state_by_config_options( opt_key == "interface_shells" || opt_key == "infill_only_where_needed" || opt_key == "infill_every_layers" + || opt_key == "automatic_infill_combination" + || opt_key == "automatic_infill_combination_max_layer_height" || opt_key == "solid_infill_every_layers" || opt_key == "ensure_vertical_shell_thickness" || opt_key == "bottom_solid_min_thickness" @@ -3053,16 +3055,24 @@ void PrintObject::discover_horizontal_shells() void PrintObject::combine_infill() { // Work on each region separately. - for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { - const PrintRegion ®ion = this->printing_region(region_id); - const size_t every = region.config().infill_every_layers.value; - if (every < 2 || region.config().fill_density == 0.) + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) { + const PrintRegion ®ion = this->printing_region(region_id); + const size_t combine_infill_every_n_layers = region.config().infill_every_layers.value; + const bool automatic_infill_combination = region.config().automatic_infill_combination; + const bool enable_combine_infill = automatic_infill_combination || combine_infill_every_n_layers >= 2; + + if (!enable_combine_infill || region.config().fill_density == 0.) { continue; + } + // Limit the number of combined layers to the maximum height allowed by this regions' nozzle. //FIXME limit the layer height to max_layer_height - double nozzle_diameter = std::min( - this->print()->config().nozzle_diameter.get_at(region.config().infill_extruder.value - 1), - this->print()->config().nozzle_diameter.get_at(region.config().solid_infill_extruder.value - 1)); + const double nozzle_diameter = std::min(this->print()->config().nozzle_diameter.get_at(region.config().infill_extruder.value - 1), + this->print()->config().nozzle_diameter.get_at(region.config().solid_infill_extruder.value - 1)); + + const double automatic_infill_combination_max_layer_height = region.config().automatic_infill_combination_max_layer_height.get_abs_value(nozzle_diameter); + const double max_combine_layer_height = automatic_infill_combination ? std::min(automatic_infill_combination_max_layer_height, nozzle_diameter) : nozzle_diameter; + // define the combinations std::vector combine(m_layers.size(), 0); { @@ -3070,19 +3080,21 @@ void PrintObject::combine_infill() size_t num_layers = 0; for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++ layer_idx) { m_print->throw_if_canceled(); - const Layer *layer = m_layers[layer_idx]; - if (layer->id() == 0) + const Layer &layer = *m_layers[layer_idx]; + if (layer.id() == 0) // Skip first print layer (which may not be first layer in array because of raft). continue; + // Check whether the combination of this layer with the lower layers' buffer // would exceed max layer height or max combined layer count. - if (current_height + layer->height >= nozzle_diameter + EPSILON || num_layers >= every) { + if (current_height + layer.height >= max_combine_layer_height + EPSILON || (!automatic_infill_combination && num_layers >= combine_infill_every_n_layers)) { // Append combination to lower layer. combine[layer_idx - 1] = num_layers; current_height = 0.; num_layers = 0; } - current_height += layer->height; + + current_height += layer.height; ++ num_layers; } diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index dca9d2e58e..ac6b29a2d5 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -38,6 +38,64 @@ void ConfigManipulation::toggle_field(const std::string& opt_key, const bool tog cb_toggle_field(opt_key, toggle, opt_index); } +std::optional handle_automatic_extrusion_widths(const DynamicPrintConfig &config, const bool is_global_config, wxWindow *msg_dlg_parent) +{ + const std::vector extrusion_width_parameters = {"extrusion_width", "external_perimeter_extrusion_width", "first_layer_extrusion_width", + "infill_extrusion_width", "perimeter_extrusion_width", "solid_infill_extrusion_width", + "support_material_extrusion_width", "top_infill_extrusion_width"}; + + auto is_zero_width = [](const ConfigOptionFloatOrPercent &opt) -> bool { + return opt.value == 0. && !opt.percent; + }; + + auto is_parameters_adjustment_needed = [&is_zero_width, &config, &extrusion_width_parameters]() -> bool { + if (!config.opt_bool("automatic_extrusion_widths")) { + return false; + } + + for (const std::string &extrusion_width_parameter : extrusion_width_parameters) { + if (!is_zero_width(*config.option(extrusion_width_parameter))) { + return true; + } + } + + return false; + }; + + if (is_parameters_adjustment_needed()) { + wxString msg_text = _(L("The automatic extrusion widths calculation requires:\n" + "- Default extrusion width: 0\n" + "- First layer extrusion width: 0\n" + "- Perimeter extrusion width: 0\n" + "- External perimeter extrusion width: 0\n" + "- Infill extrusion width: 0\n" + "- Solid infill extrusion width: 0\n" + "- Top infill extrusion width: 0\n" + "- Support material extrusion width: 0")); + + if (is_global_config) { + msg_text += "\n\n" + _(L("Shall I adjust those settings in order to enable automatic extrusion widths calculation?")); + } + + MessageDialog dialog(msg_dlg_parent, msg_text, _(L("Automatic extrusion widths calculation")), + wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK)); + + const int answer = dialog.ShowModal(); + DynamicPrintConfig new_conf = config; + if (!is_global_config || answer == wxID_YES) { + for (const std::string &extrusion_width_parameter : extrusion_width_parameters) { + new_conf.set_key_value(extrusion_width_parameter, new ConfigOptionFloatOrPercent(0., false)); + } + } else { + new_conf.set_key_value("automatic_extrusion_widths", new ConfigOptionBool(false)); + } + + return new_conf; + } + + return std::nullopt; +} + void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, const bool is_global_config) { // #ys_FIXME_to_delete @@ -76,7 +134,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con double fill_density = config->option("fill_density")->value; if (config->opt_bool("spiral_vase") && - ! (config->opt_int("perimeters") == 1 && + ! (config->opt_int("perimeters") == 1 && config->opt_int("top_solid_layers") == 0 && fill_density == 0 && ! config->opt_bool("support_material") && @@ -102,7 +160,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con new_conf.set_key_value("fill_density", new ConfigOptionPercent(0)); new_conf.set_key_value("support_material", new ConfigOptionBool(false)); new_conf.set_key_value("support_material_enforce_layers", new ConfigOptionInt(0)); - new_conf.set_key_value("thin_walls", new ConfigOptionBool(false)); + new_conf.set_key_value("thin_walls", new ConfigOptionBool(false)); fill_density = 0; support = false; } @@ -213,6 +271,13 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con } } } + + if (config->opt_bool("automatic_extrusion_widths")) { + std::optional new_config = handle_automatic_extrusion_widths(*config, is_global_config, m_msg_dlg_parent); + if (new_config.has_value()) { + apply(config, &(*new_config)); + } + } } void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) @@ -227,11 +292,17 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("overhang_speed_" + std::to_string(i), config->opt_bool("enable_dynamic_overhang_speeds")); } - bool have_infill = config->option("fill_density")->value > 0; + const bool have_infill = config->option("fill_density")->value > 0; + const bool has_automatic_infill_combination = config->option("automatic_infill_combination")->value; // infill_extruder uses the same logic as in Print::extruders() - for (auto el : { "fill_pattern", "infill_every_layers", "infill_only_where_needed", - "solid_infill_every_layers", "solid_infill_below_area", "infill_extruder", "infill_anchor_max" }) + for (auto el : { "fill_pattern","solid_infill_every_layers", "solid_infill_below_area", "infill_extruder", + "infill_anchor_max", "automatic_infill_combination" }) { toggle_field(el, have_infill); + } + + toggle_field("infill_every_layers", have_infill && !has_automatic_infill_combination); + toggle_field("automatic_infill_combination_max_layer_height", have_infill && has_automatic_infill_combination); + // Only allow configuration of open anchors if the anchoring is enabled. bool has_infill_anchors = have_infill && config->option("infill_anchor_max")->value > 0; toggle_field("infill_anchor", has_infill_anchors); diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 0c6390f2ec..255a35b7d1 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -140,6 +140,8 @@ enum class NotificationType BedTemperaturesDiffer, // Notification that shrinkage compensations for the used filaments differ. ShrinkageCompensationsDiffer, + // Notification about using wipe tower with different nozzle diameters. + WipeTowerNozzleDiameterDiffer, }; class NotificationManager diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 83a16e1f32..3985e77989 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2098,9 +2098,10 @@ void Plater::priv::process_validation_warning(const std::vector& wa if (warnings.empty()) notification_manager->close_notification_of_type(NotificationType::ValidateWarning); - // Always close warnings BedTemperaturesDiffer and ShrinkageCompensationsDiffer before next processing. + // Always close warnings BedTemperaturesDiffer, ShrinkageCompensationsDiffer and WipeTowerNozzleDiameterDiffer before next processing. notification_manager->close_notification_of_type(NotificationType::BedTemperaturesDiffer); notification_manager->close_notification_of_type(NotificationType::ShrinkageCompensationsDiffer); + notification_manager->close_notification_of_type(NotificationType::WipeTowerNozzleDiameterDiffer); for (std::string text : warnings) { std::string hypertext = ""; @@ -2128,6 +2129,10 @@ void Plater::priv::process_validation_warning(const std::vector& wa text = _u8L("Filament shrinkage will not be used because filament shrinkage " "for the used filaments differs significantly."); notification_type = NotificationType::ShrinkageCompensationsDiffer; + } else if (text == "_WIPE_TOWER_NOZZLE_DIAMETER_DIFFER") { + text = _u8L("Using the wipe tower for extruders with different nozzle diameters " + "is experimental, so proceed with caution."); + notification_type = NotificationType::WipeTowerNozzleDiameterDiffer; } notification_manager->push_notification( diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 02dfe6cf8b..7b3787b0a5 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1503,8 +1503,9 @@ void TabPrint::build() optgroup = page->new_optgroup(L("Reducing printing time")); category_path = "infill_42#"; + optgroup->append_single_option_line("automatic_infill_combination"); + optgroup->append_single_option_line("automatic_infill_combination_max_layer_height"); optgroup->append_single_option_line("infill_every_layers", category_path + "combine-infill-every-x-layers"); - // optgroup->append_single_option_line("infill_only_where_needed", category_path + "only-infill-where-needed"); optgroup = page->new_optgroup(L("Advanced")); optgroup->append_single_option_line("solid_infill_every_layers", category_path + "solid-infill-every-x-layers"); @@ -1662,6 +1663,7 @@ void TabPrint::build() optgroup->append_single_option_line("solid_infill_extrusion_width"); optgroup->append_single_option_line("top_infill_extrusion_width"); optgroup->append_single_option_line("support_material_extrusion_width"); + optgroup->append_single_option_line("automatic_extrusion_widths"); optgroup = page->new_optgroup(L("Overlap")); optgroup->append_single_option_line("infill_overlap"); From 091bf7d8e3f9a3ff321c63cfdcc1a2d240e0b2f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 11 Nov 2024 21:58:24 +0100 Subject: [PATCH 2/2] SPE-2491: Allow using supports with different nozzle diameters. Printing supports with different nozzle diameters is experimental, so the user is notified that it requires caution. When automatic calculation of extrusion width is used, then the extrusion width of supports is calculated from the nozzle diameter of the smallest extruder. --- src/libslic3r/Flow.cpp | 39 ++++++++++++++++++++++---- src/libslic3r/Print.cpp | 10 +++---- src/slic3r/GUI/NotificationManager.cpp | 12 ++++---- src/slic3r/GUI/NotificationManager.hpp | 8 ++++-- src/slic3r/GUI/Plater.cpp | 17 +++++++++-- 5 files changed, 64 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index bd6fb3d457..ad80156e01 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -230,37 +230,64 @@ double Flow::mm3_per_mm() const return res; } +static float min_nozzle_diameter(const PrintObject &print_object) +{ + const ConfigOptionFloats &nozzle_diameters = print_object.print()->config().nozzle_diameter; + float min_nozzle_diameter = std::numeric_limits::max(); + + for (const double nozzle_diameter : nozzle_diameters.values) { + min_nozzle_diameter = std::min(min_nozzle_diameter, static_cast(nozzle_diameter)); + } + + return min_nozzle_diameter; +} + Flow support_material_flow(const PrintObject *object, float layer_height) { + const PrintConfig &print_config = object->print()->config(); + const int extruder = object->config().support_material_extruder - 1; + + // If object->config().support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), use the smallest nozzle diameter. + const float nozzle_diameter = extruder >= 0 ? static_cast(print_config.nozzle_diameter.get_at(extruder)) : min_nozzle_diameter(*object); + return Flow::new_from_config_width( frSupportMaterial, // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. (object->config().support_material_extrusion_width.value > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width, - // if object->config().support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. - float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)), + nozzle_diameter, (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value)); } Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height) { const PrintConfig &print_config = object->print()->config(); - const auto &width = (print_config.first_layer_extrusion_width.value > 0) ? print_config.first_layer_extrusion_width : object->config().support_material_extrusion_width; + const int extruder = object->config().support_material_extruder - 1; + + // If object->config().support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), use the smallest nozzle diameter. + const float nozzle_diameter = extruder >= 0 ? static_cast(print_config.nozzle_diameter.get_at(extruder)) : min_nozzle_diameter(*object); + const auto &width = (print_config.first_layer_extrusion_width.value > 0) ? print_config.first_layer_extrusion_width : object->config().support_material_extrusion_width; + return Flow::new_from_config_width( frSupportMaterial, // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. (width.value > 0) ? width : object->config().extrusion_width, - float(print_config.nozzle_diameter.get_at(object->config().support_material_extruder-1)), + nozzle_diameter, (layer_height > 0.f) ? layer_height : float(print_config.first_layer_height.get_abs_value(object->config().layer_height.value))); } Flow support_material_interface_flow(const PrintObject *object, float layer_height) { + const PrintConfig &print_config = object->print()->config(); + const int extruder = object->config().support_material_interface_extruder - 1; + + // If object->config().support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), use the smallest nozzle diameter. + const float nozzle_diameter = extruder >= 0 ? static_cast(print_config.nozzle_diameter.get_at(extruder)) : min_nozzle_diameter(*object); + return Flow::new_from_config_width( frSupportMaterialInterface, // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. (object->config().support_material_extrusion_width > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width, - // if object->config().support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. - float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_interface_extruder-1)), + nozzle_diameter, (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value)); } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 92c8d49628..7097576f20 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -744,13 +744,11 @@ std::string Print::validate(std::vector* warnings) const }; for (PrintObject *object : m_objects) { if (object->has_support_material()) { - if ((object->config().support_material_extruder == 0 || object->config().support_material_interface_extruder == 0) && max_nozzle_diameter - min_nozzle_diameter > EPSILON) { + if (warnings != nullptr && (object->config().support_material_extruder == 0 || object->config().support_material_interface_extruder == 0) && max_nozzle_diameter - min_nozzle_diameter > EPSILON) { // The object has some form of support and either support_material_extruder or support_material_interface_extruder - // will be printed with the current tool without a forced tool change. Play safe, assert that all object nozzles - // are of the same diameter. - return _u8L("Printing with multiple extruders of differing nozzle diameters. " - "If support is to be printed with the current extruder (support_material_extruder == 0 or support_material_interface_extruder == 0), " - "all nozzles have to be of the same diameter."); + // will be printed with the current tool without a forced tool change. + // Notify the user that printing supports with different nozzle diameters is experimental and requires caution. + warnings->emplace_back("_SUPPORT_NOZZLE_DIAMETER_DIFFER"); } if (this->has_wipe_tower() && object->config().support_material_style != smsOrganic) { if (object->config().support_material_contact_distance == 0) { diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 99021fa8ab..9674b2f471 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -133,7 +133,7 @@ void NotificationManager::NotificationIDProvider::release_id(int) {} #endif //------PopNotification-------- -NotificationManager::PopNotification::PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler) : +NotificationManager::PopNotification::PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler, const bool multiline) : m_data (n) , m_id_provider (id_provider) , m_text1 (n.text1) @@ -141,6 +141,7 @@ NotificationManager::PopNotification::PopNotification(const NotificationData &n, , m_text2 (n.text2) , m_evt_handler (evt_handler) , m_notification_start (GLCanvas3D::timestamp_now()) + , m_multiline (multiline) {} void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width) @@ -2157,10 +2158,11 @@ void NotificationManager::push_notification(NotificationType type, const std::string& hypertext, std::function callback, const std::string& text_after, - int timestamp) + const int timestamp, + const bool multiline) { int duration = get_standard_duration(level); - push_notification_data({ type, level, duration, text, hypertext, callback, text_after }, timestamp); + push_notification_data({ type, level, duration, text, hypertext, callback, text_after }, timestamp, multiline); } void NotificationManager::push_delayed_notification(const NotificationType type, std::function condition_callback, int64_t initial_delay, int64_t delay_interval) @@ -2839,9 +2841,9 @@ void NotificationManager::push_updated_item_info_notification(InfoItemType type) } } -bool NotificationManager::push_notification_data(const NotificationData& notification_data, int timestamp) +bool NotificationManager::push_notification_data(const NotificationData& notification_data, int timestamp, const bool multiline) { - return push_notification_data(std::make_unique(notification_data, m_id_provider, m_evt_handler), timestamp); + return push_notification_data(std::make_unique(notification_data, m_id_provider, m_evt_handler, multiline), timestamp); } bool NotificationManager::push_notification_data(std::unique_ptr notification, int timestamp) { diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 255a35b7d1..57ecbf4719 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -142,6 +142,8 @@ enum class NotificationType ShrinkageCompensationsDiffer, // Notification about using wipe tower with different nozzle diameters. WipeTowerNozzleDiameterDiffer, + // Notification about using supports with different nozzle diameters. + SupportNozzleDiameterDiffer, }; class NotificationManager @@ -181,7 +183,7 @@ public: // Push a NotificationType::CustomNotification with provided notification level and 10s for RegularNotificationLevel. // ErrorNotificationLevel are never faded out. void push_notification(NotificationType type, NotificationLevel level, const std::string& text, const std::string& hypertext = "", - std::function callback = std::function(), const std::string& text_after = "", int timestamp = 0); + std::function callback = std::function(), const std::string& text_after = "", int timestamp = 0, bool multiline = false); // Pushes basic_notification with delay. See push_delayed_notification_data. void push_delayed_notification(const NotificationType type, std::function condition_callback, int64_t initial_delay, int64_t delay_interval); // Removes all notifications of type from m_waiting_notifications @@ -344,7 +346,7 @@ private: Paused }; - PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler); + PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler, bool multiline = false); virtual ~PopNotification() { if (m_id) m_id_provider.release_id(m_id); } virtual void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width); // close will dissapear notification on next render @@ -855,7 +857,7 @@ private: //pushes notification into the queue of notifications that are rendered //can be used to create custom notification - bool push_notification_data(const NotificationData& notification_data, int timestamp); + bool push_notification_data(const NotificationData& notification_data, int timestamp, bool multiline = false); bool push_notification_data(std::unique_ptr notification, int timestamp); // Delayed notifications goes first to the m_waiting_notifications vector and only after remaining time is <= 0 // and condition callback is success, notification is regular pushed from update function. diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3985e77989..adf299b97d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2098,13 +2098,16 @@ void Plater::priv::process_validation_warning(const std::vector& wa if (warnings.empty()) notification_manager->close_notification_of_type(NotificationType::ValidateWarning); - // Always close warnings BedTemperaturesDiffer, ShrinkageCompensationsDiffer and WipeTowerNozzleDiameterDiffer before next processing. + // Always close warnings BedTemperaturesDiffer, ShrinkageCompensationsDiffer, WipeTowerNozzleDiameterDiffer and SupportNozzleDiameterDiffer before next processing. notification_manager->close_notification_of_type(NotificationType::BedTemperaturesDiffer); notification_manager->close_notification_of_type(NotificationType::ShrinkageCompensationsDiffer); notification_manager->close_notification_of_type(NotificationType::WipeTowerNozzleDiameterDiffer); + notification_manager->close_notification_of_type(NotificationType::SupportNozzleDiameterDiffer); for (std::string text : warnings) { std::string hypertext = ""; + std::string text_after = ""; + bool multiline = false; NotificationType notification_type = NotificationType::ValidateWarning; std::function action_fn = [](wxEvtHandler*){ return false; }; @@ -2133,12 +2136,22 @@ void Plater::priv::process_validation_warning(const std::vector& wa text = _u8L("Using the wipe tower for extruders with different nozzle diameters " "is experimental, so proceed with caution."); notification_type = NotificationType::WipeTowerNozzleDiameterDiffer; + } else if (text == "_SUPPORT_NOZZLE_DIAMETER_DIFFER") { + text = _u8L("Printing supports with different nozzle diameters " + "is experimental. For best results, switch to Organic supports and"); + hypertext = _u8L("assign a specific extruder for supports."); + multiline = true; + notification_type = NotificationType::SupportNozzleDiameterDiffer; + action_fn = [](wxEvtHandler*) { + GUI::wxGetApp().jump_to_option("support_material_extruder", Preset::Type::TYPE_PRINT, boost::nowide::widen("Multiple Extruders")); + return false; + }; } notification_manager->push_notification( notification_type, NotificationManager::NotificationLevel::WarningNotificationLevel, - _u8L("WARNING:") + "\n" + text, hypertext, action_fn + _u8L("WARNING:") + "\n" + text, hypertext, action_fn, text_after, 0, multiline ); } }