From 19dae83e40145ae94174c870ee02a85fcb9576db Mon Sep 17 00:00:00 2001 From: Ian Bassi Date: Thu, 20 Mar 2025 10:17:20 -0300 Subject: [PATCH 01/10] Input Shaping Frequency Input Shaping calibration based in https://marlinfw.org/docs/gcode/M593.html --- .../calib/input_shaping/ringing_tower.stl | Bin 0 -> 175284 bytes src/libslic3r/GCode.cpp | 6 +- src/libslic3r/GCodeWriter.cpp | 19 +++ src/libslic3r/GCodeWriter.hpp | 1 + src/libslic3r/calib.cpp | 2 + src/libslic3r/calib.hpp | 10 +- src/slic3r/GUI/CalibrationWizardSavePage.cpp | 2 + src/slic3r/GUI/MainFrame.cpp | 11 ++ src/slic3r/GUI/MainFrame.hpp | 1 + src/slic3r/GUI/Plater.cpp | 34 ++++++ src/slic3r/GUI/Plater.hpp | 1 + src/slic3r/GUI/calib_dlg.cpp | 114 +++++++++++++++++- src/slic3r/GUI/calib_dlg.hpp | 20 ++- src/slic3r/Utils/CalibUtils.cpp | 4 + 14 files changed, 217 insertions(+), 8 deletions(-) create mode 100644 resources/calib/input_shaping/ringing_tower.stl diff --git a/resources/calib/input_shaping/ringing_tower.stl b/resources/calib/input_shaping/ringing_tower.stl new file mode 100644 index 0000000000000000000000000000000000000000..70cae09ea63db2411493f22ab8c4677f64f7467f GIT binary patch literal 175284 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 literal 0 HcmV?d00001 diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ce3da04890..be180e3cb5 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3749,6 +3749,7 @@ LayerResult GCode::process_layer( //BBS: set layer time fan speed after layer change gcode gcode += ";_SET_FAN_SPEED_CHANGING_LAYER\n"; + //TODO: Why this is not a Switch-case? if (print.calib_mode() == CalibMode::Calib_PA_Tower) { gcode += writer().set_pressure_advance(print.calib_params().start + static_cast(print_z) * print.calib_params().step); } else if (print.calib_mode() == CalibMode::Calib_Temp_Tower) { @@ -3760,14 +3761,15 @@ LayerResult GCode::process_layer( } else if (print.calib_mode() == CalibMode::Calib_Vol_speed_Tower) { auto _speed = print.calib_params().start + print_z * print.calib_params().step; m_calib_config.set_key_value("outer_wall_speed", new ConfigOptionFloat(std::round(_speed))); - } - else if (print.calib_mode() == CalibMode::Calib_Retraction_tower) { + } else if (print.calib_mode() == CalibMode::Calib_Retraction_tower) { auto _length = print.calib_params().start + std::floor(std::max(0.0,print_z-0.4)) * print.calib_params().step; DynamicConfig _cfg; _cfg.set_key_value("retraction_length", new ConfigOptionFloats{_length}); writer().config.apply(_cfg); sprintf(buf, "; Calib_Retraction_tower: Z_HEIGHT: %g, length:%g\n", print_z, _length); gcode += buf; + } else if (print.calib_mode() == CalibMode::Calib_Input_shaping) { + gcode += writer().set_input_shaping(m_layer_index < 2 ? 0.f : (print.calib_params().start) + ((print.calib_params().end)-(print.calib_params().start)) * (m_layer_index - 2) / (m_layer_count - 3), (print.calib_params().step)); } //BBS diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index e72bbb6685..acbff7a667 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -333,6 +333,25 @@ std::string GCodeWriter::set_pressure_advance(double pa) const return gcode.str(); } +std::string GCodeWriter::set_input_shaping(float freq, float damp) const +{ + std::ostringstream gcode; + if (FLAVOR_IS(gcfKlipper)) + { + throw std::runtime_error("M593 - ZV Input Shaping is NOT supported by Klipper"); + } + if (freq < 0.0f || damp < 0.f || damp > 1.0f) + { + throw std::runtime_error("Invalid input shaping parameters: freq=" + std::to_string(freq) + ", damp=" + std::to_string(damp)); + } + gcode << "M593 F" << freq; + if (damp != 0.0f) + { + gcode << " D" << damp; + } + gcode << "; Override input shaping value\n"; + return gcode.str(); +} std::string GCodeWriter::reset_e(bool force) diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 038325b446..69e20515a4 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -54,6 +54,7 @@ public: // Orca: set acceleration and jerk in one command for Klipper std::string set_accel_and_jerk(unsigned int acceleration, double jerk); std::string set_pressure_advance(double pa) const; + std::string set_input_shaping(float freq, float damp) const; std::string reset_e(bool force = false); std::string update_progress(unsigned int num, unsigned int tot, bool allow_100 = false) const; // return false if this extruder was already selected diff --git a/src/libslic3r/calib.cpp b/src/libslic3r/calib.cpp index efec782e7f..81d5d507f2 100644 --- a/src/libslic3r/calib.cpp +++ b/src/libslic3r/calib.cpp @@ -879,4 +879,6 @@ double CalibPressureAdvancePattern::pattern_shift() const { return (wall_count() - 1) * line_spacing_first_layer() + line_width_first_layer() + m_glyph_padding_horizontal; } + + } // namespace Slic3r diff --git a/src/libslic3r/calib.hpp b/src/libslic3r/calib.hpp index 2a02be5e62..b8c81db86c 100644 --- a/src/libslic3r/calib.hpp +++ b/src/libslic3r/calib.hpp @@ -21,7 +21,8 @@ enum class CalibMode : int { Calib_Temp_Tower, Calib_Vol_speed_Tower, Calib_VFA_Tower, - Calib_Retraction_tower + Calib_Retraction_tower, + Calib_Input_shaping }; enum class CalibState { Start = 0, Preset, Calibration, CoarseSave, FineCalibration, Save, Finish }; @@ -30,13 +31,13 @@ struct Calib_Params { Calib_Params() : mode(CalibMode::Calib_None){}; double start, end, step; - bool print_numbers; + bool print_numbers; std::vector accelerations; std::vector speeds; CalibMode mode; -}; + }; enum FlowRatioCalibrationType { COMPLETE_CALIBRATION = 0, @@ -335,4 +336,5 @@ private: const double m_glyph_padding_horizontal{1}; const double m_glyph_padding_vertical{1}; }; -} // namespace Slic3r + +} // namespace Slic3 \ No newline at end of file diff --git a/src/slic3r/GUI/CalibrationWizardSavePage.cpp b/src/slic3r/GUI/CalibrationWizardSavePage.cpp index 246623fffe..7131e0a391 100644 --- a/src/slic3r/GUI/CalibrationWizardSavePage.cpp +++ b/src/slic3r/GUI/CalibrationWizardSavePage.cpp @@ -44,6 +44,8 @@ static wxString get_default_name(wxString filament_name, CalibMode mode){ break; case Slic3r::CalibMode::Calib_Retraction_tower: break; + case Slic3r::CalibMode::Calib_Input_shaping: + break; default: break; } diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 3b81a41bd4..2767a2a576 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -2974,6 +2974,17 @@ void MainFrame::init_menubar_as_editor() }, "", nullptr, [this]() {return m_plater->is_view3D_shown();; }, this); + + append_menu_item( + advance_menu, wxID_ANY, _L("Input Shaping"), _L("Input Shaping"), + [this](wxCommandEvent&) { + std::string url = "https://marlinfw.org/docs/gcode/M593.html";//TODO: Make OrcaSlicer wiki page + if (!m_IS_calib_dlg) + m_IS_calib_dlg = new Input_Shaping_Test_Dlg((wxWindow*)this, wxID_ANY, m_plater); + m_IS_calib_dlg->ShowModal(); + }, + "", nullptr, + [this]() {return m_plater->is_view3D_shown();; }, this); m_topbar->GetCalibMenu()->AppendSubMenu(advance_menu, _L("More...")); // help diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 18682a2071..311a12c855 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -354,6 +354,7 @@ public: MaxVolumetricSpeed_Test_Dlg* m_vol_test_dlg { nullptr }; VFA_Test_Dlg* m_vfa_test_dlg { nullptr }; Retraction_Test_Dlg* m_retraction_calib_dlg{ nullptr }; + Input_Shaping_Test_Dlg* m_IS_calib_dlg{ nullptr }; // BBS. Replace title bar and menu bar with top bar. BBLTopbar* m_topbar{ nullptr }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index fa02512197..a259a3a6ca 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -10149,6 +10149,40 @@ void Plater::calib_VFA(const Calib_Params& params) p->background_process.fff_print()->set_calib_params(params); } + +void Plater::calib_input_shaping(const Calib_Params& params) +{ + const auto calib_input_shaping_name = wxString::Format(L"Input shaping test"); + new_project(false, false, calib_input_shaping_name); + wxGetApp().mainframe->select_tab(size_t(MainFrame::tp3DEditor)); + if (params.mode != CalibMode::Calib_Input_shaping) + return; + + add_model(false, Slic3r::resources_dir() + "/calib/input_shaping/ringing_tower.stl"); + auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config; + auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config; + filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 2.0 }); + filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats { 200 }); + 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)); + print_config->set_key_value("bottom_shell_layers", new ConfigOptionInt(1)); + print_config->set_key_value("sparse_infill_density", new ConfigOptionPercent(0)); + print_config->set_key_value("spiral_mode", new ConfigOptionBool(true)); + model().objects[0]->config.set_key_value("brim_type", new ConfigOptionEnum(btOuterOnly)); + model().objects[0]->config.set_key_value("brim_width", new ConfigOptionFloat(3.0)); + model().objects[0]->config.set_key_value("brim_object_gap", new ConfigOptionFloat(0.0)); + + changed_objects({ 0 }); + wxGetApp().get_tab(Preset::TYPE_PRINT)->update_dirty(); + wxGetApp().get_tab(Preset::TYPE_FILAMENT)->update_dirty(); + wxGetApp().get_tab(Preset::TYPE_PRINT)->update_ui_from_settings(); + wxGetApp().get_tab(Preset::TYPE_FILAMENT)->update_ui_from_settings(); + + p->background_process.fff_print()->set_calib_params(params); +} + BuildVolume_Type Plater::get_build_volume_type() const { return p->bed.get_build_volume_type(); } void Plater::import_zip_archive() diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 34f276132e..b8f6e93333 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -273,6 +273,7 @@ public: void calib_max_vol_speed(const Calib_Params& params); void calib_retraction(const Calib_Params& params); void calib_VFA(const Calib_Params& params); + void calib_input_shaping(const Calib_Params& params); BuildVolume_Type get_build_volume_type() const; diff --git a/src/slic3r/GUI/calib_dlg.cpp b/src/slic3r/GUI/calib_dlg.cpp index 615114b413..f4d282ed03 100644 --- a/src/slic3r/GUI/calib_dlg.cpp +++ b/src/slic3r/GUI/calib_dlg.cpp @@ -722,7 +722,7 @@ Retraction_Test_Dlg::Retraction_Test_Dlg(wxWindow* parent, wxWindowID id, Plater auto end_length_sizer = new wxBoxSizer(wxHORIZONTAL); auto end_length_text = new wxStaticText(this, wxID_ANY, end_length_str, wxDefaultPosition, st_size, wxALIGN_LEFT); m_tiEnd = new TextInput(this, std::to_string(2), _L("mm"), "", wxDefaultPosition, ti_size, wxTE_CENTRE); - m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + m_tiEnd->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));//m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));//TODO: IANALEXIS CHECK end_length_sizer->Add(end_length_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); end_length_sizer->Add(m_tiEnd, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); settings_sizer->Add(end_length_sizer); @@ -789,5 +789,117 @@ void Retraction_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect) { } +// Input_Shaping_Test_Dlg +// + +Input_Shaping_Test_Dlg::Input_Shaping_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater) + : DPIDialog(parent, id, _L("Input shaping test"), wxDefaultPosition, parent->FromDIP(wxSize(-1, 280)), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), m_plater(plater) +{ + wxBoxSizer* v_sizer = new wxBoxSizer(wxVERTICAL); + SetSizer(v_sizer); + + // Settings + // + wxString start_length_str = _L("Start: "); + wxString end_length_str = _L("End: "); + auto text_size = wxWindow::GetTextExtent(start_length_str); + text_size.IncTo(wxWindow::GetTextExtent(end_length_str)); + text_size.x = text_size.x * 1.5; + wxStaticBoxSizer* settings_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _L("Frequency settings")); + + auto st_size = FromDIP(wxSize(text_size.x, -1)); + auto ti_size = FromDIP(wxSize(90, -1)); + + auto start_length_sizer = new wxBoxSizer(wxHORIZONTAL); + auto start_length_text = new wxStaticText(this, wxID_ANY, start_length_str, wxDefaultPosition, st_size, wxALIGN_LEFT); + m_tiStart = new TextInput(this, std::to_string(15), _L("HZ"), "", wxDefaultPosition, ti_size, wxTE_CENTRE); + m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + auto end_length_text = new wxStaticText(this, wxID_ANY, end_length_str, wxDefaultPosition, st_size, wxALIGN_LEFT); + m_tiEnd = new TextInput(this, std::to_string(45), _L("HZ"), "", wxDefaultPosition, ti_size, wxTE_CENTRE); + m_tiEnd->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + + start_length_sizer->Add(start_length_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + start_length_sizer->Add(m_tiStart, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + start_length_sizer->Add(end_length_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + start_length_sizer->Add(m_tiEnd, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + settings_sizer->Add(start_length_sizer); + + // Damping Factor + wxString damping_factor_str = _L("Damp: "); + auto damping_factor_sizer = new wxBoxSizer(wxHORIZONTAL); + auto damping_factor_text = new wxStaticText(this, wxID_ANY, damping_factor_str, wxDefaultPosition, st_size, wxALIGN_LEFT); + m_tiDampingFactor = new TextInput(this, wxString::Format("%.2f", 0.15), "", "", wxDefaultPosition, ti_size, wxTE_CENTRE); + m_tiDampingFactor->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + + damping_factor_sizer->Add(damping_factor_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + damping_factor_sizer->Add(m_tiDampingFactor, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + settings_sizer->Add(damping_factor_sizer); + + // Add a note explaining that 0 means use default value + auto note_text = new wxStaticText(this, wxID_ANY, _L("Note: 0 Damp = Printer default"), + wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + note_text->SetForegroundColour(wxColour(128, 128, 128)); + settings_sizer->Add(note_text, 0, wxALL, 5); + + v_sizer->Add(settings_sizer); + v_sizer->Add(0, FromDIP(10), 0, wxEXPAND, 5); + m_btnStart = new Button(this, _L("OK")); + StateColor btn_bg_green(std::pair(wxColour(0, 137, 123), StateColor::Pressed), + std::pair(wxColour(38, 166, 154), StateColor::Hovered), + std::pair(wxColour(0, 150, 136), StateColor::Normal)); + + m_btnStart->SetBackgroundColor(btn_bg_green); + m_btnStart->SetBorderColor(wxColour(0, 150, 136)); + m_btnStart->SetTextColor(wxColour("#FFFFFE")); + m_btnStart->SetSize(wxSize(FromDIP(48), FromDIP(24))); + m_btnStart->SetMinSize(wxSize(FromDIP(48), FromDIP(24))); + m_btnStart->SetCornerRadius(FromDIP(3)); + m_btnStart->Bind(wxEVT_BUTTON, &Input_Shaping_Test_Dlg::on_start, this); + v_sizer->Add(m_btnStart, 0, wxALL | wxALIGN_RIGHT, FromDIP(5)); + + m_btnStart->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(Input_Shaping_Test_Dlg::on_start), NULL, this); + + //wxGetApp().UpdateDlgDarkUI(this);//FIXME: dark mode background color + + Layout(); + Fit(); +} + +Input_Shaping_Test_Dlg::~Input_Shaping_Test_Dlg() { + // Disconnect Events + m_btnStart->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(Input_Shaping_Test_Dlg::on_start), NULL, this); +} + +void Input_Shaping_Test_Dlg::on_start(wxCommandEvent& event) { + bool read_double = false; + read_double = m_tiStart->GetTextCtrl()->GetValue().ToDouble(&m_params.start); + read_double = read_double && m_tiEnd->GetTextCtrl()->GetValue().ToDouble(&m_params.end); + + double dampingFactor = 0.0; + bool read_damping = m_tiDampingFactor->GetTextCtrl()->GetValue().ToDouble(&dampingFactor); + + if (!read_double || m_params.start < 0 || m_params.end > 500|| m_params.end < m_params.start) { + MessageDialog msg_dlg(nullptr, _L("Please input valid values:\nStart >= 0\nEnd <= 500\nStart < End"), wxEmptyString, wxICON_WARNING | wxOK); + msg_dlg.ShowModal(); + return; + } + + if (!read_damping || dampingFactor < 0 || dampingFactor >= 1) { + MessageDialog msg_dlg(nullptr, _L("Please input a valid damping factor (0 < Damping/zeta factor <= 1)"), wxEmptyString, wxICON_WARNING | wxOK); + msg_dlg.ShowModal(); + return; + } + + m_params.step = dampingFactor; + m_params.mode = CalibMode::Calib_Input_shaping; + m_plater->calib_input_shaping(m_params); + EndModal(wxID_OK); +} + +void Input_Shaping_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect) { + this->Refresh(); + Fit(); + +} }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/calib_dlg.hpp b/src/slic3r/GUI/calib_dlg.hpp index bbd0903586..71720facc2 100644 --- a/src/slic3r/GUI/calib_dlg.hpp +++ b/src/slic3r/GUI/calib_dlg.hpp @@ -125,6 +125,24 @@ protected: Plater* m_plater; }; -}} // namespace Slic3r::GUI +class Input_Shaping_Test_Dlg : public DPIDialog +{ +public: + Input_Shaping_Test_Dlg (wxWindow* parent, wxWindowID id, Plater* plater); + ~Input_Shaping_Test_Dlg (); + void on_dpi_changed(const wxRect& suggested_rect) override; + +protected: + virtual void on_start(wxCommandEvent& event); + Calib_Params m_params; + + TextInput* m_tiStart; + TextInput* m_tiEnd; + TextInput* m_tiDampingFactor; + Button* m_btnStart; + Plater* m_plater; +}; + +}} // namespace Slic3r::GUI #endif diff --git a/src/slic3r/Utils/CalibUtils.cpp b/src/slic3r/Utils/CalibUtils.cpp index 0732b99370..af55d71dd6 100644 --- a/src/slic3r/Utils/CalibUtils.cpp +++ b/src/slic3r/Utils/CalibUtils.cpp @@ -57,6 +57,8 @@ std::string get_calib_mode_name(CalibMode cali_mode, int stage) return "vfa_tower_calib_mode"; case CalibMode::Calib_Retraction_tower: return "retration_tower_calib_mode"; + case CalibMode::Calib_Input_shaping: + return "input_shaping_calib_mode"; default: assert(false); return ""; @@ -196,6 +198,8 @@ CalibMode CalibUtils::get_calib_mode_by_name(const std::string name, int& cali_s return CalibMode::Calib_VFA_Tower; else if (name == "retration_tower_calib_mode") return CalibMode::Calib_Retraction_tower; + else if (name == "input_shaping_calib_mode") + return CalibMode::Calib_Input_shaping; return CalibMode::Calib_None; } From 1857849cba9ee7ee9be0fab9bf155d64aede4b4e Mon Sep 17 00:00:00 2001 From: Ian Bassi Date: Sun, 23 Mar 2025 00:10:13 -0300 Subject: [PATCH 02/10] Input Shaping Damping Damping Fix spanish comments --- src/libslic3r/GCode.cpp | 11 +- src/libslic3r/GCodeWriter.cpp | 18 ++- src/libslic3r/GCodeWriter.hpp | 2 +- src/libslic3r/calib.hpp | 7 +- src/slic3r/GUI/CalibrationWizardSavePage.cpp | 4 +- src/slic3r/GUI/MainFrame.cpp | 50 +++++++- src/slic3r/GUI/MainFrame.hpp | 3 +- src/slic3r/GUI/Plater.cpp | 39 +++++- src/slic3r/GUI/Plater.hpp | 3 +- src/slic3r/GUI/calib_dlg.cpp | 118 +++++++++++++++++-- src/slic3r/GUI/calib_dlg.hpp | 24 +++- src/slic3r/Utils/CalibUtils.cpp | 12 +- 12 files changed, 249 insertions(+), 42 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index be180e3cb5..d480a7445d 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3768,8 +3768,15 @@ LayerResult GCode::process_layer( writer().config.apply(_cfg); sprintf(buf, "; Calib_Retraction_tower: Z_HEIGHT: %g, length:%g\n", print_z, _length); gcode += buf; - } else if (print.calib_mode() == CalibMode::Calib_Input_shaping) { - gcode += writer().set_input_shaping(m_layer_index < 2 ? 0.f : (print.calib_params().start) + ((print.calib_params().end)-(print.calib_params().start)) * (m_layer_index - 2) / (m_layer_count - 3), (print.calib_params().step)); + } else if (print.calib_mode() == CalibMode::Calib_Input_shaping_freq) { + gcode += writer().set_input_shaping('A', print.calib_params().step, m_layer_index < 2 ? 0.f : (print.calib_params().start) + ((print.calib_params().end)-(print.calib_params().start)) * (m_layer_index - 2) / (m_layer_count - 3)); + } else if (print.calib_mode() == CalibMode::Calib_Input_shaping_damp) { + if (m_layer_index == 1){ + gcode += writer().set_input_shaping('X', 0.f, print.calib_params().start); + gcode += writer().set_input_shaping('Y', 0.f, print.calib_params().end); + } else { + gcode += writer().set_input_shaping('A', float(m_layer_index) / float(m_layer_count), 0.f); + } } //BBS diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index acbff7a667..f1012e5a7e 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -333,21 +333,29 @@ std::string GCodeWriter::set_pressure_advance(double pa) const return gcode.str(); } -std::string GCodeWriter::set_input_shaping(float freq, float damp) const +std::string GCodeWriter::set_input_shaping(char axis, float damp, float freq) const { std::ostringstream gcode; if (FLAVOR_IS(gcfKlipper)) { throw std::runtime_error("M593 - ZV Input Shaping is NOT supported by Klipper"); } - if (freq < 0.0f || damp < 0.f || damp > 1.0f) + 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)); } - gcode << "M593 F" << freq; - if (damp != 0.0f) + gcode << "M593"; + if (axis != 'A') { - gcode << " D" << damp; + gcode << " " << axis; + } + if (freq > 0.0f) + { + gcode << " F" << std::fixed << std::setprecision(2) << freq; + } + if (damp > 0.0f) + { + gcode << " D" << std::fixed << std::setprecision(2) << damp; } gcode << "; Override input shaping value\n"; return gcode.str(); diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 69e20515a4..2b2887a152 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -54,7 +54,7 @@ public: // Orca: set acceleration and jerk in one command for Klipper std::string set_accel_and_jerk(unsigned int acceleration, double jerk); std::string set_pressure_advance(double pa) const; - std::string set_input_shaping(float freq, float damp) const; + std::string set_input_shaping(char axis, float damp, float freq) const; std::string reset_e(bool force = false); std::string update_progress(unsigned int num, unsigned int tot, bool allow_100 = false) const; // return false if this extruder was already selected diff --git a/src/libslic3r/calib.hpp b/src/libslic3r/calib.hpp index b8c81db86c..7cdad011d7 100644 --- a/src/libslic3r/calib.hpp +++ b/src/libslic3r/calib.hpp @@ -22,7 +22,8 @@ enum class CalibMode : int { Calib_Vol_speed_Tower, Calib_VFA_Tower, Calib_Retraction_tower, - Calib_Input_shaping + Calib_Input_shaping_freq, + Calib_Input_shaping_damp }; enum class CalibState { Start = 0, Preset, Calibration, CoarseSave, FineCalibration, Save, Finish }; @@ -31,13 +32,13 @@ struct Calib_Params { Calib_Params() : mode(CalibMode::Calib_None){}; double start, end, step; - bool print_numbers; + bool print_numbers; std::vector accelerations; std::vector speeds; CalibMode mode; - }; +}; enum FlowRatioCalibrationType { COMPLETE_CALIBRATION = 0, diff --git a/src/slic3r/GUI/CalibrationWizardSavePage.cpp b/src/slic3r/GUI/CalibrationWizardSavePage.cpp index 7131e0a391..7499acd27a 100644 --- a/src/slic3r/GUI/CalibrationWizardSavePage.cpp +++ b/src/slic3r/GUI/CalibrationWizardSavePage.cpp @@ -44,7 +44,9 @@ static wxString get_default_name(wxString filament_name, CalibMode mode){ break; case Slic3r::CalibMode::Calib_Retraction_tower: break; - case Slic3r::CalibMode::Calib_Input_shaping: + case Slic3r::CalibMode::Calib_Input_shaping_freq: + break; + case Slic3r::CalibMode::Calib_Input_shaping_damp: break; default: break; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 2767a2a576..776574deff 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -2975,16 +2975,31 @@ void MainFrame::init_menubar_as_editor() "", nullptr, [this]() {return m_plater->is_view3D_shown();; }, this); + // Input Shaping calibrations + auto input_shaping_menu = new wxMenu(); + append_menu_item( - advance_menu, wxID_ANY, _L("Input Shaping"), _L("Input Shaping"), + input_shaping_menu, wxID_ANY, _L("Input Shaping Frequency"), _L("Input Shaping Frequency"), [this](wxCommandEvent&) { - std::string url = "https://marlinfw.org/docs/gcode/M593.html";//TODO: Make OrcaSlicer wiki page - if (!m_IS_calib_dlg) - m_IS_calib_dlg = new Input_Shaping_Test_Dlg((wxWindow*)this, wxID_ANY, m_plater); - m_IS_calib_dlg->ShowModal(); + if (!m_IS_freq_calib_dlg) + m_IS_freq_calib_dlg = new Input_Shaping_Freq_Test_Dlg((wxWindow*)this, wxID_ANY, m_plater); + m_IS_freq_calib_dlg->ShowModal(); }, "", nullptr, [this]() {return m_plater->is_view3D_shown();; }, this); + + append_menu_item( + input_shaping_menu, wxID_ANY, _L("Input Shaping Damping/zeta factor"), _L("Input Shaping Damping/zeta factor"), + [this](wxCommandEvent&) { + if (!m_IS_damp_calib_dlg) + m_IS_damp_calib_dlg = new Input_Shaping_Damp_Test_Dlg((wxWindow*)this, wxID_ANY, m_plater); + m_IS_damp_calib_dlg->ShowModal(); + }, + "", nullptr, + [this]() {return m_plater->is_view3D_shown();; }, this); + + m_topbar->GetCalibMenu()->AppendSubMenu(input_shaping_menu, _L("Input Shaping")); + m_topbar->GetCalibMenu()->AppendSubMenu(advance_menu, _L("More...")); // help @@ -3083,6 +3098,31 @@ void MainFrame::init_menubar_as_editor() }, "", nullptr, [this]() {return m_plater->is_view3D_shown();; }, this); + // Input Shaping calibrations + auto input_shaping_menu = new wxMenu(); + + append_menu_item( + input_shaping_menu, wxID_ANY, _L("Input Shaping Frequency"), _L("Input Shaping Frequency"), + [this](wxCommandEvent&) { + if (!m_IS_freq_calib_dlg) + m_IS_freq_calib_dlg = new Input_Shaping_Freq_Test_Dlg((wxWindow*)this, wxID_ANY, m_plater); + m_IS_freq_calib_dlg->ShowModal(); + }, + "", nullptr, + [this]() {return m_plater->is_view3D_shown();; }, this); + + append_menu_item( + input_shaping_menu, wxID_ANY, _L("Input Shaping Damping/zeta factor"), _L("Input Shaping Damping/zeta factor"), + [this](wxCommandEvent&) { + if (!m_IS_damp_calib_dlg) + m_IS_damp_calib_dlg = new Input_Shaping_Damp_Test_Dlg((wxWindow*)this, wxID_ANY, m_plater); + m_IS_damp_calib_dlg->ShowModal(); + }, + "", nullptr, + [this]() {return m_plater->is_view3D_shown();; }, this); + + calib_menu->AppendSubMenu(input_shaping_menu, _L("Input Shaping")); + append_submenu(calib_menu, advance_menu, wxID_ANY, _L("More..."), _L("More calibrations"), "", [this]() {return m_plater->is_view3D_shown();; }); // help diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 311a12c855..1fffbcd336 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -354,7 +354,8 @@ public: MaxVolumetricSpeed_Test_Dlg* m_vol_test_dlg { nullptr }; VFA_Test_Dlg* m_vfa_test_dlg { nullptr }; Retraction_Test_Dlg* m_retraction_calib_dlg{ nullptr }; - Input_Shaping_Test_Dlg* m_IS_calib_dlg{ nullptr }; + Input_Shaping_Freq_Test_Dlg* m_IS_freq_calib_dlg{ nullptr }; + Input_Shaping_Damp_Test_Dlg* m_IS_damp_calib_dlg{ nullptr }; // BBS. Replace title bar and menu bar with top bar. BBLTopbar* m_topbar{ nullptr }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a259a3a6ca..8ae6998305 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -10150,12 +10150,45 @@ void Plater::calib_VFA(const Calib_Params& params) p->background_process.fff_print()->set_calib_params(params); } -void Plater::calib_input_shaping(const Calib_Params& params) +void Plater::calib_input_shaping_freq(const Calib_Params& params) { - const auto calib_input_shaping_name = wxString::Format(L"Input shaping test"); + const auto calib_input_shaping_name = wxString::Format(L"Input shaping Frequency test"); new_project(false, false, calib_input_shaping_name); wxGetApp().mainframe->select_tab(size_t(MainFrame::tp3DEditor)); - if (params.mode != CalibMode::Calib_Input_shaping) + if (params.mode != CalibMode::Calib_Input_shaping_freq) + return; + + add_model(false, Slic3r::resources_dir() + "/calib/input_shaping/ringing_tower.stl"); + auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config; + auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config; + filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 2.0 }); + filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats { 200 }); + 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)); + print_config->set_key_value("bottom_shell_layers", new ConfigOptionInt(1)); + print_config->set_key_value("sparse_infill_density", new ConfigOptionPercent(0)); + print_config->set_key_value("spiral_mode", new ConfigOptionBool(true)); + model().objects[0]->config.set_key_value("brim_type", new ConfigOptionEnum(btOuterOnly)); + model().objects[0]->config.set_key_value("brim_width", new ConfigOptionFloat(3.0)); + model().objects[0]->config.set_key_value("brim_object_gap", new ConfigOptionFloat(0.0)); + + changed_objects({ 0 }); + wxGetApp().get_tab(Preset::TYPE_PRINT)->update_dirty(); + wxGetApp().get_tab(Preset::TYPE_FILAMENT)->update_dirty(); + wxGetApp().get_tab(Preset::TYPE_PRINT)->update_ui_from_settings(); + wxGetApp().get_tab(Preset::TYPE_FILAMENT)->update_ui_from_settings(); + + p->background_process.fff_print()->set_calib_params(params); +} + +void Plater::calib_input_shaping_damp(const Calib_Params& params) +{ + const auto calib_input_shaping_name = wxString::Format(L"Input shaping Damping test"); + new_project(false, false, calib_input_shaping_name); + wxGetApp().mainframe->select_tab(size_t(MainFrame::tp3DEditor)); + if (params.mode != CalibMode::Calib_Input_shaping_damp) return; add_model(false, Slic3r::resources_dir() + "/calib/input_shaping/ringing_tower.stl"); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index b8f6e93333..eff592d455 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -273,7 +273,8 @@ public: void calib_max_vol_speed(const Calib_Params& params); void calib_retraction(const Calib_Params& params); void calib_VFA(const Calib_Params& params); - void calib_input_shaping(const Calib_Params& params); + void calib_input_shaping_freq(const Calib_Params& params); + void calib_input_shaping_damp(const Calib_Params& params); BuildVolume_Type get_build_volume_type() const; diff --git a/src/slic3r/GUI/calib_dlg.cpp b/src/slic3r/GUI/calib_dlg.cpp index f4d282ed03..1cb6564f57 100644 --- a/src/slic3r/GUI/calib_dlg.cpp +++ b/src/slic3r/GUI/calib_dlg.cpp @@ -722,7 +722,7 @@ Retraction_Test_Dlg::Retraction_Test_Dlg(wxWindow* parent, wxWindowID id, Plater auto end_length_sizer = new wxBoxSizer(wxHORIZONTAL); auto end_length_text = new wxStaticText(this, wxID_ANY, end_length_str, wxDefaultPosition, st_size, wxALIGN_LEFT); m_tiEnd = new TextInput(this, std::to_string(2), _L("mm"), "", wxDefaultPosition, ti_size, wxTE_CENTRE); - m_tiEnd->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));//m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));//TODO: IANALEXIS CHECK + m_tiEnd->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));//m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); end_length_sizer->Add(end_length_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); end_length_sizer->Add(m_tiEnd, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); settings_sizer->Add(end_length_sizer); @@ -789,11 +789,11 @@ void Retraction_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect) { } -// Input_Shaping_Test_Dlg +// Input_Shaping_Freq_Test_Dlg // -Input_Shaping_Test_Dlg::Input_Shaping_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater) - : DPIDialog(parent, id, _L("Input shaping test"), wxDefaultPosition, parent->FromDIP(wxSize(-1, 280)), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), m_plater(plater) +Input_Shaping_Freq_Test_Dlg::Input_Shaping_Freq_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater) + : DPIDialog(parent, id, _L("Input shaping Frequency test"), wxDefaultPosition, parent->FromDIP(wxSize(-1, 280)), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), m_plater(plater) { wxBoxSizer* v_sizer = new wxBoxSizer(wxVERTICAL); SetSizer(v_sizer); @@ -854,10 +854,10 @@ Input_Shaping_Test_Dlg::Input_Shaping_Test_Dlg(wxWindow* parent, wxWindowID id, m_btnStart->SetSize(wxSize(FromDIP(48), FromDIP(24))); m_btnStart->SetMinSize(wxSize(FromDIP(48), FromDIP(24))); m_btnStart->SetCornerRadius(FromDIP(3)); - m_btnStart->Bind(wxEVT_BUTTON, &Input_Shaping_Test_Dlg::on_start, this); + m_btnStart->Bind(wxEVT_BUTTON, &Input_Shaping_Freq_Test_Dlg::on_start, this); v_sizer->Add(m_btnStart, 0, wxALL | wxALIGN_RIGHT, FromDIP(5)); - m_btnStart->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(Input_Shaping_Test_Dlg::on_start), NULL, this); + m_btnStart->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(Input_Shaping_Freq_Test_Dlg::on_start), NULL, this); //wxGetApp().UpdateDlgDarkUI(this);//FIXME: dark mode background color @@ -865,12 +865,12 @@ Input_Shaping_Test_Dlg::Input_Shaping_Test_Dlg(wxWindow* parent, wxWindowID id, Fit(); } -Input_Shaping_Test_Dlg::~Input_Shaping_Test_Dlg() { +Input_Shaping_Freq_Test_Dlg::~Input_Shaping_Freq_Test_Dlg() { // Disconnect Events - m_btnStart->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(Input_Shaping_Test_Dlg::on_start), NULL, this); + m_btnStart->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(Input_Shaping_Freq_Test_Dlg::on_start), NULL, this); } -void Input_Shaping_Test_Dlg::on_start(wxCommandEvent& event) { +void Input_Shaping_Freq_Test_Dlg::on_start(wxCommandEvent& event) { bool read_double = false; read_double = m_tiStart->GetTextCtrl()->GetValue().ToDouble(&m_params.start); read_double = read_double && m_tiEnd->GetTextCtrl()->GetValue().ToDouble(&m_params.end); @@ -879,7 +879,7 @@ void Input_Shaping_Test_Dlg::on_start(wxCommandEvent& event) { bool read_damping = m_tiDampingFactor->GetTextCtrl()->GetValue().ToDouble(&dampingFactor); if (!read_double || m_params.start < 0 || m_params.end > 500|| m_params.end < m_params.start) { - MessageDialog msg_dlg(nullptr, _L("Please input valid values:\nStart >= 0\nEnd <= 500\nStart < End"), wxEmptyString, wxICON_WARNING | wxOK); + MessageDialog msg_dlg(nullptr, _L("Please input valid values:\nStart > 0\nEnd < 500\nStart < End"), wxEmptyString, wxICON_WARNING | wxOK); msg_dlg.ShowModal(); return; } @@ -891,12 +891,104 @@ void Input_Shaping_Test_Dlg::on_start(wxCommandEvent& event) { } m_params.step = dampingFactor; - m_params.mode = CalibMode::Calib_Input_shaping; - m_plater->calib_input_shaping(m_params); + m_params.mode = CalibMode::Calib_Input_shaping_freq; + m_plater->calib_input_shaping_freq(m_params); EndModal(wxID_OK); } -void Input_Shaping_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect) { +void Input_Shaping_Freq_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect) { + this->Refresh(); + Fit(); + +} + +// Input_Shaping_Damp_Test_Dlg +// + +Input_Shaping_Damp_Test_Dlg::Input_Shaping_Damp_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater) + : DPIDialog(parent, id, _L("Input shaping Damp test"), wxDefaultPosition, parent->FromDIP(wxSize(-1, 280)), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), m_plater(plater) +{ + wxBoxSizer* v_sizer = new wxBoxSizer(wxVERTICAL); + SetSizer(v_sizer); + + // Settings + // + wxString start_length_str = _L("X: "); + wxString end_length_str = _L("Y: "); + auto text_size = wxWindow::GetTextExtent(start_length_str); + text_size.IncTo(wxWindow::GetTextExtent(end_length_str)); + text_size.x = text_size.x * 1.5; + wxStaticBoxSizer* settings_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _L("Frequency settings")); + + auto st_size = FromDIP(wxSize(text_size.x, -1)); + auto ti_size = FromDIP(wxSize(90, -1)); + + auto start_length_sizer = new wxBoxSizer(wxHORIZONTAL); + auto start_length_text = new wxStaticText(this, wxID_ANY, start_length_str, wxDefaultPosition, st_size, wxALIGN_LEFT); + m_tiStart = new TextInput(this, std::to_string(40), _L("HZ"), "", wxDefaultPosition, ti_size, wxTE_CENTRE); + m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + auto end_length_text = new wxStaticText(this, wxID_ANY, end_length_str, wxDefaultPosition, st_size, wxALIGN_LEFT); + m_tiEnd = new TextInput(this, std::to_string(40), _L("HZ"), "", wxDefaultPosition, ti_size, wxTE_CENTRE); + m_tiEnd->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + + start_length_sizer->Add(start_length_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + start_length_sizer->Add(m_tiStart, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + start_length_sizer->Add(end_length_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + start_length_sizer->Add(m_tiEnd, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + settings_sizer->Add(start_length_sizer); + + // Add a note to explain users to use their previously calculated frequency + auto note_text = new wxStaticText(this, wxID_ANY, _L("Note: Set frequencies to the previously calculated values"), + wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + note_text->SetForegroundColour(wxColour(128, 128, 128)); + settings_sizer->Add(note_text, 0, wxALL, 5); + + v_sizer->Add(settings_sizer); + v_sizer->Add(0, FromDIP(10), 0, wxEXPAND, 5); + m_btnStart = new Button(this, _L("OK")); + StateColor btn_bg_green(std::pair(wxColour(0, 137, 123), StateColor::Pressed), + std::pair(wxColour(38, 166, 154), StateColor::Hovered), + std::pair(wxColour(0, 150, 136), StateColor::Normal)); + + m_btnStart->SetBackgroundColor(btn_bg_green); + m_btnStart->SetBorderColor(wxColour(0, 150, 136)); + m_btnStart->SetTextColor(wxColour("#FFFFFE")); + m_btnStart->SetSize(wxSize(FromDIP(48), FromDIP(24))); + m_btnStart->SetMinSize(wxSize(FromDIP(48), FromDIP(24))); + m_btnStart->SetCornerRadius(FromDIP(3)); + m_btnStart->Bind(wxEVT_BUTTON, &Input_Shaping_Damp_Test_Dlg::on_start, this); + v_sizer->Add(m_btnStart, 0, wxALL | wxALIGN_RIGHT, FromDIP(5)); + + m_btnStart->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(Input_Shaping_Damp_Test_Dlg::on_start), NULL, this); + + //wxGetApp().UpdateDlgDarkUI(this);//FIXME: dark mode background color + + Layout(); + Fit(); +} + +Input_Shaping_Damp_Test_Dlg::~Input_Shaping_Damp_Test_Dlg() { + // Disconnect Events + m_btnStart->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(Input_Shaping_Damp_Test_Dlg::on_start), NULL, this); +} + +void Input_Shaping_Damp_Test_Dlg::on_start(wxCommandEvent& event) { + bool read_double = false; + read_double = m_tiStart->GetTextCtrl()->GetValue().ToDouble(&m_params.start); + read_double = read_double && m_tiEnd->GetTextCtrl()->GetValue().ToDouble(&m_params.end); + + if (!read_double || m_params.start < 0 || m_params.start > 500 || m_params.end < 0 || m_params.end > 500) { + MessageDialog msg_dlg(nullptr, _L("Please input valid values\n(0 < Freq < 500"), wxEmptyString, wxICON_WARNING | wxOK); + msg_dlg.ShowModal(); + return; + } + + m_params.mode = CalibMode::Calib_Input_shaping_damp; + m_plater->calib_input_shaping_damp(m_params); + EndModal(wxID_OK); +} + +void Input_Shaping_Damp_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect) { this->Refresh(); Fit(); diff --git a/src/slic3r/GUI/calib_dlg.hpp b/src/slic3r/GUI/calib_dlg.hpp index 71720facc2..cf9ce0b022 100644 --- a/src/slic3r/GUI/calib_dlg.hpp +++ b/src/slic3r/GUI/calib_dlg.hpp @@ -125,11 +125,11 @@ protected: Plater* m_plater; }; -class Input_Shaping_Test_Dlg : public DPIDialog +class Input_Shaping_Freq_Test_Dlg : public DPIDialog { public: - Input_Shaping_Test_Dlg (wxWindow* parent, wxWindowID id, Plater* plater); - ~Input_Shaping_Test_Dlg (); + Input_Shaping_Freq_Test_Dlg (wxWindow* parent, wxWindowID id, Plater* plater); + ~Input_Shaping_Freq_Test_Dlg (); void on_dpi_changed(const wxRect& suggested_rect) override; protected: @@ -144,5 +144,23 @@ protected: Plater* m_plater; }; +class Input_Shaping_Damp_Test_Dlg : public DPIDialog +{ +public: + Input_Shaping_Damp_Test_Dlg (wxWindow* parent, wxWindowID id, Plater* plater); + ~Input_Shaping_Damp_Test_Dlg (); + void on_dpi_changed(const wxRect& suggested_rect) override; + +protected: + + virtual void on_start(wxCommandEvent& event); + Calib_Params m_params; + + TextInput* m_tiStart; + TextInput* m_tiEnd; + Button* m_btnStart; + Plater* m_plater; +}; + }} // namespace Slic3r::GUI #endif diff --git a/src/slic3r/Utils/CalibUtils.cpp b/src/slic3r/Utils/CalibUtils.cpp index af55d71dd6..71fcdd4ed5 100644 --- a/src/slic3r/Utils/CalibUtils.cpp +++ b/src/slic3r/Utils/CalibUtils.cpp @@ -57,8 +57,10 @@ std::string get_calib_mode_name(CalibMode cali_mode, int stage) return "vfa_tower_calib_mode"; case CalibMode::Calib_Retraction_tower: return "retration_tower_calib_mode"; - case CalibMode::Calib_Input_shaping: - return "input_shaping_calib_mode"; + case CalibMode::Calib_Input_shaping_freq: + return "input_shaping_freq_calib_mode"; + case CalibMode::Calib_Input_shaping_damp: + return "input_shaping_damp_calib_mode"; default: assert(false); return ""; @@ -198,8 +200,10 @@ CalibMode CalibUtils::get_calib_mode_by_name(const std::string name, int& cali_s return CalibMode::Calib_VFA_Tower; else if (name == "retration_tower_calib_mode") return CalibMode::Calib_Retraction_tower; - else if (name == "input_shaping_calib_mode") - return CalibMode::Calib_Input_shaping; + else if (name == "input_shaping_freq_calib_mode") + return CalibMode::Calib_Input_shaping_freq; + else if (name == "input_shaping_damp_calib_mode") + return CalibMode::Calib_Input_shaping_damp; return CalibMode::Calib_None; } From 3662b5139c9ae43a3084bcc46a57002eceaf8ed3 Mon Sep 17 00:00:00 2001 From: Ian Bassi Date: Wed, 26 Mar 2025 17:54:07 -0300 Subject: [PATCH 03/10] FastTower and Granular Parameters Co-Authored-By: Rodrigo <162915171+rf47@users.noreply.github.com> --- .../input_shaping/fast_input_shaping_test.stl | Bin 0 -> 67284 bytes src/libslic3r/GCode.cpp | 17 +- src/libslic3r/calib.hpp | 5 +- src/slic3r/GUI/Plater.cpp | 8 +- src/slic3r/GUI/calib_dlg.cpp | 170 +++++++++++++----- src/slic3r/GUI/calib_dlg.hpp | 14 +- 6 files changed, 154 insertions(+), 60 deletions(-) create mode 100644 resources/calib/input_shaping/fast_input_shaping_test.stl diff --git a/resources/calib/input_shaping/fast_input_shaping_test.stl b/resources/calib/input_shaping/fast_input_shaping_test.stl new file mode 100644 index 0000000000000000000000000000000000000000..e2fd5cd370c4404744b0faecef41f07902cafed9 GIT binary patch literal 67284 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 accelerations; std::vector speeds; @@ -338,4 +339,4 @@ private: const double m_glyph_padding_vertical{1}; }; -} // namespace Slic3 \ No newline at end of file +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 8ae6998305..d01bff010b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -10158,7 +10158,7 @@ void Plater::calib_input_shaping_freq(const Calib_Params& params) if (params.mode != CalibMode::Calib_Input_shaping_freq) return; - add_model(false, Slic3r::resources_dir() + "/calib/input_shaping/ringing_tower.stl"); + add_model(false, Slic3r::resources_dir() + (params.test_model < 1 ? "/calib/input_shaping/ringing_tower.stl" : "/calib/input_shaping/fast_input_shaping_test.stl")); auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config; auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config; filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 2.0 }); @@ -10169,7 +10169,9 @@ void Plater::calib_input_shaping_freq(const Calib_Params& params) print_config->set_key_value("top_shell_layers", new ConfigOptionInt(0)); print_config->set_key_value("bottom_shell_layers", new ConfigOptionInt(1)); print_config->set_key_value("sparse_infill_density", new ConfigOptionPercent(0)); + print_config->set_key_value("detect_thin_wall", new ConfigOptionBool(false)); print_config->set_key_value("spiral_mode", new ConfigOptionBool(true)); + print_config->set_key_value("spiral_mode_smooth", new ConfigOptionBool(true)); model().objects[0]->config.set_key_value("brim_type", new ConfigOptionEnum(btOuterOnly)); model().objects[0]->config.set_key_value("brim_width", new ConfigOptionFloat(3.0)); model().objects[0]->config.set_key_value("brim_object_gap", new ConfigOptionFloat(0.0)); @@ -10191,7 +10193,7 @@ void Plater::calib_input_shaping_damp(const Calib_Params& params) if (params.mode != CalibMode::Calib_Input_shaping_damp) return; - add_model(false, Slic3r::resources_dir() + "/calib/input_shaping/ringing_tower.stl"); + add_model(false, Slic3r::resources_dir() + (params.test_model < 1 ? "/calib/input_shaping/ringing_tower.stl" : "/calib/input_shaping/fast_input_shaping_test.stl")); auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config; auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config; filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 2.0 }); @@ -10202,7 +10204,9 @@ void Plater::calib_input_shaping_damp(const Calib_Params& params) print_config->set_key_value("top_shell_layers", new ConfigOptionInt(0)); print_config->set_key_value("bottom_shell_layers", new ConfigOptionInt(1)); print_config->set_key_value("sparse_infill_density", new ConfigOptionPercent(0)); + print_config->set_key_value("detect_thin_wall", new ConfigOptionBool(false)); print_config->set_key_value("spiral_mode", new ConfigOptionBool(true)); + print_config->set_key_value("spiral_mode_smooth", new ConfigOptionBool(true)); model().objects[0]->config.set_key_value("brim_type", new ConfigOptionEnum(btOuterOnly)); model().objects[0]->config.set_key_value("brim_width", new ConfigOptionFloat(3.0)); model().objects[0]->config.set_key_value("brim_object_gap", new ConfigOptionFloat(0.0)); diff --git a/src/slic3r/GUI/calib_dlg.cpp b/src/slic3r/GUI/calib_dlg.cpp index 1cb6564f57..d9d80fa5d2 100644 --- a/src/slic3r/GUI/calib_dlg.cpp +++ b/src/slic3r/GUI/calib_dlg.cpp @@ -722,7 +722,7 @@ Retraction_Test_Dlg::Retraction_Test_Dlg(wxWindow* parent, wxWindowID id, Plater auto end_length_sizer = new wxBoxSizer(wxHORIZONTAL); auto end_length_text = new wxStaticText(this, wxID_ANY, end_length_str, wxDefaultPosition, st_size, wxALIGN_LEFT); m_tiEnd = new TextInput(this, std::to_string(2), _L("mm"), "", wxDefaultPosition, ti_size, wxTE_CENTRE); - m_tiEnd->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));//m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); end_length_sizer->Add(end_length_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); end_length_sizer->Add(m_tiEnd, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); settings_sizer->Add(end_length_sizer); @@ -798,37 +798,64 @@ Input_Shaping_Freq_Test_Dlg::Input_Shaping_Freq_Test_Dlg(wxWindow* parent, wxWin wxBoxSizer* v_sizer = new wxBoxSizer(wxVERTICAL); SetSizer(v_sizer); + // Model selection + wxString m_rbModelChoices[] = { _L("Ringing Tower"), _L("Fast Tower") }; + int m_rbModelNChoices = sizeof(m_rbModelChoices) / sizeof(wxString); + m_rbModel = new wxRadioBox(this, wxID_ANY, _L("Test model"), wxDefaultPosition, wxDefaultSize, m_rbModelNChoices, m_rbModelChoices, 1, wxRA_SPECIFY_ROWS); + m_rbModel->SetSelection(0); + v_sizer->Add(m_rbModel, 0, wxALL | wxEXPAND, 5); + // Settings // - wxString start_length_str = _L("Start: "); - wxString end_length_str = _L("End: "); - auto text_size = wxWindow::GetTextExtent(start_length_str); - text_size.IncTo(wxWindow::GetTextExtent(end_length_str)); + wxString start_x_str = _L("Start X: "); + wxString end_x_str = _L("End X: "); + wxString start_y_str = _L("Start Y: "); + wxString end_y_str = _L("End Y: "); + auto text_size = wxWindow::GetTextExtent(start_x_str); + text_size.IncTo(wxWindow::GetTextExtent(end_x_str)); + text_size.IncTo(wxWindow::GetTextExtent(start_y_str)); + text_size.IncTo(wxWindow::GetTextExtent(end_y_str)); text_size.x = text_size.x * 1.5; wxStaticBoxSizer* settings_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _L("Frequency settings")); auto st_size = FromDIP(wxSize(text_size.x, -1)); auto ti_size = FromDIP(wxSize(90, -1)); - auto start_length_sizer = new wxBoxSizer(wxHORIZONTAL); - auto start_length_text = new wxStaticText(this, wxID_ANY, start_length_str, wxDefaultPosition, st_size, wxALIGN_LEFT); - m_tiStart = new TextInput(this, std::to_string(15), _L("HZ"), "", wxDefaultPosition, ti_size, wxTE_CENTRE); - m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); - auto end_length_text = new wxStaticText(this, wxID_ANY, end_length_str, wxDefaultPosition, st_size, wxALIGN_LEFT); - m_tiEnd = new TextInput(this, std::to_string(45), _L("HZ"), "", wxDefaultPosition, ti_size, wxTE_CENTRE); - m_tiEnd->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + // X axis frequencies + auto x_freq_sizer = new wxBoxSizer(wxHORIZONTAL); + auto start_x_text = new wxStaticText(this, wxID_ANY, start_x_str, wxDefaultPosition, st_size, wxALIGN_LEFT); + m_tiFreqStartX = new TextInput(this, std::to_string(15), _L("HZ"), "", wxDefaultPosition, ti_size, wxTE_CENTRE); + m_tiFreqStartX->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + auto end_x_text = new wxStaticText(this, wxID_ANY, end_x_str, wxDefaultPosition, st_size, wxALIGN_LEFT); + m_tiFreqEndX = new TextInput(this, std::to_string(60), _L("HZ"), "", wxDefaultPosition, ti_size, wxTE_CENTRE); + m_tiFreqEndX->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); - start_length_sizer->Add(start_length_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); - start_length_sizer->Add(m_tiStart, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); - start_length_sizer->Add(end_length_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); - start_length_sizer->Add(m_tiEnd, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); - settings_sizer->Add(start_length_sizer); + x_freq_sizer->Add(start_x_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + x_freq_sizer->Add(m_tiFreqStartX, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + x_freq_sizer->Add(end_x_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + x_freq_sizer->Add(m_tiFreqEndX, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + settings_sizer->Add(x_freq_sizer); + + // Y axis frequencies + auto y_freq_sizer = new wxBoxSizer(wxHORIZONTAL); + auto start_y_text = new wxStaticText(this, wxID_ANY, start_y_str, wxDefaultPosition, st_size, wxALIGN_LEFT); + m_tiFreqStartY = new TextInput(this, std::to_string(15), _L("HZ"), "", wxDefaultPosition, ti_size, wxTE_CENTRE); + m_tiFreqStartY->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + auto end_y_text = new wxStaticText(this, wxID_ANY, end_y_str, wxDefaultPosition, st_size, wxALIGN_LEFT); + m_tiFreqEndY = new TextInput(this, std::to_string(60), _L("HZ"), "", wxDefaultPosition, ti_size, wxTE_CENTRE); + m_tiFreqEndY->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + + y_freq_sizer->Add(start_y_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + y_freq_sizer->Add(m_tiFreqStartY, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + y_freq_sizer->Add(end_y_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + y_freq_sizer->Add(m_tiFreqEndY, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + settings_sizer->Add(y_freq_sizer); // Damping Factor wxString damping_factor_str = _L("Damp: "); auto damping_factor_sizer = new wxBoxSizer(wxHORIZONTAL); auto damping_factor_text = new wxStaticText(this, wxID_ANY, damping_factor_str, wxDefaultPosition, st_size, wxALIGN_LEFT); - m_tiDampingFactor = new TextInput(this, wxString::Format("%.2f", 0.15), "", "", wxDefaultPosition, ti_size, wxTE_CENTRE); + m_tiDampingFactor = new TextInput(this, wxString::Format("%.2f", 0.35), "", "", wxDefaultPosition, ti_size, wxTE_CENTRE); m_tiDampingFactor->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); damping_factor_sizer->Add(damping_factor_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); @@ -872,26 +899,33 @@ Input_Shaping_Freq_Test_Dlg::~Input_Shaping_Freq_Test_Dlg() { void Input_Shaping_Freq_Test_Dlg::on_start(wxCommandEvent& event) { bool read_double = false; - read_double = m_tiStart->GetTextCtrl()->GetValue().ToDouble(&m_params.start); - read_double = read_double && m_tiEnd->GetTextCtrl()->GetValue().ToDouble(&m_params.end); + read_double = m_tiFreqStartX->GetTextCtrl()->GetValue().ToDouble(&m_params.freqStartX); + read_double = read_double && m_tiFreqEndX->GetTextCtrl()->GetValue().ToDouble(&m_params.freqEndX); + read_double = read_double && m_tiFreqStartY->GetTextCtrl()->GetValue().ToDouble(&m_params.freqStartY); + read_double = read_double && m_tiFreqEndY->GetTextCtrl()->GetValue().ToDouble(&m_params.freqEndY); + read_double = read_double && m_tiDampingFactor->GetTextCtrl()->GetValue().ToDouble(&m_params.start); - double dampingFactor = 0.0; - bool read_damping = m_tiDampingFactor->GetTextCtrl()->GetValue().ToDouble(&dampingFactor); - - if (!read_double || m_params.start < 0 || m_params.end > 500|| m_params.end < m_params.start) { - MessageDialog msg_dlg(nullptr, _L("Please input valid values:\nStart > 0\nEnd < 500\nStart < End"), wxEmptyString, wxICON_WARNING | wxOK); + if (!read_double || + m_params.freqStartX < 0 || m_params.freqEndX > 500 || + m_params.freqStartY < 0 || m_params.freqEndX > 500 || + m_params.freqStartX >= m_params.freqEndX || + m_params.freqStartY >= m_params.freqEndY) { + MessageDialog msg_dlg(nullptr, _L("Please input valid values\n(0 < FreqStart < FreqEnd < 500"), wxEmptyString, wxICON_WARNING | wxOK); msg_dlg.ShowModal(); return; } - if (!read_damping || dampingFactor < 0 || dampingFactor >= 1) { + if (m_params.start < 0 || m_params.start >= 1) { MessageDialog msg_dlg(nullptr, _L("Please input a valid damping factor (0 < Damping/zeta factor <= 1)"), wxEmptyString, wxICON_WARNING | wxOK); msg_dlg.ShowModal(); return; } - m_params.step = dampingFactor; m_params.mode = CalibMode::Calib_Input_shaping_freq; + + // Set model type based on selection + m_params.test_model = m_rbModel->GetSelection() == 0 ? 0 : 1; // 0 = Ringing Tower, 1 = Fast Tower + m_plater->calib_input_shaping_freq(m_params); EndModal(wxID_OK); } @@ -899,7 +933,6 @@ void Input_Shaping_Freq_Test_Dlg::on_start(wxCommandEvent& event) { void Input_Shaping_Freq_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect) { this->Refresh(); Fit(); - } // Input_Shaping_Damp_Test_Dlg @@ -911,31 +944,57 @@ Input_Shaping_Damp_Test_Dlg::Input_Shaping_Damp_Test_Dlg(wxWindow* parent, wxWin wxBoxSizer* v_sizer = new wxBoxSizer(wxVERTICAL); SetSizer(v_sizer); + // Model selection + wxString m_rbModelChoices[] = { _L("Ringing Tower"), _L("Fast Tower") }; + int m_rbModelNChoices = sizeof(m_rbModelChoices) / sizeof(wxString); + m_rbModel = new wxRadioBox(this, wxID_ANY, _L("Test model"), wxDefaultPosition, wxDefaultSize, m_rbModelNChoices, m_rbModelChoices, 1, wxRA_SPECIFY_ROWS); + m_rbModel->SetSelection(0); + v_sizer->Add(m_rbModel, 0, wxALL | wxEXPAND, 5); + // Settings // - wxString start_length_str = _L("X: "); - wxString end_length_str = _L("Y: "); - auto text_size = wxWindow::GetTextExtent(start_length_str); - text_size.IncTo(wxWindow::GetTextExtent(end_length_str)); + wxString freq_x_str = _L("Freq X: "); + wxString freq_y_str = _L("Freq Y: "); + wxString damp_start_str = _L("Start damp: "); + wxString damp_end_str = _L("End damp: "); + auto text_size = wxWindow::GetTextExtent(freq_x_str); + text_size.IncTo(wxWindow::GetTextExtent(freq_y_str)); + text_size.IncTo(wxWindow::GetTextExtent(damp_start_str)); + text_size.IncTo(wxWindow::GetTextExtent(damp_end_str)); text_size.x = text_size.x * 1.5; wxStaticBoxSizer* settings_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _L("Frequency settings")); auto st_size = FromDIP(wxSize(text_size.x, -1)); auto ti_size = FromDIP(wxSize(90, -1)); - auto start_length_sizer = new wxBoxSizer(wxHORIZONTAL); - auto start_length_text = new wxStaticText(this, wxID_ANY, start_length_str, wxDefaultPosition, st_size, wxALIGN_LEFT); - m_tiStart = new TextInput(this, std::to_string(40), _L("HZ"), "", wxDefaultPosition, ti_size, wxTE_CENTRE); - m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); - auto end_length_text = new wxStaticText(this, wxID_ANY, end_length_str, wxDefaultPosition, st_size, wxALIGN_LEFT); - m_tiEnd = new TextInput(this, std::to_string(40), _L("HZ"), "", wxDefaultPosition, ti_size, wxTE_CENTRE); - m_tiEnd->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + auto freq_sizer = new wxBoxSizer(wxHORIZONTAL); + auto freq_x_text = new wxStaticText(this, wxID_ANY, freq_x_str, wxDefaultPosition, st_size, wxALIGN_LEFT); + m_tiFreqX = new TextInput(this, std::to_string(30), _L("HZ"), "", wxDefaultPosition, ti_size, wxTE_CENTRE); + m_tiFreqX->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + auto freq_y_text = new wxStaticText(this, wxID_ANY, freq_y_str, wxDefaultPosition, st_size, wxALIGN_LEFT); + m_tiFreqY = new TextInput(this, std::to_string(30), _L("HZ"), "", wxDefaultPosition, ti_size, wxTE_CENTRE); + m_tiFreqY->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); - start_length_sizer->Add(start_length_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); - start_length_sizer->Add(m_tiStart, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); - start_length_sizer->Add(end_length_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); - start_length_sizer->Add(m_tiEnd, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); - settings_sizer->Add(start_length_sizer); + freq_sizer->Add(freq_x_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + freq_sizer->Add(m_tiFreqX, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + freq_sizer->Add(freq_y_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + freq_sizer->Add(m_tiFreqY, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + settings_sizer->Add(freq_sizer); + + // Damping Factor Start and End + auto damp_sizer = new wxBoxSizer(wxHORIZONTAL); + auto damp_start_text = new wxStaticText(this, wxID_ANY, damp_start_str, wxDefaultPosition, st_size, wxALIGN_LEFT); + m_tiDampingFactorStart = new TextInput(this, wxString::Format("%.2f", 0.00), "", "", wxDefaultPosition, ti_size, wxTE_CENTRE); + m_tiDampingFactorStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + auto damp_end_text = new wxStaticText(this, wxID_ANY, damp_end_str, wxDefaultPosition, st_size, wxALIGN_LEFT); + m_tiDampingFactorEnd = new TextInput(this, wxString::Format("%.2f", 0.40), "", "", wxDefaultPosition, ti_size, wxTE_CENTRE); + m_tiDampingFactorEnd->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + + damp_sizer->Add(damp_start_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + damp_sizer->Add(m_tiDampingFactorStart, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + damp_sizer->Add(damp_end_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + damp_sizer->Add(m_tiDampingFactorEnd, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + settings_sizer->Add(damp_sizer); // Add a note to explain users to use their previously calculated frequency auto note_text = new wxStaticText(this, wxID_ANY, _L("Note: Set frequencies to the previously calculated values"), @@ -974,16 +1033,32 @@ Input_Shaping_Damp_Test_Dlg::~Input_Shaping_Damp_Test_Dlg() { void Input_Shaping_Damp_Test_Dlg::on_start(wxCommandEvent& event) { bool read_double = false; - read_double = m_tiStart->GetTextCtrl()->GetValue().ToDouble(&m_params.start); - read_double = read_double && m_tiEnd->GetTextCtrl()->GetValue().ToDouble(&m_params.end); + read_double = m_tiFreqX->GetTextCtrl()->GetValue().ToDouble(&m_params.freqStartX); + read_double = read_double && m_tiFreqY->GetTextCtrl()->GetValue().ToDouble(&m_params.freqStartY); + read_double = read_double && m_tiDampingFactorStart->GetTextCtrl()->GetValue().ToDouble(&m_params.start); + read_double = read_double && m_tiDampingFactorEnd->GetTextCtrl()->GetValue().ToDouble(&m_params.end); + - if (!read_double || m_params.start < 0 || m_params.start > 500 || m_params.end < 0 || m_params.end > 500) { + if (!read_double || + m_params.freqStartX < 0 || m_params.freqStartX > 500 || + m_params.freqStartY < 0 || m_params.freqStartY > 500 ) { MessageDialog msg_dlg(nullptr, _L("Please input valid values\n(0 < Freq < 500"), wxEmptyString, wxICON_WARNING | wxOK); msg_dlg.ShowModal(); return; } + if (m_params.start < 0 || m_params.end > 1 + || m_params.start >= m_params.end) { + MessageDialog msg_dlg(nullptr, _L("Please input a valid damping factor (0 <= DampingStart < DampingEnd <= 1)"), wxEmptyString, wxICON_WARNING | wxOK); + msg_dlg.ShowModal(); + return; + } + m_params.mode = CalibMode::Calib_Input_shaping_damp; + + // Set model type based on selection + m_params.test_model = m_rbModel->GetSelection() == 0 ? 0 : 1; // 0 = Ringing Tower, 1 = Fast Tower + m_plater->calib_input_shaping_damp(m_params); EndModal(wxID_OK); } @@ -991,7 +1066,6 @@ void Input_Shaping_Damp_Test_Dlg::on_start(wxCommandEvent& event) { void Input_Shaping_Damp_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect) { this->Refresh(); Fit(); - } }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/calib_dlg.hpp b/src/slic3r/GUI/calib_dlg.hpp index cf9ce0b022..a12eabd3ce 100644 --- a/src/slic3r/GUI/calib_dlg.hpp +++ b/src/slic3r/GUI/calib_dlg.hpp @@ -137,8 +137,11 @@ protected: virtual void on_start(wxCommandEvent& event); Calib_Params m_params; - TextInput* m_tiStart; - TextInput* m_tiEnd; + wxRadioBox* m_rbModel; + TextInput* m_tiFreqStartX; + TextInput* m_tiFreqEndX; + TextInput* m_tiFreqStartY; + TextInput* m_tiFreqEndY; TextInput* m_tiDampingFactor; Button* m_btnStart; Plater* m_plater; @@ -156,8 +159,11 @@ protected: virtual void on_start(wxCommandEvent& event); Calib_Params m_params; - TextInput* m_tiStart; - TextInput* m_tiEnd; + wxRadioBox* m_rbModel; + TextInput* m_tiFreqX; + TextInput* m_tiFreqY; + TextInput* m_tiDampingFactorStart; + TextInput* m_tiDampingFactorEnd; Button* m_btnStart; Plater* m_plater; }; From 428cba9a25344628995cdd7de464026f11ca2ac5 Mon Sep 17 00:00:00 2001 From: Ian Bassi Date: Sun, 30 Mar 2025 14:23:17 -0300 Subject: [PATCH 04/10] Juntion Deviation + Documentation Junction Deviation Calibration Test Base documentation - VFA - Input Shaping - Junction Deviation Co-Authored-By: Rodrigo <162915171+rf47@users.noreply.github.com> --- doc/Calibration.md | 106 ++++++++++++++++-- src/libslic3r/GCode.cpp | 12 +- src/libslic3r/GCodeWriter.cpp | 10 ++ src/libslic3r/GCodeWriter.hpp | 1 + src/libslic3r/calib.hpp | 3 +- src/slic3r/GUI/CalibrationWizardSavePage.cpp | 2 + src/slic3r/GUI/MainFrame.cpp | 21 ++++ src/slic3r/GUI/MainFrame.hpp | 1 + src/slic3r/GUI/Plater.cpp | 35 ++++++ src/slic3r/GUI/Plater.hpp | 1 + src/slic3r/GUI/calib_dlg.cpp | 109 ++++++++++++++++++- src/slic3r/GUI/calib_dlg.hpp | 17 +++ src/slic3r/Utils/CalibUtils.cpp | 4 + 13 files changed, 307 insertions(+), 15 deletions(-) diff --git a/doc/Calibration.md b/doc/Calibration.md index 58204657d7..202198e436 100644 --- a/doc/Calibration.md +++ b/doc/Calibration.md @@ -1,14 +1,18 @@ -- [Flow rate](#Flow-rate) -- [Pressure Advance](#Pressure-Advance) - 1. [Line method](#Line-method) - 2. [Pattern method](#Pattern-method) - 3. [Tower method](#Tower-method) -- [Temp tower](#Temp-tower) -- [Retraction test](#Retraction-test) -- [Orca Tolerance Test](#Orca-Tolerance-Test) -- [Advanced calibration](#Advanced-Calibration) - 1. [Max Volumetric speed](#Max-Volumetric-speed) - 2. [VFA] +- [Flow rate](#flow-rate) +- [Pressure Advance](#pressure-advance) + - [Line method](#line-method) + - [Pattern method](#pattern-method) + - [Tower method](#tower-method) +- [Temp tower](#temp-tower) +- [Retraction test](#retraction-test) +- [Orca Tolerance Test](#orca-tolerance-test) +- [Advanced Calibration](#advanced-calibration) + - [Max Volumetric speed](#max-volumetric-speed) + - [Input Shaping](#input-shaping) + - [ZV Input Shaping](#zv-input-shaping) + - [Fixed-Time Motion](#fixed-time-motion) + - [Junction Deviation](#junction-deviation) + - [VFA](#vfa) > [!IMPORTANT] > After completing the calibration process, remember to create a new project in order to exit the calibration mode. @@ -152,6 +156,85 @@ You can also return to OrcaSlicer in the "Preview" tab, make sure the color sche > [!NOTE] > You may also choose to conservatively reduce the flow by 5-10% to ensure print quality. +## Input Shaping + +During high-speed movements, vibrations can cause a phenomenon called "ringing," where periodic ripples appear on the print surface. Input Shaping provides an effective solution by counteracting these vibrations, improving print quality and reducing wear on components without needing to significantly lower print speeds. + +### ZV Input Shaping + +ZV Input Shaping introduces an anti-vibration signal into the stepper motion for the X and Y axes. It works by splitting the step count into two halves: the first at half the frequency and the second as an "echo," delayed by half the ringing interval. This simple approach effectively reduces vibrations, improving print quality and allowing for higher speeds. + +1. Pre-requisites: + 1. In OrcaSlicer, set: + 1. Acceleration high enough to trigger ringing (e.g., 2000 mm/s²). + 2. Speed high enough to trigger ringing (e.g., 100 mm/s). + 3. Jerk + 1. If using a [Classic Jerk](https://marlinfw.org/docs/configuration/configuration.html#jerk-) use a high value (e.g., 20). + 2. If using [Junction Deviation](https://marlinfw.org/docs/features/junction_deviation.html) this test will use 0.25 (high enough to most printers). + 2. Use a high gloss filament to make the ringing more visible. +2. You need to print the Input SHaping Frequency test. + 1. Measure the X and Y heights and read the frequency set at that point in Orca Slicer. + 2. If not a clear result, you can measure a X and Y min and max acceptable heights and repeat the test with that min and max value. +3. When you find the best X and Y frequency, its time to Damp test setting your X and Y frequency to the value you found in the previous step. +4. Restore your 3D Printer settings to avoid keep using high acceleration and jerk values. + 1. Reboot your printer. + 2. Use the following G-code to restore your printer settings: + ```gcode + M501 + ``` +5. Save the settings + 1. You need to go to the printer settings and set the X and Y frequency and damp to the value you found in the previous step. + 2. Use the following G-code to set the frequency: + ```gcode + M593 X F#Xfrequency D#XDamping + M593 Y F#Yfrequency D#YDamping + M500 + ``` + Example + ```gcode + M593 X F37.25 D0.16 + M593 Y F37.5 D0.06 + M500 + ``` + +### Fixed-Time Motion + +TODO This calibration test is currently under development. + +## Junction Deviation + +Junction Deviation is the default method for controlling cornering speed in MarlinFW printers. +Higher values result in more aggressive cornering speeds, while lower values produce smoother, more controlled cornering. +The default value in Marlin is typically set to 0.08mm, which may be too high for some printers, potentially causing ringing. Consider lowering this value to reduce ringing, but avoid setting it too low, as this could lead to excessively slow cornering speeds. + +1. Pre-requisites: + 1. Check if your printer has Junction Deviation enabled. You can do this by sending the command `M503` to your printer and looking for the line `Junction deviation: 0.25`. + 2. In OrcaSlicer, set: + 1. Acceleration high enough to trigger ringing (e.g., 2000 mm/s²). + 2. Speed high enough to trigger ringing (e.g., 100 mm/s). + 3. Use a high gloss filament to make the ringing more visible. +2. You need to print the Junction Deviation test. + 1. Measure the X and Y heights and read the frequency set at that point in Orca Slicer. + 2. If not a clear result, you can measure a X and Y min and max acceptable heights and repeat the test with that min and max value. +3. Save the settings + 1. Use the following G-code to set the frequency: + ```gcode + M205 J#JunctionDeviationValue + M500 + ``` + Example + ```gcode + M205 J0.013 + M500 + ``` + 2. Set it in your Marlin Compilation. + +## VFA + +Vertical Fine Artifacts (VFA) are small artifacts that can occur on the surface of a 3D print, particularly in areas where there are sharp corners or changes in direction. These artifacts can be caused by a variety of factors, including mechanical vibrations, resonance, and other factors that can affect the quality of the print. +Because of the nature of these artifacts the methods to reduce them can be mechanical such as changing motors, belts and pulleys or with advanced calibrations such as Jerk/[Juction Deviation](#junction-deviation) corrections or [Input Shaping](#input-shaping). + + *** *Credits:* - *The Flowrate test and retraction test is inspired by [SuperSlicer](https://github.com/supermerill/SuperSlicer)* @@ -159,4 +242,5 @@ You can also return to OrcaSlicer in the "Preview" tab, make sure the color sche - *The PA Tower method is inspired by [Klipper](https://www.klipper3d.org/Pressure_Advance.html)* - *The temp tower model is remixed from [Smart compact temperature calibration tower](https://www.thingiverse.com/thing:2729076) - *The max flowrate test was inspired by Stefan(CNC Kitchen), and the model used in the test is a remix of his [Extrusion Test Structure](https://www.printables.com/model/342075-extrusion-test-structure). +- *ZV Input Shaping is inspired by [Marlin Input Shaping](https://marlinfw.org/docs/features/input_shaping.html) and [Ringing Tower 3D STL](https://marlinfw.org/assets/stl/ringing_tower.stl) - *ChatGPT* ;) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index c53a077b87..8e1d1e5231 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3749,7 +3749,6 @@ LayerResult GCode::process_layer( //BBS: set layer time fan speed after layer change gcode gcode += ";_SET_FAN_SPEED_CHANGING_LAYER\n"; - //TODO: Why this is not a Switch-case? if (print.calib_mode() == CalibMode::Calib_PA_Tower) { gcode += writer().set_pressure_advance(print.calib_params().start + static_cast(print_z) * print.calib_params().step); } else if (print.calib_mode() == CalibMode::Calib_Temp_Tower) { @@ -3770,9 +3769,13 @@ LayerResult GCode::process_layer( gcode += buf; } else if (print.calib_mode() == CalibMode::Calib_Input_shaping_freq) { if (m_layer_index == 1){ + if (print.config().gcode_flavor.value == gcfMarlinFirmware) { + gcode += writer().set_junction_deviation(0.25);//Set junction deviation at high value to maximize ringing. + } gcode += writer().set_input_shaping('A', print.calib_params().start, 0.f); } else { if (print.calib_params().freqStartX == print.calib_params().freqStartY && print.calib_params().freqEndX == print.calib_params().freqEndY) { + gcode += writer().set_junction_deviation(0.25); gcode += writer().set_input_shaping('A', 0.f, (print.calib_params().freqStartX) + ((print.calib_params().freqEndX)-(print.calib_params().freqStartX)) * (m_layer_index - 2) / (m_layer_count - 3)); } else { gcode += writer().set_input_shaping('X', 0.f, (print.calib_params().freqStartX) + ((print.calib_params().freqEndX)-(print.calib_params().freqStartX)) * (m_layer_index - 2) / (m_layer_count - 3)); @@ -3781,11 +3784,16 @@ LayerResult GCode::process_layer( } } else if (print.calib_mode() == CalibMode::Calib_Input_shaping_damp) { if (m_layer_index == 1){ - gcode += writer().set_input_shaping('X', 0.f, print.calib_params().freqStartX); + if (print.config().gcode_flavor.value == gcfMarlinFirmware) { + gcode += writer().set_junction_deviation(0.25); // Set junction deviation at high value to maximize ringing. + } + gcode += writer().set_input_shaping('X', 0.f, print.calib_params().freqStartX); gcode += writer().set_input_shaping('Y', 0.f, print.calib_params().freqStartY); } else { gcode += writer().set_input_shaping('A', print.calib_params().start + ((print.calib_params().end)-(print.calib_params().start)) * (m_layer_index) / (m_layer_count), 0.f); } + } else if (print.calib_mode() == CalibMode::Calib_Junction_Deviation){ + gcode += writer().set_junction_deviation(print.calib_params().start + ((print.calib_params().end)-(print.calib_params().start)) * (m_layer_index) / (m_layer_count)); } //BBS diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index f1012e5a7e..8f5b4b4c0c 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -313,6 +313,16 @@ 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_NOT(gcfMarlinFirmware)) { + throw std::runtime_error("Junction deviation is only supported by Marlin firmware"); + } + gcode << "M205 J" << junction_deviation << " ; Junction Deviation\n"; + return gcode.str(); +} + + std::string GCodeWriter::set_pressure_advance(double pa) const { std::ostringstream gcode; diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 2b2887a152..3b2133c456 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -53,6 +53,7 @@ public: std::string set_jerk_xy(double jerk); // Orca: set acceleration and jerk in one command for Klipper std::string set_accel_and_jerk(unsigned int acceleration, double jerk); + std::string set_junction_deviation(double junction_deviation); std::string set_pressure_advance(double pa) const; std::string set_input_shaping(char axis, float damp, float freq) const; std::string reset_e(bool force = false); diff --git a/src/libslic3r/calib.hpp b/src/libslic3r/calib.hpp index df78fe90b3..32c516c778 100644 --- a/src/libslic3r/calib.hpp +++ b/src/libslic3r/calib.hpp @@ -23,7 +23,8 @@ enum class CalibMode : int { Calib_VFA_Tower, Calib_Retraction_tower, Calib_Input_shaping_freq, - Calib_Input_shaping_damp + Calib_Input_shaping_damp, + Calib_Junction_Deviation }; enum class CalibState { Start = 0, Preset, Calibration, CoarseSave, FineCalibration, Save, Finish }; diff --git a/src/slic3r/GUI/CalibrationWizardSavePage.cpp b/src/slic3r/GUI/CalibrationWizardSavePage.cpp index 7499acd27a..4d9fb67a5e 100644 --- a/src/slic3r/GUI/CalibrationWizardSavePage.cpp +++ b/src/slic3r/GUI/CalibrationWizardSavePage.cpp @@ -48,6 +48,8 @@ static wxString get_default_name(wxString filament_name, CalibMode mode){ break; case Slic3r::CalibMode::Calib_Input_shaping_damp: break; + case Slic3r::CalibMode::Calib_Junction_Deviation: + break; default: break; } diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 776574deff..590ae1f792 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -3000,6 +3000,17 @@ void MainFrame::init_menubar_as_editor() m_topbar->GetCalibMenu()->AppendSubMenu(input_shaping_menu, _L("Input Shaping")); + // Add Junction Deviation option to More menu + append_menu_item( + advance_menu, wxID_ANY, _L("Junction Deviation"), _L("Junction Deviation calibration"), + [this](wxCommandEvent&) { + if (!m_junction_deviation_calib_dlg) + m_junction_deviation_calib_dlg = new Junction_Deviation_Test_Dlg((wxWindow*)this, wxID_ANY, m_plater); + m_junction_deviation_calib_dlg->ShowModal(); + }, + "", nullptr, + [this]() {return m_plater->is_view3D_shown();; }, this); + m_topbar->GetCalibMenu()->AppendSubMenu(advance_menu, _L("More...")); // help @@ -3098,6 +3109,16 @@ void MainFrame::init_menubar_as_editor() }, "", nullptr, [this]() {return m_plater->is_view3D_shown();; }, this); + // Add Junction Deviation option to More menu + append_menu_item( + advance_menu, wxID_ANY, _L("Junction Deviation"), _L("Junction Deviation calibration"), + [this](wxCommandEvent&) { + if (!m_junction_deviation_calib_dlg) + m_junction_deviation_calib_dlg = new Junction_Deviation_Test_Dlg((wxWindow*)this, wxID_ANY, m_plater); + m_junction_deviation_calib_dlg->ShowModal(); + }, "", nullptr, + [this]() {return m_plater->is_view3D_shown();; }, this); + // Input Shaping calibrations auto input_shaping_menu = new wxMenu(); diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 1fffbcd336..410ea8dc62 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -356,6 +356,7 @@ public: Retraction_Test_Dlg* m_retraction_calib_dlg{ nullptr }; Input_Shaping_Freq_Test_Dlg* m_IS_freq_calib_dlg{ nullptr }; Input_Shaping_Damp_Test_Dlg* m_IS_damp_calib_dlg{ nullptr }; + Junction_Deviation_Test_Dlg* m_junction_deviation_calib_dlg{ nullptr }; // BBS. Replace title bar and menu bar with top bar. BBLTopbar* m_topbar{ nullptr }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index d01bff010b..6df4bd6044 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -10220,6 +10220,41 @@ void Plater::calib_input_shaping_damp(const Calib_Params& params) p->background_process.fff_print()->set_calib_params(params); } +void Plater::calib_junction_deviation(const Calib_Params& params) +{ + const auto calib_junction_deviation = wxString::Format(L"Input shaping Damping test"); + new_project(false, false, calib_junction_deviation); + wxGetApp().mainframe->select_tab(size_t(MainFrame::tp3DEditor)); + if (params.mode != CalibMode::Calib_Junction_Deviation) + return; + + add_model(false, Slic3r::resources_dir() + (params.test_model < 1 ? "/calib/input_shaping/ringing_tower.stl" : "/calib/input_shaping/fast_input_shaping_test.stl")); + auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config; + auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config; + filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 2.0 }); + filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats { 200 }); + 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)); + print_config->set_key_value("bottom_shell_layers", new ConfigOptionInt(1)); + print_config->set_key_value("sparse_infill_density", new ConfigOptionPercent(0)); + print_config->set_key_value("detect_thin_wall", new ConfigOptionBool(false)); + print_config->set_key_value("spiral_mode", new ConfigOptionBool(true)); + print_config->set_key_value("spiral_mode_smooth", new ConfigOptionBool(true)); + model().objects[0]->config.set_key_value("brim_type", new ConfigOptionEnum(btOuterOnly)); + model().objects[0]->config.set_key_value("brim_width", new ConfigOptionFloat(3.0)); + model().objects[0]->config.set_key_value("brim_object_gap", new ConfigOptionFloat(0.0)); + + changed_objects({ 0 }); + wxGetApp().get_tab(Preset::TYPE_PRINT)->update_dirty(); + wxGetApp().get_tab(Preset::TYPE_FILAMENT)->update_dirty(); + wxGetApp().get_tab(Preset::TYPE_PRINT)->update_ui_from_settings(); + wxGetApp().get_tab(Preset::TYPE_FILAMENT)->update_ui_from_settings(); + + p->background_process.fff_print()->set_calib_params(params); +} + BuildVolume_Type Plater::get_build_volume_type() const { return p->bed.get_build_volume_type(); } void Plater::import_zip_archive() diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index eff592d455..ece34ba829 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -275,6 +275,7 @@ public: void calib_VFA(const Calib_Params& params); void calib_input_shaping_freq(const Calib_Params& params); void calib_input_shaping_damp(const Calib_Params& params); + void calib_junction_deviation(const Calib_Params& params); BuildVolume_Type get_build_volume_type() const; diff --git a/src/slic3r/GUI/calib_dlg.cpp b/src/slic3r/GUI/calib_dlg.cpp index d9d80fa5d2..7ff4d9629b 100644 --- a/src/slic3r/GUI/calib_dlg.cpp +++ b/src/slic3r/GUI/calib_dlg.cpp @@ -1068,4 +1068,111 @@ void Input_Shaping_Damp_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect) { Fit(); } -}} // namespace Slic3r::GUI +// Junction_Deviation_Test_Dlg +// + +Junction_Deviation_Test_Dlg::Junction_Deviation_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater) + : DPIDialog(parent, id, _L("Junction Deviation test"), wxDefaultPosition, parent->FromDIP(wxSize(-1, 280)), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), m_plater(plater) +{ + wxBoxSizer* v_sizer = new wxBoxSizer(wxVERTICAL); + SetSizer(v_sizer); + + // Model selection + wxString m_rbModelChoices[] = { _L("Ringing Tower"), _L("Fast Tower") }; + int m_rbModelNChoices = sizeof(m_rbModelChoices) / sizeof(wxString); + m_rbModel = new wxRadioBox(this, wxID_ANY, _L("Test model"), wxDefaultPosition, wxDefaultSize, m_rbModelNChoices, m_rbModelChoices, 1, wxRA_SPECIFY_ROWS); + m_rbModel->SetSelection(1); + v_sizer->Add(m_rbModel, 0, wxALL | wxEXPAND, 5); + + // Settings + wxString start_jd_str = _L("Start junction deviation: "); + wxString end_jd_str = _L("End junction deviation: "); + auto text_size = wxWindow::GetTextExtent(start_jd_str); + text_size.IncTo(wxWindow::GetTextExtent(end_jd_str)); + text_size.x = text_size.x * 1.5; + wxStaticBoxSizer* settings_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _L("Junction Deviation settings")); + + auto st_size = FromDIP(wxSize(text_size.x, -1)); + auto ti_size = FromDIP(wxSize(90, -1)); + + // Start junction deviation + auto start_jd_sizer = new wxBoxSizer(wxHORIZONTAL); + auto start_jd_text = new wxStaticText(this, wxID_ANY, start_jd_str, wxDefaultPosition, st_size, wxALIGN_LEFT); + m_tiJDStart = new TextInput(this, wxString::Format("%.3f", 0.000), _L("mm"), "", wxDefaultPosition, ti_size, wxTE_CENTRE); + m_tiJDStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + start_jd_sizer->Add(start_jd_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + start_jd_sizer->Add(m_tiJDStart, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + settings_sizer->Add(start_jd_sizer); + + // End junction deviation + auto end_jd_sizer = new wxBoxSizer(wxHORIZONTAL); + auto end_jd_text = new wxStaticText(this, wxID_ANY, end_jd_str, wxDefaultPosition, st_size, wxALIGN_LEFT); + m_tiJDEnd = new TextInput(this, wxString::Format("%.3f", 0.250), _L("mm"), "", wxDefaultPosition, ti_size, wxTE_CENTRE); + m_tiJDEnd->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + end_jd_sizer->Add(end_jd_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + end_jd_sizer->Add(m_tiJDEnd, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2); + settings_sizer->Add(end_jd_sizer); + + // Add note about junction deviation + auto note_text = new wxStaticText(this, wxID_ANY, _L("Note: Lower values = sharper corners but slower speeds"), + wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + note_text->SetForegroundColour(wxColour(128, 128, 128)); + settings_sizer->Add(note_text, 0, wxALL, 5); + + v_sizer->Add(settings_sizer); + v_sizer->Add(0, FromDIP(10), 0, wxEXPAND, 5); + m_btnStart = new Button(this, _L("OK")); + StateColor btn_bg_green(std::pair(wxColour(0, 137, 123), StateColor::Pressed), + std::pair(wxColour(38, 166, 154), StateColor::Hovered), + std::pair(wxColour(0, 150, 136), StateColor::Normal)); + + m_btnStart->SetBackgroundColor(btn_bg_green); + m_btnStart->SetBorderColor(wxColour(0, 150, 136)); + m_btnStart->SetTextColor(wxColour("#FFFFFE")); + m_btnStart->SetSize(wxSize(FromDIP(48), FromDIP(24))); + m_btnStart->SetMinSize(wxSize(FromDIP(48), FromDIP(24))); + m_btnStart->SetCornerRadius(FromDIP(3)); + m_btnStart->Bind(wxEVT_BUTTON, &Junction_Deviation_Test_Dlg::on_start, this); + v_sizer->Add(m_btnStart, 0, wxALL | wxALIGN_RIGHT, FromDIP(5)); + + m_btnStart->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(Junction_Deviation_Test_Dlg::on_start), NULL, this); + + Layout(); + Fit(); +} + +Junction_Deviation_Test_Dlg::~Junction_Deviation_Test_Dlg() { + // Disconnect Events + m_btnStart->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(Junction_Deviation_Test_Dlg::on_start), NULL, this); +} + +void Junction_Deviation_Test_Dlg::on_start(wxCommandEvent& event) { + bool read_double = false; + read_double = m_tiJDStart->GetTextCtrl()->GetValue().ToDouble(&m_params.start); + read_double = read_double && m_tiJDEnd->GetTextCtrl()->GetValue().ToDouble(&m_params.end); + + if (!read_double || m_params.start < 0 || m_params.end >= 1 || m_params.start >= m_params.end) { + MessageDialog msg_dlg(nullptr, _L("Please input valid values\n(0 <= Junction Deviation < 1)"), wxEmptyString, wxICON_WARNING | wxOK); + msg_dlg.ShowModal(); + return; + } else if (m_params.end > 0.3) { + MessageDialog msg_dlg(nullptr, _L("NOTE: High values may cause Layer shift"), wxEmptyString, wxICON_WARNING | wxOK); + msg_dlg.ShowModal(); + return; + } + + m_params.mode = CalibMode::Calib_Junction_Deviation; + + // Set model type based on selection + m_params.test_model = m_rbModel->GetSelection() == 0 ? 0 : 1; // 0 = Ringing Tower, 1 = Fast Tower + + m_plater->calib_junction_deviation(m_params); + EndModal(wxID_OK); +} + +void Junction_Deviation_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect) { + this->Refresh(); + Fit(); +} + +}} // namespace Slic3r::GUI \ No newline at end of file diff --git a/src/slic3r/GUI/calib_dlg.hpp b/src/slic3r/GUI/calib_dlg.hpp index a12eabd3ce..52d71d1520 100644 --- a/src/slic3r/GUI/calib_dlg.hpp +++ b/src/slic3r/GUI/calib_dlg.hpp @@ -168,5 +168,22 @@ protected: Plater* m_plater; }; +class Junction_Deviation_Test_Dlg : public DPIDialog +{ +public: + Junction_Deviation_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater); + ~Junction_Deviation_Test_Dlg(); + void on_dpi_changed(const wxRect& suggested_rect) override; + +protected: + virtual void on_start(wxCommandEvent& event); + Calib_Params m_params; + + wxRadioBox* m_rbModel; + TextInput* m_tiJDStart; + TextInput* m_tiJDEnd; + Button* m_btnStart; + Plater* m_plater; +}; }} // namespace Slic3r::GUI #endif diff --git a/src/slic3r/Utils/CalibUtils.cpp b/src/slic3r/Utils/CalibUtils.cpp index 71fcdd4ed5..cf8709c91c 100644 --- a/src/slic3r/Utils/CalibUtils.cpp +++ b/src/slic3r/Utils/CalibUtils.cpp @@ -61,6 +61,8 @@ std::string get_calib_mode_name(CalibMode cali_mode, int stage) return "input_shaping_freq_calib_mode"; case CalibMode::Calib_Input_shaping_damp: return "input_shaping_damp_calib_mode"; + case CalibMode::Calib_Junction_Deviation: + return "junction_deviation_calib_mode"; default: assert(false); return ""; @@ -204,6 +206,8 @@ CalibMode CalibUtils::get_calib_mode_by_name(const std::string name, int& cali_s return CalibMode::Calib_Input_shaping_freq; else if (name == "input_shaping_damp_calib_mode") return CalibMode::Calib_Input_shaping_damp; + else if (name == "junction_deviation_calib_mode") + return CalibMode::Calib_Junction_Deviation; return CalibMode::Calib_None; } From 9890ab333c337e9265271542326f7f7e7de85be8 Mon Sep 17 00:00:00 2001 From: Ian Bassi Date: Mon, 31 Mar 2025 10:32:40 -0300 Subject: [PATCH 05/10] Disable PA PA = 0 To improve the test and Klipper compatibility Fix --- src/libslic3r/GCode.cpp | 1 - src/slic3r/GUI/Plater.cpp | 12 +++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 8e1d1e5231..6f90c67ef6 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3775,7 +3775,6 @@ LayerResult GCode::process_layer( gcode += writer().set_input_shaping('A', print.calib_params().start, 0.f); } else { if (print.calib_params().freqStartX == print.calib_params().freqStartY && print.calib_params().freqEndX == print.calib_params().freqEndY) { - gcode += writer().set_junction_deviation(0.25); gcode += writer().set_input_shaping('A', 0.f, (print.calib_params().freqStartX) + ((print.calib_params().freqEndX)-(print.calib_params().freqStartX)) * (m_layer_index - 2) / (m_layer_count - 3)); } else { gcode += writer().set_input_shaping('X', 0.f, (print.calib_params().freqStartX) + ((print.calib_params().freqEndX)-(print.calib_params().freqStartX)) * (m_layer_index - 2) / (m_layer_count - 3)); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6df4bd6044..c22b68e2c2 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -10163,6 +10163,8 @@ void Plater::calib_input_shaping_freq(const Calib_Params& params) auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config; filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 2.0 }); filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats { 200 }); + //filament_config->set_key_value("enable_pressure_advance", new ConfigOptionBoolsNullable {false }); + filament_config->set_key_value("pressure_advance", 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)); print_config->set_key_value("wall_loops", new ConfigOptionInt(1)); @@ -10198,7 +10200,9 @@ void Plater::calib_input_shaping_damp(const Calib_Params& params) auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config; filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 2.0 }); filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats { 200 }); - print_config->set_key_value("enable_overhang_speed", new ConfigOptionBool { false }); + //filament_config->set_key_value("enable_pressure_advance", new ConfigOptionBoolsNullable{false}); + filament_config->set_key_value("pressure_advance", 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)); print_config->set_key_value("wall_loops", new ConfigOptionInt(1)); print_config->set_key_value("top_shell_layers", new ConfigOptionInt(0)); @@ -10232,8 +10236,10 @@ void Plater::calib_junction_deviation(const Calib_Params& params) auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config; auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config; filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 2.0 }); - filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats { 200 }); - print_config->set_key_value("enable_overhang_speed", new ConfigOptionBool { false }); + filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats{200}); + // filament_config->set_key_value("enable_pressure_advance", new ConfigOptionBoolsNullable{false}); + filament_config->set_key_value("pressure_advance", 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)); print_config->set_key_value("wall_loops", new ConfigOptionInt(1)); print_config->set_key_value("top_shell_layers", new ConfigOptionInt(0)); From ac56fffffdc3a0b29f7bbbc9bfb31807e2419a7e Mon Sep 17 00:00:00 2001 From: Ian Bassi Date: Tue, 1 Apr 2025 11:25:54 -0300 Subject: [PATCH 06/10] Jerk maxed + Documentation --- doc/Calibration.md | 6 ++++++ src/slic3r/GUI/Plater.cpp | 3 +++ 2 files changed, 9 insertions(+) diff --git a/doc/Calibration.md b/doc/Calibration.md index 202198e436..61871f715e 100644 --- a/doc/Calibration.md +++ b/doc/Calibration.md @@ -172,6 +172,12 @@ ZV Input Shaping introduces an anti-vibration signal into the stepper motion for 1. If using a [Classic Jerk](https://marlinfw.org/docs/configuration/configuration.html#jerk-) use a high value (e.g., 20). 2. If using [Junction Deviation](https://marlinfw.org/docs/features/junction_deviation.html) this test will use 0.25 (high enough to most printers). 2. Use a high gloss filament to make the ringing more visible. + 3. In printer settigs: + 1. Klipper: + 1. Disable [Minimun Cruise Ratio](https://www.klipper3d.org/Kinematics.html#minimum-cruise-ratio) with: + ```gcode + SET_VELOCITY_LIMIT MINIMUM_CRUISE_RATIO=0 + ``` 2. You need to print the Input SHaping Frequency test. 1. Measure the X and Y heights and read the frequency set at that point in Orca Slicer. 2. If not a clear result, you can measure a X and Y min and max acceptable heights and repeat the test with that min and max value. diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c22b68e2c2..28154bd5c8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -10165,6 +10165,7 @@ void Plater::calib_input_shaping_freq(const Calib_Params& params) filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats { 200 }); //filament_config->set_key_value("enable_pressure_advance", new ConfigOptionBoolsNullable {false }); filament_config->set_key_value("pressure_advance", new ConfigOptionFloats { 0.0 }); + print_config->set_key_value("default_jerk", new ConfigOptionFloat { 500.0 });// Set jerk to maximum. 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)); @@ -10202,6 +10203,7 @@ void Plater::calib_input_shaping_damp(const Calib_Params& params) filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats { 200 }); //filament_config->set_key_value("enable_pressure_advance", new ConfigOptionBoolsNullable{false}); filament_config->set_key_value("pressure_advance", new ConfigOptionFloats{0.0}); + print_config->set_key_value("default_jerk", new ConfigOptionFloat { 500.0 });// Set jerk to maximum. 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)); @@ -10239,6 +10241,7 @@ void Plater::calib_junction_deviation(const Calib_Params& params) filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats{200}); // filament_config->set_key_value("enable_pressure_advance", new ConfigOptionBoolsNullable{false}); filament_config->set_key_value("pressure_advance", new ConfigOptionFloats{0.0}); + print_config->set_key_value("default_jerk", new ConfigOptionFloat { 500.0 });// Set jerk to maximum. 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)); From 69b645d5c815848c33b2bf4d3ef5d053fb8e2994 Mon Sep 17 00:00:00 2001 From: Ian Bassi Date: Tue, 1 Apr 2025 16:44:34 -0300 Subject: [PATCH 07/10] Jerk Fix + No slow down --- src/slic3r/GUI/Plater.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 28154bd5c8..8df37f6234 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -10161,11 +10161,14 @@ void Plater::calib_input_shaping_freq(const Calib_Params& params) add_model(false, Slic3r::resources_dir() + (params.test_model < 1 ? "/calib/input_shaping/ringing_tower.stl" : "/calib/input_shaping/fast_input_shaping_test.stl")); auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config; auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config; - filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 2.0 }); + 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}); filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats { 200 }); - //filament_config->set_key_value("enable_pressure_advance", new ConfigOptionBoolsNullable {false }); + filament_config->set_key_value("enable_pressure_advance", new ConfigOptionBools {false }); filament_config->set_key_value("pressure_advance", new ConfigOptionFloats { 0.0 }); print_config->set_key_value("default_jerk", new ConfigOptionFloat { 500.0 });// Set jerk to maximum. + print_config->set_key_value("outer_wall_jerk", new ConfigOptionFloat { 500.0 }); 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)); @@ -10199,11 +10202,14 @@ void Plater::calib_input_shaping_damp(const Calib_Params& params) add_model(false, Slic3r::resources_dir() + (params.test_model < 1 ? "/calib/input_shaping/ringing_tower.stl" : "/calib/input_shaping/fast_input_shaping_test.stl")); auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config; auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config; - filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 2.0 }); + 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}); filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats { 200 }); - //filament_config->set_key_value("enable_pressure_advance", new ConfigOptionBoolsNullable{false}); + filament_config->set_key_value("enable_pressure_advance", new ConfigOptionBools{false}); filament_config->set_key_value("pressure_advance", new ConfigOptionFloats{0.0}); print_config->set_key_value("default_jerk", new ConfigOptionFloat { 500.0 });// Set jerk to maximum. + print_config->set_key_value("outer_wall_jerk", new ConfigOptionFloat { 500.0 }); 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)); @@ -10237,11 +10243,14 @@ void Plater::calib_junction_deviation(const Calib_Params& params) add_model(false, Slic3r::resources_dir() + (params.test_model < 1 ? "/calib/input_shaping/ringing_tower.stl" : "/calib/input_shaping/fast_input_shaping_test.stl")); auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config; auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config; - filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 2.0 }); + 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}); filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats{200}); - // filament_config->set_key_value("enable_pressure_advance", new ConfigOptionBoolsNullable{false}); + filament_config->set_key_value("enable_pressure_advance", new ConfigOptionBools{false}); filament_config->set_key_value("pressure_advance", new ConfigOptionFloats{0.0}); print_config->set_key_value("default_jerk", new ConfigOptionFloat { 500.0 });// Set jerk to maximum. + print_config->set_key_value("outer_wall_jerk", new ConfigOptionFloat { 500.0 }); 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)); From 4376f8ba3caee7d7f40f47075006151687199b4a Mon Sep 17 00:00:00 2001 From: Ian Bassi Date: Tue, 1 Apr 2025 18:34:51 -0300 Subject: [PATCH 08/10] Lowering Jerk --- doc/Calibration.md | 2 +- src/slic3r/GUI/Plater.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/Calibration.md b/doc/Calibration.md index 61871f715e..ba94745af3 100644 --- a/doc/Calibration.md +++ b/doc/Calibration.md @@ -169,7 +169,7 @@ ZV Input Shaping introduces an anti-vibration signal into the stepper motion for 1. Acceleration high enough to trigger ringing (e.g., 2000 mm/s²). 2. Speed high enough to trigger ringing (e.g., 100 mm/s). 3. Jerk - 1. If using a [Classic Jerk](https://marlinfw.org/docs/configuration/configuration.html#jerk-) use a high value (e.g., 20). + 1. If using a [Classic Jerk](https://marlinfw.org/docs/configuration/configuration.html#jerk-) or [Klipper Square Corner Velocity](https://www.klipper3d.org/Kinematics.html?h=square+corner+velocity#look-ahead) use a high value (e.g., 20). 2. If using [Junction Deviation](https://marlinfw.org/docs/features/junction_deviation.html) this test will use 0.25 (high enough to most printers). 2. Use a high gloss filament to make the ringing more visible. 3. In printer settigs: diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 8df37f6234..313809b56e 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -10167,8 +10167,8 @@ void Plater::calib_input_shaping_freq(const Calib_Params& params) filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats { 200 }); filament_config->set_key_value("enable_pressure_advance", new ConfigOptionBools {false }); filament_config->set_key_value("pressure_advance", new ConfigOptionFloats { 0.0 }); - print_config->set_key_value("default_jerk", new ConfigOptionFloat { 500.0 });// Set jerk to maximum. - print_config->set_key_value("outer_wall_jerk", new ConfigOptionFloat { 500.0 }); + print_config->set_key_value("default_jerk", new ConfigOptionFloat { 20.0 }); + print_config->set_key_value("outer_wall_jerk", new ConfigOptionFloat { 20.0 }); 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)); @@ -10208,8 +10208,8 @@ void Plater::calib_input_shaping_damp(const Calib_Params& params) filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats { 200 }); filament_config->set_key_value("enable_pressure_advance", new ConfigOptionBools{false}); filament_config->set_key_value("pressure_advance", new ConfigOptionFloats{0.0}); - print_config->set_key_value("default_jerk", new ConfigOptionFloat { 500.0 });// Set jerk to maximum. - print_config->set_key_value("outer_wall_jerk", new ConfigOptionFloat { 500.0 }); + print_config->set_key_value("default_jerk", new ConfigOptionFloat { 20.0 }); + print_config->set_key_value("outer_wall_jerk", new ConfigOptionFloat { 20.0 }); 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)); @@ -10249,8 +10249,8 @@ void Plater::calib_junction_deviation(const Calib_Params& params) filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats{200}); filament_config->set_key_value("enable_pressure_advance", new ConfigOptionBools{false}); filament_config->set_key_value("pressure_advance", new ConfigOptionFloats{0.0}); - print_config->set_key_value("default_jerk", new ConfigOptionFloat { 500.0 });// Set jerk to maximum. - print_config->set_key_value("outer_wall_jerk", new ConfigOptionFloat { 500.0 }); + print_config->set_key_value("default_jerk", new ConfigOptionFloat { 20.0 }); + print_config->set_key_value("outer_wall_jerk", new ConfigOptionFloat { 20.0 }); 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)); From a66bc06b2dc656e267a1996756032690483264e8 Mon Sep 17 00:00:00 2001 From: Ian Bassi Date: Thu, 3 Apr 2025 11:40:56 -0300 Subject: [PATCH 09/10] Klipper compatible Klipper freq & damp test Klipper guide separation from the Marlin guide Co-Authored-By: Shane Delmore --- doc/Calibration.md | 68 ++++++++++++++++++++++++++--------- src/libslic3r/GCodeWriter.cpp | 50 +++++++++++++++++--------- src/slic3r/GUI/Plater.cpp | 6 ---- src/slic3r/GUI/calib_dlg.cpp | 9 ++--- 4 files changed, 90 insertions(+), 43 deletions(-) diff --git a/doc/Calibration.md b/doc/Calibration.md index ba94745af3..7682737318 100644 --- a/doc/Calibration.md +++ b/doc/Calibration.md @@ -9,9 +9,12 @@ - [Advanced Calibration](#advanced-calibration) - [Max Volumetric speed](#max-volumetric-speed) - [Input Shaping](#input-shaping) - - [ZV Input Shaping](#zv-input-shaping) - - [Fixed-Time Motion](#fixed-time-motion) - - [Junction Deviation](#junction-deviation) + - [Klipper](#klipper) + - [Resonance Compensation](#resonance-compensation) + - [Marlin](#marlin) + - [ZV Input Shaping](#zv-input-shaping) + - [Fixed-Time Motion](#fixed-time-motion) + - [Junction Deviation](#junction-deviation) - [VFA](#vfa) > [!IMPORTANT] @@ -160,7 +163,43 @@ You can also return to OrcaSlicer in the "Preview" tab, make sure the color sche During high-speed movements, vibrations can cause a phenomenon called "ringing," where periodic ripples appear on the print surface. Input Shaping provides an effective solution by counteracting these vibrations, improving print quality and reducing wear on components without needing to significantly lower print speeds. -### ZV Input Shaping +### Klipper + +### Resonance Compensation + +The Klipper Resonance Compensation is a set of Input Shaping modes that can be used to reduce ringing and improve print quality. +Ussualy the recommended values modes are ``MZV`` or ``EI`` for Delta printers. + +1. Pre-requisites: + 1. In OrcaSlicer, set: + 1. Acceleration high enough to trigger ringing (e.g., 2000 mm/s²). + 2. Speed high enough to trigger ringing (e.g., 100 mm/s). + 3. Jerk [Klipper Square Corner Velocity](https://www.klipper3d.org/Kinematics.html?h=square+corner+velocity#look-ahead) to 5 or a high value (e.g., 20). + 2. In printer settigs: + 1. Set the Shaper Type to ``MZV`` or ``EI``. + ``` + SET_INPUT_SHAPER SHAPER_TYPE=MZV + ``` + 2. Disable [Minimun Cruise Ratio](https://www.klipper3d.org/Kinematics.html#minimum-cruise-ratio) with: + ``` + SET_VELOCITY_LIMIT MINIMUM_CRUISE_RATIO=0 + ``` + 3. Use a high gloss filament to make the ringing more visible. +2. Print the Input Shaping Frequency test with a range of frequencies. + 1. Measure the X and Y heights and read the frequency set at that point in Orca Slicer. + 2. If not a clear result, you can measure a X and Y min and max acceptable heights and repeat the test with that min and max value. + + **Note**: There is a chance you will need to set higher than 60Hz frequencies. Some printers with very rigid frames and excellent mechanics may exhibit frequencies exceeding 100Hz. +3. Print the Damping test setting your X and Y frequency to the value you found in the previous step. + 1. Measure the X and Y heights and read the damping set at that point in Orca Slicer. + **Note**: Not all Resonance Compensation modes support damping +1. Restore your 3D Printer settings to avoid keep using high acceleration and jerk values. +2. Save the settings + 1. You need to go to the printer settings and set the X and Y frequency and damp to the value you found in the previous step. + +### Marlin + +#### ZV Input Shaping ZV Input Shaping introduces an anti-vibration signal into the stepper motion for the X and Y axes. It works by splitting the step count into two halves: the first at half the frequency and the second as an "echo," delayed by half the ringing interval. This simple approach effectively reduces vibrations, improving print quality and allowing for higher speeds. @@ -169,19 +208,16 @@ ZV Input Shaping introduces an anti-vibration signal into the stepper motion for 1. Acceleration high enough to trigger ringing (e.g., 2000 mm/s²). 2. Speed high enough to trigger ringing (e.g., 100 mm/s). 3. Jerk - 1. If using a [Classic Jerk](https://marlinfw.org/docs/configuration/configuration.html#jerk-) or [Klipper Square Corner Velocity](https://www.klipper3d.org/Kinematics.html?h=square+corner+velocity#look-ahead) use a high value (e.g., 20). - 2. If using [Junction Deviation](https://marlinfw.org/docs/features/junction_deviation.html) this test will use 0.25 (high enough to most printers). + 1. If using [Classic Jerk](https://marlinfw.org/docs/configuration/configuration.html#jerk-) use a high value (e.g., 20). + 2. If using [Junction Deviation](https://marlinfw.org/docs/features/junction_deviation.html) (new Marlin default mode) this test will use 0.25 (high enough to most printers). 2. Use a high gloss filament to make the ringing more visible. - 3. In printer settigs: - 1. Klipper: - 1. Disable [Minimun Cruise Ratio](https://www.klipper3d.org/Kinematics.html#minimum-cruise-ratio) with: - ```gcode - SET_VELOCITY_LIMIT MINIMUM_CRUISE_RATIO=0 - ``` -2. You need to print the Input SHaping Frequency test. +2. Print the Input Shaping Frequency test with a range of frequencies. 1. Measure the X and Y heights and read the frequency set at that point in Orca Slicer. 2. If not a clear result, you can measure a X and Y min and max acceptable heights and repeat the test with that min and max value. -3. When you find the best X and Y frequency, its time to Damp test setting your X and Y frequency to the value you found in the previous step. + + **Note**: There is a chance you will need to set higher than 60Hz frequencies. Some printers with very rigid frames and excellent mechanics may exhibit frequencies exceeding 100Hz. +3. Print the Damping test setting your X and Y frequency to the value you found in the previous step. + 1. Measure the X and Y heights and read the damping set at that point in Orca Slicer. 4. Restore your 3D Printer settings to avoid keep using high acceleration and jerk values. 1. Reboot your printer. 2. Use the following G-code to restore your printer settings: @@ -203,11 +239,11 @@ ZV Input Shaping introduces an anti-vibration signal into the stepper motion for M500 ``` -### Fixed-Time Motion +#### Fixed-Time Motion TODO This calibration test is currently under development. -## Junction Deviation +### Junction Deviation Junction Deviation is the default method for controlling cornering speed in MarlinFW printers. Higher values result in more aggressive cornering speeds, while lower values produce smoother, more controlled cornering. diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 8f5b4b4c0c..8bdecb8190 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -345,27 +345,43 @@ std::string GCodeWriter::set_pressure_advance(double pa) const std::string GCodeWriter::set_input_shaping(char axis, float damp, float freq) const { - std::ostringstream gcode; - if (FLAVOR_IS(gcfKlipper)) - { - throw std::runtime_error("M593 - ZV Input Shaping is NOT supported by Klipper"); - } 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)); } - gcode << "M593"; - if (axis != 'A') - { - gcode << " " << axis; - } - if (freq > 0.0f) - { - gcode << " F" << std::fixed << std::setprecision(2) << freq; - } - if (damp > 0.0f) - { - gcode << " D" << std::fixed << std::setprecision(2) << damp; + std::ostringstream gcode; + if (FLAVOR_IS(gcfKlipper)) { + gcode << "SET_INPUT_SHAPER"; + 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 << "=" << 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; + } + if (damp > 0.0f) { + gcode << " DAMPING_RATIO_X=" << damp << " DAMPING_RATIO_Y=" << damp; + } + } + } else { + gcode << "M593"; + if (axis != 'A') + { + gcode << " " << axis; + } + if (freq > 0.0f) + { + gcode << " F" << std::fixed << std::setprecision(2) << freq; + } + if (damp > 0.0f) + { + gcode << " D" << std::fixed << std::setprecision(2) << damp; + } } gcode << "; Override input shaping value\n"; return gcode.str(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 313809b56e..95ddb2faff 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -10167,8 +10167,6 @@ void Plater::calib_input_shaping_freq(const Calib_Params& params) filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats { 200 }); filament_config->set_key_value("enable_pressure_advance", new ConfigOptionBools {false }); filament_config->set_key_value("pressure_advance", new ConfigOptionFloats { 0.0 }); - print_config->set_key_value("default_jerk", new ConfigOptionFloat { 20.0 }); - print_config->set_key_value("outer_wall_jerk", new ConfigOptionFloat { 20.0 }); 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)); @@ -10208,8 +10206,6 @@ void Plater::calib_input_shaping_damp(const Calib_Params& params) filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats { 200 }); filament_config->set_key_value("enable_pressure_advance", new ConfigOptionBools{false}); filament_config->set_key_value("pressure_advance", new ConfigOptionFloats{0.0}); - print_config->set_key_value("default_jerk", new ConfigOptionFloat { 20.0 }); - print_config->set_key_value("outer_wall_jerk", new ConfigOptionFloat { 20.0 }); 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)); @@ -10249,8 +10245,6 @@ void Plater::calib_junction_deviation(const Calib_Params& params) filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats{200}); filament_config->set_key_value("enable_pressure_advance", new ConfigOptionBools{false}); filament_config->set_key_value("pressure_advance", new ConfigOptionFloats{0.0}); - print_config->set_key_value("default_jerk", new ConfigOptionFloat { 20.0 }); - print_config->set_key_value("outer_wall_jerk", new ConfigOptionFloat { 20.0 }); 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)); diff --git a/src/slic3r/GUI/calib_dlg.cpp b/src/slic3r/GUI/calib_dlg.cpp index 7ff4d9629b..edf76c99f1 100644 --- a/src/slic3r/GUI/calib_dlg.cpp +++ b/src/slic3r/GUI/calib_dlg.cpp @@ -863,8 +863,7 @@ Input_Shaping_Freq_Test_Dlg::Input_Shaping_Freq_Test_Dlg(wxWindow* parent, wxWin settings_sizer->Add(damping_factor_sizer); // Add a note explaining that 0 means use default value - auto note_text = new wxStaticText(this, wxID_ANY, _L("Note: 0 Damp = Printer default"), - wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + auto note_text = new wxStaticText(this, wxID_ANY, _L("Note: 0 Damp = Printer default."), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); note_text->SetForegroundColour(wxColour(128, 128, 128)); settings_sizer->Add(note_text, 0, wxALL, 5); @@ -997,8 +996,10 @@ Input_Shaping_Damp_Test_Dlg::Input_Shaping_Damp_Test_Dlg(wxWindow* parent, wxWin settings_sizer->Add(damp_sizer); // Add a note to explain users to use their previously calculated frequency - auto note_text = new wxStaticText(this, wxID_ANY, _L("Note: Set frequencies to the previously calculated values"), - wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + auto note_text = new wxStaticText(this, wxID_ANY, _L("Note: Use previously calculated frequencies."), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + note_text->SetForegroundColour(wxColour(128, 128, 128)); + settings_sizer->Add(note_text, 0, wxALL, 5); + note_text->SetForegroundColour(wxColour(128, 128, 128)); settings_sizer->Add(note_text, 0, wxALL, 5); From 40928a5d22d2f57a67860618b0153db91d45bdfd Mon Sep 17 00:00:00 2001 From: Ian Bassi Date: Thu, 3 Apr 2025 15:56:07 -0300 Subject: [PATCH 10/10] Switch to improve readability --- src/libslic3r/GCode.cpp | 96 ++++++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 39 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 6f90c67ef6..db803305fe 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3749,50 +3749,68 @@ LayerResult GCode::process_layer( //BBS: set layer time fan speed after layer change gcode gcode += ";_SET_FAN_SPEED_CHANGING_LAYER\n"; - if (print.calib_mode() == CalibMode::Calib_PA_Tower) { - gcode += writer().set_pressure_advance(print.calib_params().start + static_cast(print_z) * print.calib_params().step); - } else if (print.calib_mode() == CalibMode::Calib_Temp_Tower) { - auto offset = static_cast(print_z / 10.001) * 5; - gcode += writer().set_temperature(print.calib_params().start - offset); - } else if (print.calib_mode() == CalibMode::Calib_VFA_Tower) { - auto _speed = print.calib_params().start + std::floor(print_z / 5.0) * print.calib_params().step; - m_calib_config.set_key_value("outer_wall_speed", new ConfigOptionFloat(std::round(_speed))); - } else if (print.calib_mode() == CalibMode::Calib_Vol_speed_Tower) { - auto _speed = print.calib_params().start + print_z * print.calib_params().step; - m_calib_config.set_key_value("outer_wall_speed", new ConfigOptionFloat(std::round(_speed))); - } else if (print.calib_mode() == CalibMode::Calib_Retraction_tower) { - auto _length = print.calib_params().start + std::floor(std::max(0.0,print_z-0.4)) * print.calib_params().step; - DynamicConfig _cfg; - _cfg.set_key_value("retraction_length", new ConfigOptionFloats{_length}); - writer().config.apply(_cfg); - sprintf(buf, "; Calib_Retraction_tower: Z_HEIGHT: %g, length:%g\n", print_z, _length); - gcode += buf; - } else if (print.calib_mode() == CalibMode::Calib_Input_shaping_freq) { - if (m_layer_index == 1){ - if (print.config().gcode_flavor.value == gcfMarlinFirmware) { - gcode += writer().set_junction_deviation(0.25);//Set junction deviation at high value to maximize ringing. - } - gcode += writer().set_input_shaping('A', print.calib_params().start, 0.f); - } else { - if (print.calib_params().freqStartX == print.calib_params().freqStartY && print.calib_params().freqEndX == print.calib_params().freqEndY) { - gcode += writer().set_input_shaping('A', 0.f, (print.calib_params().freqStartX) + ((print.calib_params().freqEndX)-(print.calib_params().freqStartX)) * (m_layer_index - 2) / (m_layer_count - 3)); + //Calibration Layer-specific GCode + switch (print.calib_mode()) { + case CalibMode::Calib_PA_Tower: { + gcode += writer().set_pressure_advance(print.calib_params().start + static_cast(print_z) * print.calib_params().step); + break; + } + case CalibMode::Calib_Temp_Tower: { + auto offset = static_cast(print_z / 10.001) * 5; + gcode += writer().set_temperature(print.calib_params().start - offset); + break; + } + case CalibMode::Calib_VFA_Tower: { + auto _speed = print.calib_params().start + std::floor(print_z / 5.0) * print.calib_params().step; + m_calib_config.set_key_value("outer_wall_speed", new ConfigOptionFloat(std::round(_speed))); + break; + } + case CalibMode::Calib_Vol_speed_Tower: { + auto _speed = print.calib_params().start + print_z * print.calib_params().step; + m_calib_config.set_key_value("outer_wall_speed", new ConfigOptionFloat(std::round(_speed))); + break; + } + case CalibMode::Calib_Retraction_tower: { + auto _length = print.calib_params().start + std::floor(std::max(0.0,print_z-0.4)) * print.calib_params().step; + DynamicConfig _cfg; + _cfg.set_key_value("retraction_length", new ConfigOptionFloats{_length}); + writer().config.apply(_cfg); + sprintf(buf, "; Calib_Retraction_tower: Z_HEIGHT: %g, length:%g\n", print_z, _length); + gcode += buf; + break; + } + case CalibMode::Calib_Input_shaping_freq: { + if (m_layer_index == 1){ + if (print.config().gcode_flavor.value == gcfMarlinFirmware) { + gcode += writer().set_junction_deviation(0.25);//Set junction deviation at high value to maximize ringing. + } + gcode += writer().set_input_shaping('A', print.calib_params().start, 0.f); } else { - gcode += writer().set_input_shaping('X', 0.f, (print.calib_params().freqStartX) + ((print.calib_params().freqEndX)-(print.calib_params().freqStartX)) * (m_layer_index - 2) / (m_layer_count - 3)); - gcode += writer().set_input_shaping('Y', 0.f, (print.calib_params().freqStartY) + ((print.calib_params().freqEndY)-(print.calib_params().freqStartY)) * (m_layer_index - 2) / (m_layer_count - 3)); + if (print.calib_params().freqStartX == print.calib_params().freqStartY && print.calib_params().freqEndX == print.calib_params().freqEndY) { + gcode += writer().set_input_shaping('A', 0.f, (print.calib_params().freqStartX) + ((print.calib_params().freqEndX)-(print.calib_params().freqStartX)) * (m_layer_index - 2) / (m_layer_count - 3)); + } else { + gcode += writer().set_input_shaping('X', 0.f, (print.calib_params().freqStartX) + ((print.calib_params().freqEndX)-(print.calib_params().freqStartX)) * (m_layer_index - 2) / (m_layer_count - 3)); + gcode += writer().set_input_shaping('Y', 0.f, (print.calib_params().freqStartY) + ((print.calib_params().freqEndY)-(print.calib_params().freqStartY)) * (m_layer_index - 2) / (m_layer_count - 3)); + } } + break; } - } else if (print.calib_mode() == CalibMode::Calib_Input_shaping_damp) { - if (m_layer_index == 1){ - if (print.config().gcode_flavor.value == gcfMarlinFirmware) { - gcode += writer().set_junction_deviation(0.25); // Set junction deviation at high value to maximize ringing. + case CalibMode::Calib_Input_shaping_damp: { + if (m_layer_index == 1){ + if (print.config().gcode_flavor.value == gcfMarlinFirmware) { + gcode += writer().set_junction_deviation(0.25); // Set junction deviation at high value to maximize ringing. + } + gcode += writer().set_input_shaping('X', 0.f, print.calib_params().freqStartX); + gcode += writer().set_input_shaping('Y', 0.f, print.calib_params().freqStartY); + } else { + gcode += writer().set_input_shaping('A', print.calib_params().start + ((print.calib_params().end)-(print.calib_params().start)) * (m_layer_index) / (m_layer_count), 0.f); } - gcode += writer().set_input_shaping('X', 0.f, print.calib_params().freqStartX); - gcode += writer().set_input_shaping('Y', 0.f, print.calib_params().freqStartY); - } else { - gcode += writer().set_input_shaping('A', print.calib_params().start + ((print.calib_params().end)-(print.calib_params().start)) * (m_layer_index) / (m_layer_count), 0.f); + break; + } + case CalibMode::Calib_Junction_Deviation: { + gcode += writer().set_junction_deviation(print.calib_params().start + ((print.calib_params().end)-(print.calib_params().start)) * (m_layer_index) / (m_layer_count)); + break; } - } else if (print.calib_mode() == CalibMode::Calib_Junction_Deviation){ - gcode += writer().set_junction_deviation(print.calib_params().start + ((print.calib_params().end)-(print.calib_params().start)) * (m_layer_index) / (m_layer_count)); } //BBS