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 <softfeverever@gmail.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
Ian Bassi
2026-05-11 08:38:40 -03:00
committed by GitHub
parent c8be7fb708
commit ec424fb674
10 changed files with 427 additions and 67 deletions

View File

@@ -11,6 +11,7 @@
#include "libslic3r/PrintConfig.hpp"
#include <regex>
#include <cstdint>
#include <wx/numformatter.h>
#include <wx/tooltip.h>
#include <wx/notebook.h>
@@ -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<int>(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<int>(reinterpret_cast<uintptr_t>(data));
if (stored == val) {
match_index = static_cast<int>(idx);
break;
}
}
}
if (match_index >= 0)
selection = match_index;
else if (val >= 0 && val < static_cast<int>(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<std::string>& 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<int>(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<int>(reinterpret_cast<uintptr_t>(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" ||

View File

@@ -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>(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<TimelapseType>(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<TimelapseType>(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>(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});

View File

@@ -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<TabPrinter*>(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");
// Resonanceavoidance 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<std::string> speed_axes{
"machine_max_speed_x",
@@ -5248,6 +5268,115 @@ void TabPrinter::clear_pages()
m_reset_to_filament_color = nullptr;
}
std::vector<InputShaperType> 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<InputShaperType> allowed = input_shaper_types_for_flavor(flavor);
if (allowed.empty())
return;
const InputShaperType current = m_config->opt_enum<InputShaperType>("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<InputShaperType>(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<Choice*>(field);
if (choice_field == nullptr)
return;
auto combo = dynamic_cast<ComboBox*>(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<size_t>(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<int>(type));
combo->Append(label, wxNullBitmap,
reinterpret_cast<void*>(static_cast<uintptr_t>(static_cast<int>(type))));
}
if (combo->GetCount() == 0)
return;
choice_field->set_value(static_cast<int>(desired), false);
}
void TabPrinter::on_gcode_flavor_changed()
{
auto* flavor_option = m_config->option<ConfigOptionEnum<GCodeFlavor>>("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<ConfigOptionEnum<GCodeFlavor>>("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<GCodeFlavor>("gcode_flavor") == GCodeFlavor::gcfMarlinLegacy || m_config->opt_enum<GCodeFlavor>("gcode_flavor") == GCodeFlavor::gcfMarlinFirmware || m_config->opt_enum<GCodeFlavor>("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<GCodeFlavor>("gcode_flavor") == GCodeFlavor::gcfMarlinFirmware || m_config->opt_enum<GCodeFlavor>("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<GCodeFlavor>("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);
}
}
}

View File

@@ -57,6 +57,8 @@ namespace GUI {
class TabPresetComboBox;
class OG_CustomCtrl;
std::vector<InputShaperType> 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<ConfigOptionsGroup>;
@@ -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);

View File

@@ -13,6 +13,8 @@
namespace Slic3r { namespace GUI {
std::vector<InputShaperType> input_shaper_types_for_flavor(GCodeFlavor flavor);
namespace {
void ParseStringValues(std::string str, std::vector<double> &vec)
@@ -36,18 +38,29 @@ std::vector<std::string> 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<ConfigOptionEnum<GCodeFlavor>>("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<ConfigOptionEnum<GCodeFlavor>>("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<std::string> values;
values.reserve(types.size());
for (InputShaperType type : types) {
if (type == InputShaperType::Disable)
continue;
const size_t idx = static_cast<size_t>(type);
if (def && idx < def->enum_values.size())
values.push_back(def->enum_values[idx]);
else
values.push_back(std::to_string(static_cast<int>(type)));
}
if (!values.empty())
return values;
}
}
if (def && !def->enum_values.empty())
return {def->enum_values.front()};
}
return {"Default"};
}