From ec424fb674f6d9bad6294447f527fea0a0d60b2d Mon Sep 17 00:00:00 2001 From: Ian Bassi Date: Mon, 11 May 2026 08:38:40 -0300 Subject: [PATCH] Machine Input Shaping (#11202) * Base IS Machine * Toggle line * Rebase * Intento 1 * Wiki IS * Flavorized * Tooltips * Calibration using the same list * max * Reorder JD validation * Refactor set input shaping * Calibrations IS * Default values * Axis * Orca comments * Rename input_shaping_enable to input_shaping_emit Refactor all references of the input shaping configuration option from 'input_shaping_enable' to 'input_shaping_emit' across the codebase. This improves clarity by better reflecting the option's purpose of controlling whether input shaping commands are emitted in the generated G-code. Restore DONT EMIT FOR KLIPPER * Refactor input shaping option toggling logic Simplifies and consolidates the logic for toggling input shaping related options in TabPrinter::toggle_options(). Uses a loop to handle enabling/disabling lines based on GCode flavor compatibility, and refines the conditions for toggling individual options. * Improve input shaping option toggling logic in TabPrinter * GrayOut Emit to gcode limits for klipper * Typo Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Typo * Skip Y input-shaper when type is Disable If marlin2 and disabled it will be already disabled at X. * IS expert Co-Authored-By: SoftFever --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: SoftFever --- src/libslic3r/GCode.cpp | 11 +++ src/libslic3r/GCodeWriter.cpp | 100 +++++++++++++--------- src/libslic3r/Preset.cpp | 2 + src/libslic3r/PrintConfig.cpp | 90 +++++++++++++++++++- src/libslic3r/PrintConfig.hpp | 25 ++++++ src/slic3r/GUI/Field.cpp | 66 ++++++++++++--- src/slic3r/GUI/Plater.cpp | 9 +- src/slic3r/GUI/Tab.cpp | 154 +++++++++++++++++++++++++++++++++- src/slic3r/GUI/Tab.hpp | 4 + src/slic3r/GUI/calib_dlg.cpp | 33 +++++--- 10 files changed, 427 insertions(+), 67 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 14eb0e1c3a..a514019412 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3925,6 +3925,17 @@ void GCode::print_machine_envelope(GCodeOutputStream &file, Print &print) // New Marlin uses M205 J[mm] for junction deviation (only apply if it is > 0) file.write_format(writer().set_junction_deviation(config().machine_max_junction_deviation.values.front()).c_str()); + + // Orca: Override input shaping values + if (print.config().input_shaping_emit.value && flavor != gcfMarlinLegacy) { + const bool input_shaping_disable = print.config().input_shaping_type.value == InputShaperType::Disable; + file.write_format(writer().set_input_shaping('X', print.config().input_shaping_damp_x.value, + print.config().input_shaping_freq_x.value, print.config().opt_serialize("input_shaping_type")).c_str()); + if (flavor != gcfRepRapFirmware && !input_shaping_disable) { + file.write_format(writer().set_input_shaping('Y', print.config().input_shaping_damp_y.value, + print.config().input_shaping_freq_y.value, "").c_str()); + } + } } } diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 2d18a9d5b1..90df770c71 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -350,7 +350,7 @@ std::string GCodeWriter::set_accel_and_jerk(unsigned int acceleration, double je std::string GCodeWriter::set_junction_deviation(double junction_deviation){ std::ostringstream gcode; - if (FLAVOR_IS(gcfMarlinFirmware) && junction_deviation > 0 && m_max_junction_deviation > 0) { + if (FLAVOR_IS(gcfMarlinFirmware) && m_max_junction_deviation > 0 && junction_deviation > 0) { // Clamp the junction deviation to the allowed maximum. gcode << "M205 J"; if (junction_deviation <= m_max_junction_deviation) { @@ -390,68 +390,88 @@ std::string GCodeWriter::set_pressure_advance(double pa) const return gcode.str(); } +// Orca: input shaping support std::string GCodeWriter::set_input_shaping(char axis, float damp, float freq, std::string type) const { - if (FLAVOR_IS(gcfMarlinLegacy)) - throw std::runtime_error("Input shaping is not supported by Marlin < 2.1.2.\nCheck your firmware version and update your G-code flavor to ´Marlin 2´"); - if (freq < 0.0f || damp < 0.f || damp > 1.0f || (axis != 'X' && axis != 'Y' && axis != 'Z' && axis != 'A'))// A = all axis - { - throw std::runtime_error("Invalid input shaping parameters: freq=" + std::to_string(freq) + ", damp=" + std::to_string(damp)); + bool disable = type == "Disable"; + if (disable){ + freq = 0.0f; + damp = 0.0f; + axis = 'A'; + type = "Default"; + } else if (freq < 0.0f || damp < 0.f || damp > 1.0f || (axis != 'X' && axis != 'Y' && axis != 'Z' && axis != 'A')) { // A = all axis + throw std::runtime_error("Invalid input shaping parameters: axis=" + std::string(1, axis) + ", freq=" + std::to_string(freq) + ", damp=" + std::to_string(damp)); } std::ostringstream gcode; - if (FLAVOR_IS(gcfKlipper)) { - gcode << "SET_INPUT_SHAPER"; + std::ostringstream params; + switch (this->config.gcode_flavor) { + case gcfKlipper: { if (!type.empty() && type != "Default") { - gcode << " SHAPER_TYPE=" << type; + params << " SHAPER_TYPE=" << type; } if (axis != 'A') { if (freq > 0.0f) { - gcode << " SHAPER_FREQ_" << axis << "=" << std::fixed << std::setprecision(2) << freq; - } - if (damp > 0.0f){ - gcode << " DAMPING_RATIO_" << axis << "=" << std::fixed << std::setprecision(3) << damp; - } - } else { - if (freq > 0.0f) { - gcode << " SHAPER_FREQ_X=" << std::fixed << std::setprecision(2) << freq << " SHAPER_FREQ_Y=" << std::fixed << std::setprecision(2) << freq; + params << " SHAPER_FREQ_" << axis << "=" << std::fixed << std::setprecision(2) << freq; } if (damp > 0.0f) { - gcode << " DAMPING_RATIO_X=" << std::fixed << std::setprecision(3) << damp << " DAMPING_RATIO_Y=" << std::fixed << std::setprecision(3) << damp; + params << " DAMPING_RATIO_" << axis << "=" << std::fixed << std::setprecision(3) << damp; + } + } else { + if (freq > 0.0f || disable) { + params << " SHAPER_FREQ_X=" << std::fixed << std::setprecision(2) << freq << " SHAPER_FREQ_Y=" << std::fixed << std::setprecision(2) << freq; + } + if (damp > 0.0f || disable) { + params << " DAMPING_RATIO_X=" << std::fixed << std::setprecision(3) << damp << " DAMPING_RATIO_Y=" << std::fixed << std::setprecision(3) << damp; } } - } else if (FLAVOR_IS(gcfRepRapFirmware)) { - gcode << "M593"; + if (!params.str().empty()) { + gcode << "SET_INPUT_SHAPER" << params.str(); + } + break; + } + case gcfRepRapFirmware: { if (!type.empty() && type != "Default" && type != "DAA") { - gcode << " P\"" << type << "\""; + params << " P\"" << type << "\""; } - if (freq > 0.0f) { - gcode << " F" << std::fixed << std::setprecision(2) << freq; + if (freq > 0.0f || disable) { + params << " F" << std::fixed << std::setprecision(2) << freq; } - if (damp > 0.0f){ - gcode << " S" << std::fixed << std::setprecision(3) << damp; + if (damp > 0.0f || disable) { + params << " S" << std::fixed << std::setprecision(3) << damp; } - } else if (FLAVOR_IS(gcfMarlinFirmware)) { - gcode << "M593"; - if (axis != 'A') - { - gcode << " " << axis; + if (!params.str().empty()) { + gcode << "M593" << params.str(); } - if (freq > 0.0f) - { - gcode << " F" << std::fixed << std::setprecision(2) << freq; + break; + } + case gcfMarlinFirmware: { + if (axis != 'A') { + params << " " << axis; } - if (damp > 0.0f) - { - gcode << " D" << std::fixed << std::setprecision(3) << damp; + if (freq > 0.0f || disable) { + params << " F" << std::fixed << std::setprecision(2) << freq; } - } else { + if (damp > 0.0f || disable) { + params << " D" << std::fixed << std::setprecision(3) << damp; + } + if (!params.str().empty()) { + gcode << "M593" << params.str(); + } + break; + } + case gcfMarlinLegacy: { + throw std::runtime_error("Input shaping is not supported by Marlin < 2.1.2.\nCheck your firmware version and update your G-code flavor to ´Marlin 2´"); + } + default: throw std::runtime_error("Input shaping is only supported by Klipper, RepRapFirmware and Marlin 2"); } - if (GCodeWriter::full_gcode_comment){ - gcode << " ; Override input shaping"; + if (!gcode.str().empty()) { + if (GCodeWriter::full_gcode_comment) { + gcode << " ; Override input shaping"; + } + gcode << "\n"; } - gcode << "\n"; return gcode.str(); } diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 22449fcbbf..942be33eb9 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1318,6 +1318,8 @@ static std::vector s_Preset_machine_limits_options { "machine_max_junction_deviation", //resonance avoidance ported from qidi slicer "resonance_avoidance", "min_resonance_avoidance_speed", "max_resonance_avoidance_speed", + // Orca: input shaping + "input_shaping_emit", "input_shaping_type", "input_shaping_freq_x", "input_shaping_freq_y", "input_shaping_damp_x", "input_shaping_damp_y", }; static std::vector s_Preset_printer_options { diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 22d4894edb..bda049232c 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -91,6 +91,25 @@ size_t get_extruder_index(const GCodeConfig& config, unsigned int filament_id) return 0; } + +// Orca: input shaping values types by flavor +std::vector get_shaper_type_values_for_flavor(GCodeFlavor flavor) +{ + switch (flavor) { + case GCodeFlavor::gcfKlipper: + return {"Default", "MZV", "ZV", "ZVD", "EI", "2HUMP_EI", "3HUMP_EI"}; + case GCodeFlavor::gcfRepRapFirmware: + return {"Default", "MZV", "ZV", "ZVD", "ZVDD", "ZVDDD", "EI2", "EI3", "DAA"}; + case GCodeFlavor::gcfMarlinFirmware: + return {"ZV"}; + case GCodeFlavor::gcfMarlinLegacy: + return {}; + default: + break; + } + return {"Default"}; +} + static t_config_enum_names enum_names_from_keys_map(const t_config_enum_values &enum_keys_map) { t_config_enum_names names; @@ -481,6 +500,23 @@ static t_config_enum_values s_keys_map_PrinterStructure { }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PrinterStructure) +static t_config_enum_values s_keys_map_InputShaperType { + {"Default", int(InputShaperType::Default)}, + {"MZV", int(InputShaperType::MZV)}, + {"ZV", int(InputShaperType::ZV)}, + {"ZVD", int(InputShaperType::ZVD)}, + {"ZVDD", int(InputShaperType::ZVDD)}, + {"ZVDDD", int(InputShaperType::ZVDDD)}, + {"EI", int(InputShaperType::EI)}, + {"EI2", int(InputShaperType::EI2)}, + {"2HUMP_EI",int(InputShaperType::TwoHumpEI)}, + {"EI3", int(InputShaperType::EI3)}, + {"3HUMP_EI",int(InputShaperType::ThreeHumpEI)}, + {"DAA", int(InputShaperType::DAA)}, + {"Disable", int(InputShaperType::Disable)} +}; +CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(InputShaperType) + static t_config_enum_values s_keys_map_PerimeterGeneratorType{ { "classic", int(PerimeterGeneratorType::Classic) }, { "arachne", int(PerimeterGeneratorType::Arachne) } @@ -4243,8 +4279,8 @@ void PrintConfigDef::init_fff_params() def = this->add("emit_machine_limits_to_gcode", coBool); def->label = L("Emit limits to G-code"); def->category = L("Machine limits"); - def->tooltip = L("If enabled, the machine limits will be emitted to G-code file.\nThis option will be ignored if the G-code flavor is " - "set to Klipper."); + def->tooltip = L("If enabled, the machine limits will be emitted to G-code file.\nThis option will be ignored if the G-code flavor is " + "set to Klipper."); def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool(true)); @@ -4455,6 +4491,56 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(120)); + // Orca: Input Shaping support + def = this->add("input_shaping_emit", coBool); + def->label = L("Emit input shaping"); + def->tooltip = L("Override firmware input shaping settings.\nIf disabled, firmware settings are used."); + def->mode = comExpert; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("input_shaping_type", coEnum); + def->label = L("Input shaper type"); + def->tooltip = L("Choose the input shaper algorithm.\nDefault uses the firmware default settings.\nDisable turns off input shaping in the firmware."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values = {"Default", "MZV", "ZV", "ZVD", "ZVDD", "ZVDDD", "EI", "EI2", "2HUMP_EI", "EI3", "3HUMP_EI", "DAA", "Disable"}; + def->enum_labels = {L("Default"), L("MZV"), L("ZV"), L("ZVD"), L("ZVDD"), L("ZVDDD"), L("EI"), L("EI2"), L("2HUMP_EI"), L("EI3"), L("3HUMP_EI"), L("DAA"), L("Disable")}; + def->mode = comExpert; + def->set_default_value(new ConfigOptionEnum(InputShaperType::Default)); + + def = this->add("input_shaping_freq_x", coFloat); + def->label = L("X"); + def->tooltip = L("Resonant frequency for the X axis input shaper.\nZero will use the firmware frequency.\nTo disable input shaping, use the Disable type.\nRRF: X and Y values are equal."); + def->sidetext = "Hz"; + def->min = 0; + def->max = 1000; + def->mode = comExpert; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("input_shaping_freq_y", coFloat); + def->label = L("Y"); + def->tooltip = L("Resonant frequency for the Y axis input shaper.\nZero will use the firmware frequency.\nTo disable input shaping, use the Disable type."); + def->sidetext = "Hz"; + def->min = 0; + def->max = 1000; + def->mode = comExpert; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("input_shaping_damp_x", coFloat); + def->label = L("X"); + def->tooltip = L("Damping ratio for the X axis input shaper.\nZero will use the firmware damping ratio.\nTo disable input shaping, use the Disable type.\nRRF: X and Y values are equal."); + def->min = 0; + def->max = 1; + def->mode = comExpert; + def->set_default_value(new ConfigOptionFloat(0.1)); + + def = this->add("input_shaping_damp_y", coFloat); + def->label = L("Y"); + def->tooltip = L("Damping ratio for the Y axis input shaper.\nZero will use the firmware damping ratio.\nTo disable input shaping, use the Disable type."); + def->min = 0; + def->max = 1; + def->mode = comExpert; + def->set_default_value(new ConfigOptionFloat(0.1)); + def = this->add("fan_max_speed", coFloats); def->label = L("Fan speed"); def->tooltip = L("Part cooling fan speed may be increased when auto cooling is enabled. " diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 2b62000c1a..416df9c98b 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -362,6 +362,22 @@ enum PrinterStructure { psDelta }; +enum class InputShaperType : unsigned char { + Default = 0, + MZV, + ZV, + ZVD, + ZVDD, + ZVDDD, + EI, + EI2, + TwoHumpEI, + EI3, + ThreeHumpEI, + DAA, + Disable +}; + // BBS enum ZHopType { zhtAuto = 0, @@ -525,6 +541,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BrimType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(TimelapseType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BedType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SkirtType) +CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(InputShaperType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(DraftShield) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ForwardCompatibilitySubstitutionRule) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeThumbnailsFormat) @@ -1259,6 +1276,14 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, resonance_avoidance)) ((ConfigOptionFloat, min_resonance_avoidance_speed)) ((ConfigOptionFloat, max_resonance_avoidance_speed)) + + //Orca: Input shaping + ((ConfigOptionBool, input_shaping_emit)) + ((ConfigOptionEnum, input_shaping_type)) + ((ConfigOptionFloat, input_shaping_freq_x)) + ((ConfigOptionFloat, input_shaping_freq_y)) + ((ConfigOptionFloat, input_shaping_damp_x)) + ((ConfigOptionFloat, input_shaping_damp_y)) ) // This object is mapped to Perl as Slic3r::Config::GCode. diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index bf3b2b8eda..623597b933 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -11,6 +11,7 @@ #include "libslic3r/PrintConfig.hpp" #include +#include #include #include #include @@ -1626,18 +1627,45 @@ void Choice::set_value(const boost::any& value, bool change_event) } case coEnum: // BBS - case coEnums: { + case coEnums: { int val = boost::any_cast(value); + int selection = val; - // Support ThirdPartyPrinter - if (m_opt_id.compare("host_type") == 0 && val != 0 && - m_opt.enum_values.size() > field->GetCount()) // for case, when PrusaLink isn't used as a HostType - val--; - if (m_opt_id == "top_surface_pattern" || m_opt_id == "bottom_surface_pattern" || - m_opt_id == "internal_solid_infill_pattern" || m_opt_id == "sparse_infill_pattern" || - m_opt_id == "support_base_pattern" || m_opt_id == "support_interface_pattern" || - m_opt_id == "ironing_pattern" || m_opt_id == "support_ironing_pattern" || - m_opt_id == "support_style" || m_opt_id == "curr_bed_type" || m_opt_id == "wipe_tower_wall_type") + if (m_opt_id == "input_shaping_type") { + if (field != nullptr) { + const unsigned int count = field->GetCount(); + int match_index = -1; + for (unsigned int idx = 0; idx < count; ++idx) { + if (void* data = field->GetClientData(idx)) { + int stored = static_cast(reinterpret_cast(data)); + if (stored == val) { + match_index = static_cast(idx); + break; + } + } + } + if (match_index >= 0) + selection = match_index; + else if (val >= 0 && val < static_cast(count)) + selection = val; + else if (count > 0) + selection = 0; + else + selection = -1; + } + } else { + // Support ThirdPartyPrinter + if (m_opt_id.compare("host_type") == 0 && val != 0 && + m_opt.enum_values.size() > field->GetCount()) // for case, when PrusaLink isn't used as a HostType + selection = val - 1; + else + selection = val; + + if (m_opt_id == "top_surface_pattern" || m_opt_id == "bottom_surface_pattern" || + m_opt_id == "internal_solid_infill_pattern" || m_opt_id == "sparse_infill_pattern" || + m_opt_id == "support_base_pattern" || m_opt_id == "support_interface_pattern" || + m_opt_id == "ironing_pattern" || m_opt_id == "support_ironing_pattern" || + m_opt_id == "support_style" || m_opt_id == "curr_bed_type" || m_opt_id == "wipe_tower_wall_type") { std::string key; const t_config_enum_values& map_names = *m_opt.enum_keys_map; @@ -1649,15 +1677,16 @@ void Choice::set_value(const boost::any& value, bool change_event) const std::vector& values = m_opt.enum_values; auto it = std::find(values.begin(), values.end(), key); - val = it == values.end() ? 0 : it - values.begin(); + selection = it == values.end() ? 0 : static_cast(it - values.begin()); } + } if (m_opt.nullable) { if (val != ConfigOptionEnumsGenericNullable::nil_value()) m_last_meaningful_value = value; else - val = -1; + selection = -1; } - field->SetSelection(val); + field->SetSelection(selection); break; } default: @@ -1724,6 +1753,17 @@ boost::any& Choice::get_value() { if (m_opt.nullable && field->GetSelection() == -1) m_value = ConfigOptionEnumsGenericNullable::nil_value(); + else if (m_opt_id == "input_shaping_type") + { + int selection = field->GetSelection(); + if (selection >= 0) { + if (void* data = field->GetClientData(selection)) + m_value = static_cast(reinterpret_cast(data)); + else + m_value = selection; + } else + m_value = 0; + } else if ( m_opt_id == "top_surface_pattern" || m_opt_id == "bottom_surface_pattern" || m_opt_id == "internal_solid_infill_pattern" || m_opt_id == "sparse_infill_pattern" || m_opt_id == "support_base_pattern" || m_opt_id == "support_interface_pattern" || diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 87584f88fd..7328e24d60 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -13296,6 +13296,8 @@ void Plater::calib_VFA(const Calib_Params& params) auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config; auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config; printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false}); + printer_config->set_key_value("input_shaping_emit", new ConfigOptionBool{true}); + printer_config->set_key_value("input_shaping_type", new ConfigOptionEnum(InputShaperType::Disable)); filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 0.0 }); print_config->set_key_value("enable_overhang_speed", new ConfigOptionBool { false }); print_config->set_key_value("timelapse_type", new ConfigOptionEnum(tlTraditional)); @@ -13359,10 +13361,12 @@ void Plater::calib_input_shaping_freq(const Calib_Params& params) } printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false}); + printer_config->set_key_value("input_shaping_emit", new ConfigOptionBool{false}); filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 0.0 }); filament_config->set_key_value("slow_down_min_speed", new ConfigOptionFloats { 0.0 }); filament_config->set_key_value("slow_down_for_layer_cooling", new ConfigOptionBools{false}); - print_config->set_key_value("enable_overhang_speed", new ConfigOptionBool { false }); + print_config->set_key_value("layer_height", new ConfigOptionFloat(0.2)); + print_config->set_key_value("enable_overhang_speed", new ConfigOptionBool{false}); print_config->set_key_value("timelapse_type", new ConfigOptionEnum(tlTraditional)); print_config->set_key_value("wall_loops", new ConfigOptionInt(1)); print_config->set_key_value("top_shell_layers", new ConfigOptionInt(0)); @@ -13420,6 +13424,7 @@ void Plater::calib_input_shaping_damp(const Calib_Params& params) } printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false}); + printer_config->set_key_value("input_shaping_emit", new ConfigOptionBool{false}); filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 0.0 }); filament_config->set_key_value("slow_down_min_speed", new ConfigOptionFloats { 0.0 }); filament_config->set_key_value("slow_down_for_layer_cooling", new ConfigOptionBools{false}); @@ -13482,6 +13487,8 @@ void Plater::Calib_Cornering(const Calib_Params& params) } printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false}); + printer_config->set_key_value("input_shaping_emit", new ConfigOptionBool{true}); + printer_config->set_key_value("input_shaping_type", new ConfigOptionEnum(InputShaperType::Disable)); filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 0.0 }); filament_config->set_key_value("slow_down_min_speed", new ConfigOptionFloats { 0.0 }); filament_config->set_key_value("slow_down_for_layer_cooling", new ConfigOptionBools{false}); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index ae428b4970..f882986e83 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -48,6 +48,7 @@ #include "Widgets/Label.hpp" #include "Widgets/SwitchButton.hpp" #include "Widgets/TabCtrl.hpp" +#include "Widgets/ComboBox.hpp" #include "MarkdownTip.hpp" #include "Search.hpp" #include "BedShapeDialog.hpp" @@ -1494,6 +1495,11 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) return; } + if (opt_key == "gcode_flavor" && m_type == Preset::TYPE_PRINTER) { + if (auto printer_tab = dynamic_cast(this)) + printer_tab->on_gcode_flavor_changed(); + } + if (opt_key == "compatible_prints") this->compatible_widget_reload(m_compatible_prints); if (opt_key == "compatible_printers") @@ -4796,7 +4802,7 @@ PageShp TabPrinter::build_kinematics_page() optgroup->append_single_option_line("emit_machine_limits_to_gcode", "printer_motion_ability#emit-limits-to-g-code"); // resonance avoidance ported over from qidi slicer - optgroup = page->new_optgroup(L("Resonance Avoidance"), "param_resonance_avoidance"); + optgroup = page->new_optgroup(L("Resonance Compensation"), "param_resonance_avoidance"); optgroup->append_single_option_line("resonance_avoidance", "printer_motion_ability#resonance-avoidance"); // Resonance‑avoidance speed inputs { @@ -4806,6 +4812,20 @@ PageShp TabPrinter::build_kinematics_page() resonance_line.append_option(optgroup->get_option("max_resonance_avoidance_speed")); optgroup->append_line(resonance_line); } + optgroup->append_single_option_line("input_shaping_emit", "input-shaping-calib"); + optgroup->append_single_option_line("input_shaping_type", "input-shaping-calib"); + { + Line freq_line = {L("Frequency"), L("The frequency of the anti-vibration signal will correspond to the natural frequency of the frame.")}; + freq_line.append_option(optgroup->get_option("input_shaping_freq_x")); + freq_line.append_option(optgroup->get_option("input_shaping_freq_y")); + optgroup->append_line(freq_line); + } + { + Line damping_line = {L("Damping"), L("Damping ratio for the input shaping filter.")}; + damping_line.append_option(optgroup->get_option("input_shaping_damp_x")); + damping_line.append_option(optgroup->get_option("input_shaping_damp_y")); + optgroup->append_line(damping_line); + } const std::vector speed_axes{ "machine_max_speed_x", @@ -5248,6 +5268,115 @@ void TabPrinter::clear_pages() m_reset_to_filament_color = nullptr; } +std::vector input_shaper_types_for_flavor(GCodeFlavor flavor) +{ + switch (flavor) { + case GCodeFlavor::gcfKlipper: + return { + InputShaperType::Default, + InputShaperType::ZV, + InputShaperType::MZV, + InputShaperType::ZVD, + InputShaperType::EI, + InputShaperType::TwoHumpEI, + InputShaperType::ThreeHumpEI, + InputShaperType::Disable + }; + case GCodeFlavor::gcfRepRapFirmware: + return { + InputShaperType::Default, + InputShaperType::MZV, + InputShaperType::ZVD, + InputShaperType::ZVDD, + InputShaperType::ZVDDD, + InputShaperType::EI2, + InputShaperType::EI3, + InputShaperType::DAA, + InputShaperType::Disable + }; + case GCodeFlavor::gcfMarlinFirmware: + return { + InputShaperType::ZV, + InputShaperType::Disable + }; + default: + return { + InputShaperType::Default, + InputShaperType::Disable + }; + } +} + +void TabPrinter::update_input_shaper_menu(GCodeFlavor flavor) +{ + if (m_presets->get_edited_preset().printer_technology() != ptFFF) + return; + + const std::vector allowed = input_shaper_types_for_flavor(flavor); + if (allowed.empty()) + return; + + const InputShaperType current = m_config->opt_enum("input_shaping_type"); + const bool needs_reset = std::find(allowed.begin(), allowed.end(), current) == allowed.end(); + const InputShaperType desired = needs_reset ? allowed.front() : current; + + if (needs_reset && current != desired) { + DynamicPrintConfig new_conf = *m_config; + new_conf.set_key_value("input_shaping_type", new ConfigOptionEnum(desired)); + m_config_manipulation.apply(m_config, &new_conf); + } + + Page* owning_page = nullptr; + Field* field = get_field("input_shaping_type", &owning_page); + if (field == nullptr) + return; + + auto choice_field = dynamic_cast(field); + if (choice_field == nullptr) + return; + + auto combo = dynamic_cast(choice_field->getWindow()); + if (combo == nullptr) + return; + + const ConfigOptionDef* def = m_config->def()->get("input_shaping_type"); + if (def == nullptr) + return; + + const auto& labels = def->enum_labels; + const auto& values = def->enum_values; + + wxWindowUpdateLocker locker(combo); + combo->Clear(); + + for (InputShaperType type : allowed) { + const size_t idx = static_cast(type); + wxString label; + if (idx < labels.size() && !labels[idx].empty()) + label = _(labels[idx]); + else if (idx < values.size()) + label = wxString::FromUTF8(values[idx].c_str()); + else + label = wxString::Format("%d", static_cast(type)); + + combo->Append(label, wxNullBitmap, + reinterpret_cast(static_cast(static_cast(type)))); + } + + if (combo->GetCount() == 0) + return; + + choice_field->set_value(static_cast(desired), false); +} + +void TabPrinter::on_gcode_flavor_changed() +{ + auto* flavor_option = m_config->option>("gcode_flavor"); + if (!flavor_option) + return; + update_input_shaper_menu(flavor_option->value); +} + void TabPrinter::toggle_options() { if (!m_active_page || m_presets->get_edited_preset().printer_technology() == ptSLA) @@ -5400,6 +5529,7 @@ void TabPrinter::toggle_options() if (m_active_page->title() == L("Motion ability")) { auto gcf = m_config->option>("gcode_flavor")->value; + update_input_shaper_menu(gcf); bool silent_mode = m_config->opt_bool("silent_mode"); int max_field = silent_mode ? 2 : 1; for (int i = 0; i < max_field; ++i) @@ -5427,9 +5557,31 @@ void TabPrinter::toggle_options() toggle_option("machine_max_jerk_e", enable_jerk, i); } + bool emittable_limits = m_config->opt_enum("gcode_flavor") == GCodeFlavor::gcfMarlinLegacy || m_config->opt_enum("gcode_flavor") == GCodeFlavor::gcfMarlinFirmware || m_config->opt_enum("gcode_flavor") == GCodeFlavor::gcfRepRapFirmware; + toggle_option("emit_machine_limits_to_gcode", emittable_limits); + bool resonance_avoidance = m_config->opt_bool("resonance_avoidance"); toggle_option("min_resonance_avoidance_speed", resonance_avoidance); toggle_option("max_resonance_avoidance_speed", resonance_avoidance); + + bool input_shaping_compatible = m_config->opt_enum("gcode_flavor") == GCodeFlavor::gcfMarlinFirmware || m_config->opt_enum("gcode_flavor") == GCodeFlavor::gcfRepRapFirmware; + + for (auto is : {"input_shaping_emit", "input_shaping_type", "input_shaping_freq_x", "input_shaping_freq_y", + "input_shaping_damp_x", "input_shaping_damp_y"}) + toggle_line(is, input_shaping_compatible); + + if (input_shaping_compatible) { + bool emit_machine_limits_to_gcode = m_config->opt_bool("emit_machine_limits_to_gcode"); + toggle_option("input_shaping_emit", emit_machine_limits_to_gcode); + bool input_shaping_emit = emit_machine_limits_to_gcode && m_config->opt_bool("input_shaping_emit"); + bool reprap = m_config->opt_enum("gcode_flavor") == GCodeFlavor::gcfRepRapFirmware; + toggle_option("input_shaping_type", input_shaping_emit); + toggle_option("input_shaping_freq_x", input_shaping_emit); + toggle_option("input_shaping_freq_y", input_shaping_emit && !reprap); + toggle_option("input_shaping_damp_x", input_shaping_emit); + toggle_option("input_shaping_damp_y", input_shaping_emit && !reprap); + } + } } diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index e529ebaed0..ec9cfb2db3 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -57,6 +57,8 @@ namespace GUI { class TabPresetComboBox; class OG_CustomCtrl; +std::vector input_shaper_types_for_flavor(GCodeFlavor flavor); + // Single Tab page containing a{ vsizer } of{ optgroups } // package Slic3r::GUI::Tab::Page; using ConfigOptionsGroupShp = std::shared_ptr; @@ -600,6 +602,7 @@ private: bool m_use_silent_mode = false; void append_option_line(ConfigOptionsGroupShp optgroup, const std::string opt_key, const std::string& label_path = ""); bool m_rebuild_kinematics_page = false; + void update_input_shaper_menu(GCodeFlavor flavor); ogStaticText* m_fff_print_host_upload_description_line {nullptr}; ogStaticText* m_sla_print_host_upload_description_line {nullptr}; @@ -637,6 +640,7 @@ public: void update_fff(); void update_sla(); void update_pages(); // update m_pages according to printer technology + void on_gcode_flavor_changed(); void extruders_count_changed(size_t extruders_count); PageShp build_kinematics_page(); void build_unregular_pages(bool from_initial_build = false); diff --git a/src/slic3r/GUI/calib_dlg.cpp b/src/slic3r/GUI/calib_dlg.cpp index 50b69663e0..ba78d09e2a 100644 --- a/src/slic3r/GUI/calib_dlg.cpp +++ b/src/slic3r/GUI/calib_dlg.cpp @@ -13,6 +13,8 @@ namespace Slic3r { namespace GUI { +std::vector input_shaper_types_for_flavor(GCodeFlavor flavor); + namespace { void ParseStringValues(std::string str, std::vector &vec) @@ -36,18 +38,29 @@ std::vector get_shaper_type_values() { if (auto* preset_bundle = wxGetApp().preset_bundle) { auto printer_config = &preset_bundle->printers.get_edited_preset().config; - if (auto* gcode_flavor_option = printer_config->option>("gcode_flavor")) { - switch (gcode_flavor_option->value) { - case GCodeFlavor::gcfKlipper: - return {"Default", "ZV", "MZV", "ZVD", "EI", "2HUMP_EI", "3HUMP_EI"}; - case GCodeFlavor::gcfRepRapFirmware: - return {"Default", "MZV", "ZVD", "ZVDD", "ZVDDD", "EI2", "EI3", "DAA"}; - case GCodeFlavor::gcfMarlinFirmware: - return {"ZV"}; - default: - break; + const auto* gcode_flavor_option = printer_config->option>("gcode_flavor"); + const ConfigOptionDef* def = printer_config->def()->get("input_shaping_type"); + if (gcode_flavor_option) { + auto types = input_shaper_types_for_flavor(gcode_flavor_option->value); + if (!types.empty()) { + std::vector values; + values.reserve(types.size()); + for (InputShaperType type : types) { + if (type == InputShaperType::Disable) + continue; + const size_t idx = static_cast(type); + if (def && idx < def->enum_values.size()) + values.push_back(def->enum_values[idx]); + else + values.push_back(std::to_string(static_cast(type))); + } + if (!values.empty()) + return values; } } + + if (def && !def->enum_values.empty()) + return {def->enum_values.front()}; } return {"Default"}; }