Files
OrcaSlicer-KX/src/slic3r/GUI/calib_dlg.cpp
packerlschupfer f760f4e462 Fix Flow Ratio dialog shrinking after main window minimize (#13545)
FlowRateCalibrationDialog was missing the SetSizeHints call that every
other calibration dialog in calib_dlg.cpp uses (Cornering, Pa, Pa Pattern,
Max Volumetric Speed, Temperature, Retraction, VFA, Input Shaping). Without
it, the dialog has no enforced minimum size: on wxGTK, iconizing the main
window while the dialog is open triggers a layout/refresh that re-runs
Fit() with transient zero-sized children, collapsing the dialog to ~222x249
px with the OK button clipped and unreachable. The window manager then
honors the (incorrect) small geometry hint, so the user cannot resize it
back manually.

Two changes:

* Add v_sizer->SetSizeHints(this) after Fit() in the constructor, matching
  the pattern used by all other dialogs in this file. This locks in the
  correct minimum the first time Fit() runs, before any iconize event.

* Remove the redundant Fit() from on_dpi_changed. With SetSizeHints in
  place the WM enforces the minimum, but the unconditional Fit() on every
  DPI/refresh signal was the trigger for the shrink path; Refresh() alone
  is sufficient here.

Co-authored-by: Packerlschupfer <packerl@schupfer.at>
2026-05-16 21:29:01 +08:00

1547 lines
67 KiB
C++

#include "calib_dlg.hpp"
#include "GUI_App.hpp"
#include "MsgDialog.hpp"
#include "I18N.hpp"
#include <wx/dcgraph.h>
#include "MainFrame.hpp"
#include "Widgets/DialogButtons.hpp"
#include "Widgets/HyperLink.hpp"
#include <string>
#include <vector>
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/Utils.hpp"
namespace Slic3r { namespace GUI {
std::vector<InputShaperType> input_shaper_types_for_flavor(GCodeFlavor flavor);
namespace {
void ParseStringValues(std::string str, std::vector<double> &vec)
{
vec.clear();
std::replace(str.begin(), str.end(), ',', ' ');
std::istringstream inss(str);
std::copy_if(std::istream_iterator<int>(inss), std::istream_iterator<int>(), std::back_inserter(vec),
[](int x){ return x > 0; });
}
int GetTextMax(wxWindow* parent, const std::vector<wxString>& labels)
{
wxSize text_size;
for (wxString label : labels)
text_size.IncTo(parent->GetTextExtent(label));
return text_size.x + parent->FromDIP(10);
}
std::vector<std::string> get_shaper_type_values()
{
if (auto* preset_bundle = wxGetApp().preset_bundle) {
auto printer_config = &preset_bundle->printers.get_edited_preset().config;
const auto* gcode_flavor_option = printer_config->option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor");
const ConfigOptionDef* def = printer_config->def()->get("input_shaping_type");
if (gcode_flavor_option) {
auto types = input_shaper_types_for_flavor(gcode_flavor_option->value);
if (!types.empty()) {
std::vector<std::string> values;
values.reserve(types.size());
for (InputShaperType type : types) {
if (type == InputShaperType::Disable)
continue;
const size_t idx = static_cast<size_t>(type);
if (def && idx < def->enum_values.size())
values.push_back(def->enum_values[idx]);
else
values.push_back(std::to_string(static_cast<int>(type)));
}
if (!values.empty())
return values;
}
}
if (def && !def->enum_values.empty())
return {def->enum_values.front()};
}
return {"Default"};
}
std::vector<wxString> make_shaper_type_labels()
{
auto values = get_shaper_type_values();
if (values.empty())
values.emplace_back("");
std::vector<wxString> labels;
labels.reserve(values.size());
for (const auto& label : values)
labels.emplace_back(wxString::FromUTF8(label.c_str()));
return labels;
}
}
PA_Calibration_Dlg::PA_Calibration_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
: DPIDialog(parent, id, _L("PA Calibration"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE), m_plater(plater)
{
SetBackgroundColour(*wxWHITE); // make sure background color set for dialog
SetForegroundColour(wxColour("#363636"));
SetFont(Label::Body_14);
wxBoxSizer* v_sizer = new wxBoxSizer(wxVERTICAL);
SetSizer(v_sizer);
// Extruder type Radio Group
auto labeled_box_type = new LabeledStaticBox(this, _L("Extruder type"));
auto type_box = new wxStaticBoxSizer(labeled_box_type, wxHORIZONTAL);
m_rbExtruderType = new RadioGroup(this, {_L("DDE"), _L("Bowden")}, wxHORIZONTAL);
type_box->Add(m_rbExtruderType, 0, wxALL | wxEXPAND, FromDIP(4));
v_sizer->Add(type_box, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
// Method Radio Group
auto labeled_box_method = new LabeledStaticBox(this, _L("Method"));
auto method_box = new wxStaticBoxSizer(labeled_box_method, wxHORIZONTAL);
m_rbMethod = new RadioGroup(this, { _L("PA Tower"), _L("PA Line"), _L("PA Pattern") }, wxHORIZONTAL);
method_box->Add(m_rbMethod, 0, wxALL | wxEXPAND, FromDIP(4));
v_sizer->Add(method_box, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
// Settings
wxString start_pa_str = _L("Start PA: ");
wxString end_pa_str = _L("End PA: ");
wxString PA_step_str = _L("PA step: ");
wxString sp_accel_str = _L("Accelerations: ");
wxString sp_speed_str = _L("Speeds: ");
wxString cb_print_no_str = _L("Print numbers");
int text_max = GetTextMax(this, std::vector<wxString>{start_pa_str, end_pa_str, PA_step_str, sp_accel_str, sp_speed_str, cb_print_no_str});
auto st_size = wxSize(text_max, -1);
auto ti_size = FromDIP(wxSize(120, -1));
LabeledStaticBox* stb = new LabeledStaticBox(this, _L("Settings"));
wxStaticBoxSizer* settings_sizer = new wxStaticBoxSizer(stb, wxVERTICAL);
settings_sizer->AddSpacer(FromDIP(5));
// start PA
auto start_PA_sizer = new wxBoxSizer(wxHORIZONTAL);
auto start_pa_text = new wxStaticText(this, wxID_ANY, start_pa_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
m_tiStartPA = new TextInput(this, "", "", "", wxDefaultPosition, ti_size, wxTE_PROCESS_ENTER);
m_tiStartPA->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
start_PA_sizer->Add(start_pa_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
start_PA_sizer->Add(m_tiStartPA , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(start_PA_sizer, 0, wxLEFT, FromDIP(3));
// end PA
auto end_PA_sizer = new wxBoxSizer(wxHORIZONTAL);
auto end_pa_text = new wxStaticText(this, wxID_ANY, end_pa_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
m_tiEndPA = new TextInput(this, "", "", "", wxDefaultPosition, ti_size, wxTE_PROCESS_ENTER);
m_tiEndPA->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
end_PA_sizer->Add(end_pa_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
end_PA_sizer->Add(m_tiEndPA , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(end_PA_sizer, 0, wxLEFT, FromDIP(3));
// PA step
auto PA_step_sizer = new wxBoxSizer(wxHORIZONTAL);
auto PA_step_text = new wxStaticText(this, wxID_ANY, PA_step_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
m_tiPAStep = new TextInput(this, "", "", "", wxDefaultPosition, ti_size, wxTE_PROCESS_ENTER);
m_tiPAStep->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
PA_step_sizer->Add(PA_step_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
PA_step_sizer->Add(m_tiPAStep , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(PA_step_sizer, 0, wxLEFT, FromDIP(3));
// Print Numbers
wxBoxSizer* cb_sizer = new wxBoxSizer(wxHORIZONTAL);
auto cb_title = new wxStaticText(this, wxID_ANY, cb_print_no_str, wxDefaultPosition, st_size, 0);
m_cbPrintNum = new CheckBox(this);
m_cbPrintNum->SetValue(false);
m_cbPrintNum->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent& e) {
(m_params.print_numbers) = (m_params.print_numbers) ? false : true;
e.Skip();
});
cb_sizer->Add(cb_title , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
cb_sizer->Add(m_cbPrintNum , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(cb_sizer, 0, wxLEFT | wxTOP | wxBOTTOM, FromDIP(3));
wxTextValidator val_list_validator(wxFILTER_INCLUDE_CHAR_LIST);
val_list_validator.SetCharIncludes(wxString("0123456789,"));
auto sp_accel_sizer = new wxBoxSizer(wxHORIZONTAL);
auto sp_accel_text = new wxStaticText(this, wxID_ANY, sp_accel_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
m_tiBMAccels = new TextInput(this, "", "", "", wxDefaultPosition, ti_size, wxTE_PROCESS_ENTER);
m_tiBMAccels->SetToolTip(_L("Comma-separated list of printing accelerations"));
m_tiBMAccels->GetTextCtrl()->SetValidator(val_list_validator);
sp_accel_sizer->Add(sp_accel_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
sp_accel_sizer->Add(m_tiBMAccels , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(sp_accel_sizer, 0, wxLEFT, FromDIP(3));
auto sp_speed_sizer = new wxBoxSizer(wxHORIZONTAL);
auto sp_speed_text = new wxStaticText(this, wxID_ANY, sp_speed_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
m_tiBMSpeeds = new TextInput(this, "", "", "", wxDefaultPosition, ti_size, wxTE_PROCESS_ENTER);
m_tiBMSpeeds->SetToolTip(_L("Comma-separated list of printing speeds"));
m_tiBMSpeeds->GetTextCtrl()->SetValidator(val_list_validator);
sp_speed_sizer->Add(sp_speed_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
sp_speed_sizer->Add(m_tiBMSpeeds , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(sp_speed_sizer, 0, wxLEFT, FromDIP(3));
v_sizer->Add(settings_sizer, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
v_sizer->AddSpacer(FromDIP(5));
auto dlg_btns = new DialogButtons(this, {"OK"});
auto bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
auto wiki = new HyperLink(this, _L("Wiki Guide"), "https://www.orcaslicer.com/wiki/pressure_advance_calib");
bottom_sizer->Add(wiki, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(20));
bottom_sizer->AddStretchSpacer();
bottom_sizer->Add(dlg_btns, 0, wxEXPAND);
v_sizer->Add(bottom_sizer, 0, wxEXPAND);
dlg_btns->GetOK()->Bind(wxEVT_BUTTON, &PA_Calibration_Dlg::on_start, this);
PA_Calibration_Dlg::reset_params();
// Connect Events
m_rbExtruderType->Connect(wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler(PA_Calibration_Dlg::on_extruder_type_changed), NULL, this);
m_rbMethod->Connect(wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler(PA_Calibration_Dlg::on_method_changed), NULL, this);
this->Connect(wxEVT_SHOW, wxShowEventHandler(PA_Calibration_Dlg::on_show));
wxGetApp().UpdateDlgDarkUI(this);
Layout();
Fit();
v_sizer->SetSizeHints(this);
}
PA_Calibration_Dlg::~PA_Calibration_Dlg() {
// Disconnect Events
m_rbExtruderType->Disconnect(wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler(PA_Calibration_Dlg::on_extruder_type_changed), NULL, this);
m_rbMethod->Disconnect(wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler(PA_Calibration_Dlg::on_method_changed), NULL, this);
}
void PA_Calibration_Dlg::reset_params() {
bool isDDE = m_rbExtruderType->GetSelection() == 0 ? true : false;
int method = m_rbMethod->GetSelection();
m_tiStartPA->GetTextCtrl()->SetValue(wxString::FromDouble(0.0));
switch (method) {
case 1:
m_params.mode = CalibMode::Calib_PA_Line;
m_tiEndPA->GetTextCtrl()->SetValue(wxString::FromDouble(0.1));
m_tiPAStep->GetTextCtrl()->SetValue(wxString::FromDouble(0.002));
m_cbPrintNum->SetValue(true);
m_cbPrintNum->Enable(true);
m_tiBMAccels->Enable(false);
m_tiBMSpeeds->Enable(false);
break;
case 2:
m_params.mode = CalibMode::Calib_PA_Pattern;
m_tiEndPA->GetTextCtrl()->SetValue(wxString::FromDouble(0.08));
m_tiPAStep->GetTextCtrl()->SetValue(wxString::FromDouble(0.005));
m_cbPrintNum->SetValue(true);
m_cbPrintNum->Enable(false);
m_tiBMAccels->Enable(true);
m_tiBMSpeeds->Enable(true);
break;
default:
m_params.mode = CalibMode::Calib_PA_Tower;
m_tiEndPA->GetTextCtrl()->SetValue(wxString::FromDouble(0.1));
m_tiPAStep->GetTextCtrl()->SetValue(wxString::FromDouble(0.002));
m_cbPrintNum->SetValue(false);
m_cbPrintNum->Enable(false);
m_tiBMAccels->Enable(false);
m_tiBMSpeeds->Enable(false);
break;
}
if (!isDDE) {
m_tiEndPA->GetTextCtrl()->SetValue(wxString::FromDouble(1.0));
if (m_params.mode == CalibMode::Calib_PA_Pattern) {
m_tiPAStep->GetTextCtrl()->SetValue(wxString::FromDouble(0.05));
} else {
m_tiPAStep->GetTextCtrl()->SetValue(wxString::FromDouble(0.02));
}
}
}
void PA_Calibration_Dlg::on_start(wxCommandEvent& event) {
bool read_double = false;
read_double = m_tiStartPA->GetTextCtrl()->GetValue().ToDouble(&m_params.start);
read_double = read_double && m_tiEndPA->GetTextCtrl()->GetValue().ToDouble(&m_params.end);
read_double = read_double && m_tiPAStep->GetTextCtrl()->GetValue().ToDouble(&m_params.step);
if (!read_double || m_params.start < 0 || m_params.step < 10 * EPSILON || m_params.end < m_params.start + m_params.step) {
MessageDialog msg_dlg(nullptr, _L("Please input valid values:\nStart PA: >= 0.0\nEnd PA: > Start PA\nPA step: >= 0.001"), wxEmptyString, wxICON_WARNING | wxOK);
msg_dlg.ShowModal();
return;
}
switch (m_rbMethod->GetSelection()) {
case 1:
m_params.mode = CalibMode::Calib_PA_Line;
break;
case 2:
m_params.mode = CalibMode::Calib_PA_Pattern;
break;
default:
m_params.mode = CalibMode::Calib_PA_Tower;
}
m_params.print_numbers = m_cbPrintNum->GetValue();
ParseStringValues(m_tiBMAccels->GetTextCtrl()->GetValue().ToStdString(), m_params.accelerations);
ParseStringValues(m_tiBMSpeeds->GetTextCtrl()->GetValue().ToStdString(), m_params.speeds);
if (!m_params.accelerations.empty() && !m_params.speeds.empty()) {
// Guard against swapped inputs by ensuring acceleration magnitudes exceed speeds.
const double min_accel = *std::min_element(m_params.accelerations.begin(), m_params.accelerations.end());
const double max_speed = *std::max_element(m_params.speeds.begin(), m_params.speeds.end());
if (min_accel <= max_speed) {
MessageDialog msg_dlg(nullptr,
_L("Acceleration values must be greater than speed values.\nPlease verify the inputs."),
wxEmptyString, wxICON_WARNING | wxOK);
msg_dlg.ShowModal();
return;
}
}
m_plater->calib_pa(m_params);
EndModal(wxID_OK);
}
void PA_Calibration_Dlg::on_extruder_type_changed(wxCommandEvent& event) {
PA_Calibration_Dlg::reset_params();
event.Skip();
}
void PA_Calibration_Dlg::on_method_changed(wxCommandEvent& event) {
PA_Calibration_Dlg::reset_params();
event.Skip();
}
void PA_Calibration_Dlg::on_dpi_changed(const wxRect& suggested_rect) {
this->Refresh();
Fit();
}
void PA_Calibration_Dlg::on_show(wxShowEvent& event) {
PA_Calibration_Dlg::reset_params();
}
// Temp calib dlg
//
enum FILAMENT_TYPE : int
{
tPLA = 0,
tABS_ASA,
tPETG,
tPCTG,
tTPU,
tTPU_AMS,
tPA_CF,
tPET_CF,
tCustom
};
Temp_Calibration_Dlg::Temp_Calibration_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
: DPIDialog(parent, id, _L("Temperature calibration"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE), m_plater(plater)
{
SetBackgroundColour(*wxWHITE); // make sure background color set for dialog
SetForegroundColour(wxColour("#363636"));
SetFont(Label::Body_14);
wxBoxSizer* v_sizer = new wxBoxSizer(wxVERTICAL);
SetSizer(v_sizer);
// Method Radio Group
auto labeled_box_method = new LabeledStaticBox(this, _L("Filament type"));
auto method_box = new wxStaticBoxSizer(labeled_box_method, wxHORIZONTAL);
m_rbFilamentType = new RadioGroup(this, { _L("PLA"), _L("ABS/ASA"), _L("PETG"), _L("PCTG"), _L("TPU"), _L("PA-CF"), _L("PET-CF"), _L("Custom") }, wxVERTICAL, 2);
method_box->Add(m_rbFilamentType, 0, wxALL | wxEXPAND, FromDIP(4));
v_sizer->Add(method_box, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
// Settings
wxString start_temp_str = _L("Start temp: ");
wxString end_temp_str = _L("End temp: ");
wxString temp_step_str = _L("Temp step: ");
int text_max = GetTextMax(this, std::vector<wxString>{start_temp_str, end_temp_str, temp_step_str});
auto st_size = wxSize(text_max, -1);
auto ti_size = FromDIP(wxSize(120, -1));
LabeledStaticBox* stb = new LabeledStaticBox(this, _L("Settings"));
wxStaticBoxSizer* settings_sizer = new wxStaticBoxSizer(stb, wxVERTICAL);
settings_sizer->AddSpacer(FromDIP(5));
// start temp
auto start_temp_sizer = new wxBoxSizer(wxHORIZONTAL);
auto start_temp_text = new wxStaticText(this, wxID_ANY, start_temp_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
m_tiStart = new TextInput(this, std::to_string(230), _L("\u2103" /* °C */), "", wxDefaultPosition, ti_size);
m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
start_temp_sizer->Add(start_temp_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
start_temp_sizer->Add(m_tiStart , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(start_temp_sizer, 0, wxLEFT, FromDIP(3));
// end temp
auto end_temp_sizer = new wxBoxSizer(wxHORIZONTAL);
auto end_temp_text = new wxStaticText(this, wxID_ANY, end_temp_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
m_tiEnd = new TextInput(this, std::to_string(190), _L("\u2103" /* °C */), "", wxDefaultPosition, ti_size);
m_tiEnd->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
end_temp_sizer->Add(end_temp_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
end_temp_sizer->Add(m_tiEnd , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(end_temp_sizer, 0, wxLEFT, FromDIP(3));
// temp step
auto temp_step_sizer = new wxBoxSizer(wxHORIZONTAL);
auto temp_step_text = new wxStaticText(this, wxID_ANY, temp_step_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
m_tiStep = new TextInput(this, wxString::FromDouble(5), _L("\u2103" /* °C */), "", wxDefaultPosition, ti_size);
m_tiStep->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
m_tiStep->Enable(false);
temp_step_sizer->Add(temp_step_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
temp_step_sizer->Add(m_tiStep , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(temp_step_sizer, 0, wxLEFT, FromDIP(3));
settings_sizer->AddSpacer(FromDIP(5));
v_sizer->Add(settings_sizer, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
v_sizer->AddSpacer(FromDIP(5));
auto dlg_btns = new DialogButtons(this, {"OK"});
auto bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
auto wiki = new HyperLink(this, _L("Wiki Guide"), "https://www.orcaslicer.com/wiki/temp_calib");
bottom_sizer->Add(wiki, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(20));
bottom_sizer->AddStretchSpacer();
bottom_sizer->Add(dlg_btns, 0, wxEXPAND);
v_sizer->Add(bottom_sizer, 0, wxEXPAND);
dlg_btns->GetOK()->Bind(wxEVT_BUTTON, &Temp_Calibration_Dlg::on_start, this);
m_rbFilamentType->Connect(wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler(Temp_Calibration_Dlg::on_filament_type_changed), NULL, this);
wxGetApp().UpdateDlgDarkUI(this);
Layout();
Fit();
v_sizer->SetSizeHints(this);
auto validate_text = [](TextInput* ti){
unsigned long t = 0;
if(!ti->GetTextCtrl()->GetValue().ToULong(&t))
return;
if(t> 500 || t < 155){
MessageDialog msg_dlg(nullptr, wxString::Format(L"Supported range: 170%s - 500%s",
_L("\u2103" /* °C */), _L("\u2103" /* °C */)),
wxEmptyString, wxICON_WARNING | wxOK);
msg_dlg.ShowModal();
if(t > 500)
t = 500;
else
t = 155;
}
t = (t / 5) * 5;
ti->GetTextCtrl()->SetValue(std::to_string(t));
};
m_tiStart->GetTextCtrl()->Bind(wxEVT_KILL_FOCUS, [&](wxFocusEvent &e) {
validate_text(this->m_tiStart);
e.Skip();
});
m_tiEnd->GetTextCtrl()->Bind(wxEVT_KILL_FOCUS, [&](wxFocusEvent &e) {
validate_text(this->m_tiEnd);
e.Skip();
});
}
Temp_Calibration_Dlg::~Temp_Calibration_Dlg() {
// Disconnect Events
m_rbFilamentType->Disconnect(wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler(Temp_Calibration_Dlg::on_filament_type_changed), NULL, this);
}
void Temp_Calibration_Dlg::on_start(wxCommandEvent& event) {
bool read_long = false;
unsigned long start=0,end=0;
read_long = m_tiStart->GetTextCtrl()->GetValue().ToULong(&start);
read_long = read_long && m_tiEnd->GetTextCtrl()->GetValue().ToULong(&end);
if (!read_long || start > 500 || end < 155 || end > (start - 5)) {
MessageDialog msg_dlg(nullptr, _L("Please input valid values:\nStart temp: <= 500\nEnd temp: >= 155\nStart temp >= End temp + 5"), wxEmptyString, wxICON_WARNING | wxOK);
msg_dlg.ShowModal();
return;
}
m_params.start = start;
m_params.end = end;
m_params.mode = CalibMode::Calib_Temp_Tower;
m_plater->calib_temp(m_params);
EndModal(wxID_OK);
}
void Temp_Calibration_Dlg::on_filament_type_changed(wxCommandEvent& event) {
int selection = event.GetSelection();
unsigned long start = 0, end = 0;
switch(selection)
{
case tABS_ASA:
start = 270;
end = 230;
break;
case tPETG:
start = 250;
end = 230;
break;
case tPCTG:
start = 280;
end = 240;
break;
case tTPU:
case tTPU_AMS:
start = 240;
end = 210;
break;
case tPA_CF:
start = 320;
end = 280;
break;
case tPET_CF:
start = 320;
end = 280;
break;
case tPLA:
case tCustom:
start = 230;
end = 190;
break;
}
m_tiEnd->GetTextCtrl()->SetValue(std::to_string(end));
m_tiStart->GetTextCtrl()->SetValue(std::to_string(start));
event.Skip();
}
void Temp_Calibration_Dlg::on_dpi_changed(const wxRect& suggested_rect) {
this->Refresh();
Fit();
}
// MaxVolumetricSpeed_Test_Dlg
//
MaxVolumetricSpeed_Test_Dlg::MaxVolumetricSpeed_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
: DPIDialog(parent, id, _L("Max volumetric speed test"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE), m_plater(plater)
{
SetBackgroundColour(*wxWHITE); // make sure background color set for dialog
SetForegroundColour(wxColour("#363636"));
SetFont(Label::Body_14);
wxBoxSizer* v_sizer = new wxBoxSizer(wxVERTICAL);
SetSizer(v_sizer);
// Settings
wxString start_vol_str = _L("Start volumetric speed: ");
wxString end_vol_str = _L("End volumetric speed: ");
wxString vol_step_str = _L("Step") + ": ";
int text_max = GetTextMax(this, std::vector<wxString>{start_vol_str, end_vol_str, vol_step_str});
auto st_size = wxSize(text_max, -1);
auto ti_size = FromDIP(wxSize(120, -1));
LabeledStaticBox* stb = new LabeledStaticBox(this, _L("Settings"));
wxStaticBoxSizer* settings_sizer = new wxStaticBoxSizer(stb, wxVERTICAL);
settings_sizer->AddSpacer(FromDIP(5));
// start vol
auto start_vol_sizer = new wxBoxSizer(wxHORIZONTAL);
auto start_vol_text = new wxStaticText(this, wxID_ANY, start_vol_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
m_tiStart = new TextInput(this, std::to_string(5), _L(u8"mm³/s"), "", wxDefaultPosition, ti_size);
m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
start_vol_sizer->Add(start_vol_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
start_vol_sizer->Add(m_tiStart , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(start_vol_sizer, 0, wxLEFT, FromDIP(3));
// end vol
auto end_vol_sizer = new wxBoxSizer(wxHORIZONTAL);
auto end_vol_text = new wxStaticText(this, wxID_ANY, end_vol_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
m_tiEnd = new TextInput(this, std::to_string(20), _L(u8"mm³/s"), "", wxDefaultPosition, ti_size);
m_tiEnd->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
end_vol_sizer->Add(end_vol_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
end_vol_sizer->Add(m_tiEnd , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(end_vol_sizer, 0, wxLEFT, FromDIP(3));
// vol step
auto vol_step_sizer = new wxBoxSizer(wxHORIZONTAL);
auto vol_step_text = new wxStaticText(this, wxID_ANY, vol_step_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
m_tiStep = new TextInput(this, wxString::FromDouble(0.5), _L(u8"mm³/s"), "", wxDefaultPosition, ti_size);
m_tiStep->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
vol_step_sizer->Add(vol_step_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
vol_step_sizer->Add(m_tiStep , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(vol_step_sizer, 0, wxLEFT, FromDIP(3));
v_sizer->Add(settings_sizer, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
v_sizer->AddSpacer(FromDIP(5));
auto dlg_btns = new DialogButtons(this, {"OK"});
auto bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
auto wiki = new HyperLink(this, _L("Wiki Guide"), "https://www.orcaslicer.com/wiki/volumetric_speed_calib");
bottom_sizer->Add(wiki, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(20));
bottom_sizer->AddStretchSpacer();
bottom_sizer->Add(dlg_btns, 0, wxEXPAND);
v_sizer->Add(bottom_sizer, 0, wxEXPAND);
dlg_btns->GetOK()->Bind(wxEVT_BUTTON, &MaxVolumetricSpeed_Test_Dlg::on_start, this);
wxGetApp().UpdateDlgDarkUI(this);
Layout();
Fit();
v_sizer->SetSizeHints(this);
}
MaxVolumetricSpeed_Test_Dlg::~MaxVolumetricSpeed_Test_Dlg() {
// Disconnect Events
}
void MaxVolumetricSpeed_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 = read_double && m_tiStep->GetTextCtrl()->GetValue().ToDouble(&m_params.step);
if (!read_double || m_params.start <= 0 || m_params.step <= 0 || m_params.end < (m_params.start + m_params.step)) {
MessageDialog msg_dlg(nullptr, _L("Please input valid values:\nstart > 0\nstep >= 0\nend > start + step"), wxEmptyString, wxICON_WARNING | wxOK);
msg_dlg.ShowModal();
return;
}
m_params.mode = CalibMode::Calib_Vol_speed_Tower;
m_plater->calib_max_vol_speed(m_params);
EndModal(wxID_OK);
}
void MaxVolumetricSpeed_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect) {
this->Refresh();
Fit();
}
// VFA_Test_Dlg
//
VFA_Test_Dlg::VFA_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
: DPIDialog(parent, id, _L("VFA test"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
, m_plater(plater)
{
SetBackgroundColour(*wxWHITE); // make sure background color set for dialog
SetForegroundColour(wxColour("#363636"));
SetFont(Label::Body_14);
wxBoxSizer* v_sizer = new wxBoxSizer(wxVERTICAL);
SetSizer(v_sizer);
// Settings
wxString start_str = _L("Start speed: ");
wxString end_vol_str = _L("End speed: ");
wxString vol_step_str = _L("Step") + ": ";
int text_max = GetTextMax(this, std::vector<wxString>{start_str, end_vol_str, vol_step_str});
auto st_size = wxSize(text_max, -1);
auto ti_size = FromDIP(wxSize(120, -1));
LabeledStaticBox* stb = new LabeledStaticBox(this, _L("Settings"));
wxStaticBoxSizer* settings_sizer = new wxStaticBoxSizer(stb, wxVERTICAL);
settings_sizer->AddSpacer(FromDIP(5));
// start vol
auto start_vol_sizer = new wxBoxSizer(wxHORIZONTAL);
auto start_vol_text = new wxStaticText(this, wxID_ANY, start_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
m_tiStart = new TextInput(this, std::to_string(40), _L("mm/s"), "", wxDefaultPosition, ti_size);
m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
start_vol_sizer->Add(start_vol_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
start_vol_sizer->Add(m_tiStart , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(start_vol_sizer, 0, wxLEFT, FromDIP(3));
// end vol
auto end_vol_sizer = new wxBoxSizer(wxHORIZONTAL);
auto end_vol_text = new wxStaticText(this, wxID_ANY, end_vol_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
m_tiEnd = new TextInput(this, std::to_string(200), _L("mm/s"), "", wxDefaultPosition, ti_size);
m_tiEnd->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
end_vol_sizer->Add(end_vol_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
end_vol_sizer->Add(m_tiEnd , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(end_vol_sizer, 0, wxLEFT, FromDIP(3));
// vol step
auto vol_step_sizer = new wxBoxSizer(wxHORIZONTAL);
auto vol_step_text = new wxStaticText(this, wxID_ANY, vol_step_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
m_tiStep = new TextInput(this, wxString::FromDouble(10), _L("mm/s"), "", wxDefaultPosition, ti_size);
m_tiStep->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
vol_step_sizer->Add(vol_step_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
vol_step_sizer->Add(m_tiStep , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(vol_step_sizer, 0, wxLEFT, FromDIP(3));
settings_sizer->AddSpacer(FromDIP(5));
v_sizer->Add(settings_sizer, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
v_sizer->AddSpacer(FromDIP(5));
auto dlg_btns = new DialogButtons(this, {"OK"});
auto bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
auto wiki = new HyperLink(this, _L("Wiki Guide"), "https://www.orcaslicer.com/wiki/vfa_calib");
bottom_sizer->Add(wiki, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(20));
bottom_sizer->AddStretchSpacer();
bottom_sizer->Add(dlg_btns, 0, wxEXPAND);
v_sizer->Add(bottom_sizer, 0, wxEXPAND);
dlg_btns->GetOK()->Bind(wxEVT_BUTTON, &VFA_Test_Dlg::on_start, this);
wxGetApp().UpdateDlgDarkUI(this);
Layout();
Fit();
v_sizer->SetSizeHints(this);
}
VFA_Test_Dlg::~VFA_Test_Dlg()
{
// Disconnect Events
}
void VFA_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 = read_double && m_tiStep->GetTextCtrl()->GetValue().ToDouble(&m_params.step);
if (!read_double || m_params.start <= 10 || m_params.step <= 0 || m_params.end < (m_params.start + m_params.step)) {
MessageDialog msg_dlg(nullptr, _L("Please input valid values:\nstart > 10\nstep >= 0\nend > start + step"), wxEmptyString, wxICON_WARNING | wxOK);
msg_dlg.ShowModal();
return;
}
m_params.mode = CalibMode::Calib_VFA_Tower;
m_plater->calib_VFA(m_params);
EndModal(wxID_OK);
}
void VFA_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect)
{
this->Refresh();
Fit();
}
// Retraction_Test_Dlg
//
Retraction_Test_Dlg::Retraction_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
: DPIDialog(parent, id, _L("Retraction"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE), m_plater(plater)
{
SetBackgroundColour(*wxWHITE); // make sure background color set for dialog
SetForegroundColour(wxColour("#363636"));
SetFont(Label::Body_14);
wxBoxSizer* v_sizer = new wxBoxSizer(wxVERTICAL);
SetSizer(v_sizer);
// Settings
wxString start_length_str = _L("Start retraction length: ");
wxString end_length_str = _L("End retraction length: ");
wxString length_step_str = _L("Step") + ": ";
int text_max = GetTextMax(this, std::vector<wxString>{start_length_str, end_length_str, length_step_str});
auto st_size = wxSize(text_max, -1);
auto ti_size = FromDIP(wxSize(120, -1));
LabeledStaticBox* stb = new LabeledStaticBox(this, _L("Settings"));
wxStaticBoxSizer* settings_sizer = new wxStaticBoxSizer(stb, wxVERTICAL);
settings_sizer->AddSpacer(FromDIP(5));
// start length
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(0), _L("mm"), "", wxDefaultPosition, ti_size);
m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
start_length_sizer->Add(start_length_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
start_length_sizer->Add(m_tiStart , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(start_length_sizer, 0, wxLEFT, FromDIP(3));
// end length
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);
m_tiEnd->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
end_length_sizer->Add(end_length_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
end_length_sizer->Add(m_tiEnd , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(end_length_sizer, 0, wxLEFT, FromDIP(3));
// length step
auto length_step_sizer = new wxBoxSizer(wxHORIZONTAL);
auto length_step_text = new wxStaticText(this, wxID_ANY, length_step_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
m_tiStep = new TextInput(this, wxString::FromDouble(0.1), _L("mm"), "", wxDefaultPosition, ti_size);
m_tiStep->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
length_step_sizer->Add(length_step_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
length_step_sizer->Add(m_tiStep , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(length_step_sizer, 0, wxLEFT, FromDIP(3));
settings_sizer->AddSpacer(FromDIP(5));
v_sizer->Add(settings_sizer, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
v_sizer->AddSpacer(FromDIP(5));
auto dlg_btns = new DialogButtons(this, {"OK"});
auto bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
auto wiki = new HyperLink(this, _L("Wiki Guide"), "https://www.orcaslicer.com/wiki/retraction_calib");
bottom_sizer->Add(wiki, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(20));
bottom_sizer->AddStretchSpacer();
bottom_sizer->Add(dlg_btns, 0, wxEXPAND);
v_sizer->Add(bottom_sizer, 0, wxEXPAND);
dlg_btns->GetOK()->Bind(wxEVT_BUTTON, &Retraction_Test_Dlg::on_start, this);
wxGetApp().UpdateDlgDarkUI(this);
Layout();
Fit();
v_sizer->SetSizeHints(this);
}
Retraction_Test_Dlg::~Retraction_Test_Dlg() {
// Disconnect Events
}
void Retraction_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 = read_double && m_tiStep->GetTextCtrl()->GetValue().ToDouble(&m_params.step);
if (!read_double || m_params.start < 0 || m_params.step <= 0 || m_params.end < (m_params.start + m_params.step)) {
MessageDialog msg_dlg(nullptr, _L("Please input valid values:\nstart > 0\nstep >= 0\nend > start + step"), wxEmptyString, wxICON_WARNING | wxOK);
msg_dlg.ShowModal();
return;
}
m_params.mode = CalibMode::Calib_Retraction_tower;
m_plater->calib_retraction(m_params);
EndModal(wxID_OK);
}
void Retraction_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect) {
this->Refresh();
Fit();
}
// Input_Shaping_Freq_Test_Dlg
//
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, wxDefaultSize, wxDEFAULT_DIALOG_STYLE), m_plater(plater)
{
SetBackgroundColour(*wxWHITE); // make sure background color set for dialog
SetForegroundColour(wxColour("#363636"));
SetFont(Label::Body_14);
const auto* preset_bundle = wxGetApp().preset_bundle;
const auto* gcode_flavor_option = (preset_bundle != nullptr)
? preset_bundle->printers.get_edited_preset().config.option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")
: nullptr;
const bool reprap_firmware = gcode_flavor_option && gcode_flavor_option->value == GCodeFlavor::gcfRepRapFirmware;
wxBoxSizer* v_sizer = new wxBoxSizer(wxVERTICAL);
SetSizer(v_sizer);
// Model selection
auto labeled_box_model = new LabeledStaticBox(this, _L("Test model"));
auto model_box = new wxStaticBoxSizer(labeled_box_model, wxHORIZONTAL);
m_rbModel = new RadioGroup(this, { _L("Ringing Tower"), _L("Fast Tower") }, wxVERTICAL);
model_box->Add(m_rbModel, 0, wxALL | wxEXPAND, FromDIP(4));
v_sizer->Add(model_box, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
// Input shaper type selection
auto labeled_box_type = new LabeledStaticBox(this, _L("Input shaper type"));
auto type_box = new wxStaticBoxSizer(labeled_box_type, wxVERTICAL);
auto type_labels = make_shaper_type_labels();
m_rbType = new RadioGroup(this, type_labels, wxVERTICAL, 3);
type_box->Add(m_rbType, 0, wxALL | wxEXPAND, FromDIP(4));
m_rbType->SetSelection(0);
// Determine firmware-specific note
wxString firmware_note = _L("Please ensure the selected type is compatible with your firmware version.");
if (gcode_flavor_option) {
switch (gcode_flavor_option->value) {
case GCodeFlavor::gcfMarlinFirmware:
case GCodeFlavor::gcfMarlinLegacy:
firmware_note = _L("Marlin version => 2.1.2\nFixed-Time motion not yet implemented.");
break;
case GCodeFlavor::gcfKlipper:
firmware_note = _L("Klipper version => 0.9.0");
break;
case GCodeFlavor::gcfRepRapFirmware:
firmware_note = _L("RepRap firmware version => 3.4.0\nCheck your firmware documentation for supported shaper types.");
break;
default:
break;
}
}
auto type_note = new wxStaticText(this, wxID_ANY, firmware_note, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
type_note->SetForegroundColour(wxColour(128, 128, 128));
type_box->Add(type_note, 0, wxALL, FromDIP(5));
v_sizer->Add(type_box, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
// Settings
wxString x_axis_str = reprap_firmware ? _L("Frequency (Start / End): ") : "X " + _L("Start / End") + ": ";
wxString y_axis_str = "Y " + _L("Start / End") + ": ";
int text_max = GetTextMax(this, std::vector<wxString>{x_axis_str, y_axis_str});
auto st_size = wxSize(text_max, -1);
auto ti_size = FromDIP(wxSize(120, -1));
LabeledStaticBox* stb = new LabeledStaticBox(this, _L("Frequency settings"));
wxStaticBoxSizer* settings_sizer = new wxStaticBoxSizer(stb, wxVERTICAL);
settings_sizer->AddSpacer(FromDIP(5));
// X axis frequencies
auto x_freq_sizer = new wxBoxSizer(wxHORIZONTAL);
auto start_x_text = new wxStaticText(this, wxID_ANY, x_axis_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
m_tiFreqStartX = new TextInput(this, std::to_string(15) , _L("Hz"), "", wxDefaultPosition, ti_size);
m_tiFreqStartX->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
m_tiFreqEndX = new TextInput(this, std::to_string(110), _L("Hz"), "", wxDefaultPosition, ti_size);
m_tiFreqEndX->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
x_freq_sizer->Add(start_x_text , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
x_freq_sizer->Add(m_tiFreqStartX, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
x_freq_sizer->Add(m_tiFreqEndX , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(x_freq_sizer, 0, wxLEFT, FromDIP(3));
// Y axis frequencies
auto y_freq_sizer = new wxBoxSizer(wxHORIZONTAL);
auto start_y_text = new wxStaticText(this, wxID_ANY, y_axis_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
m_tiFreqStartY = new TextInput(this, std::to_string(15) , _L("Hz"), "", wxDefaultPosition, ti_size);
m_tiFreqStartY->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
m_tiFreqEndY = new TextInput(this, std::to_string(110), _L("Hz"), "", wxDefaultPosition, ti_size);
m_tiFreqEndY->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
y_freq_sizer->Add(start_y_text , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
y_freq_sizer->Add(m_tiFreqStartY, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
y_freq_sizer->Add(m_tiFreqEndY , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(y_freq_sizer, 0, wxLEFT, FromDIP(3));
if (reprap_firmware) {
m_tiFreqStartY->GetTextCtrl()->SetValue(m_tiFreqStartX->GetTextCtrl()->GetValue());
m_tiFreqEndY->GetTextCtrl()->SetValue(m_tiFreqEndX->GetTextCtrl()->GetValue());
start_y_text->Hide();
m_tiFreqStartY->Hide();
m_tiFreqEndY->Hide();
settings_sizer->Hide(y_freq_sizer);
start_x_text->SetLabel(_L("Frequency (Start / End): "));
m_tiFreqStartX->GetTextCtrl()->SetToolTip(_L("RepRap firmware uses the same frequency range for both axes."));
m_tiFreqEndX->GetTextCtrl()->SetToolTip(_L("RepRap firmware uses the same frequency range for both axes."));
}
// 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("%.3f", 0.15), "", "", wxDefaultPosition, ti_size);
m_tiDampingFactor->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
damping_factor_sizer->Add(damping_factor_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
damping_factor_sizer->Add(m_tiDampingFactor , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(damping_factor_sizer, 0, wxLEFT, FromDIP(3));
settings_sizer->AddSpacer(FromDIP(5));
auto note_text = new wxStaticText(this, wxID_ANY, _L("Recommended: Set Damp to 0.\nThis will use the printer's default or saved value."), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
note_text->SetForegroundColour(wxColour(128, 128, 128));
note_text->Wrap(FromDIP(350));
settings_sizer->Add(note_text, 0, wxALL, FromDIP(5));
settings_sizer->AddSpacer(FromDIP(5));
v_sizer->Add(settings_sizer, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
v_sizer->AddSpacer(FromDIP(5));
auto dlg_btns = new DialogButtons(this, {"OK"});
auto bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
auto wiki = new HyperLink(this, _L("Wiki Guide"), "https://www.orcaslicer.com/wiki/input_shaping_calib");
bottom_sizer->Add(wiki, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(20));
bottom_sizer->AddStretchSpacer();
bottom_sizer->Add(dlg_btns, 0, wxEXPAND);
v_sizer->Add(bottom_sizer, 0, wxEXPAND);
dlg_btns->GetOK()->Bind(wxEVT_BUTTON, &Input_Shaping_Freq_Test_Dlg::on_start, this);
wxGetApp().UpdateDlgDarkUI(this);
Layout();
Fit();
v_sizer->SetSizeHints(this);
}
Input_Shaping_Freq_Test_Dlg::~Input_Shaping_Freq_Test_Dlg() {
// Disconnect Events
}
void Input_Shaping_Freq_Test_Dlg::on_start(wxCommandEvent& event) {
bool read_double = false;
read_double = m_tiFreqStartX->GetTextCtrl()->GetValue().ToDouble(&m_params.freqStartX);
read_double = read_double && m_tiFreqEndX->GetTextCtrl()->GetValue().ToDouble(&m_params.freqEndX);
const auto* preset_bundle = wxGetApp().preset_bundle;
const auto* gcode_flavor_option = (preset_bundle != nullptr)
? preset_bundle->printers.get_edited_preset().config.option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")
: nullptr;
const bool reprap_firmware = gcode_flavor_option && gcode_flavor_option->value == GCodeFlavor::gcfRepRapFirmware;
if (!reprap_firmware) {
read_double = read_double && m_tiFreqStartY->GetTextCtrl()->GetValue().ToDouble(&m_params.freqStartY);
read_double = read_double && m_tiFreqEndY->GetTextCtrl()->GetValue().ToDouble(&m_params.freqEndY);
} else {
m_params.freqStartY = m_params.freqStartX;
m_params.freqEndY = m_params.freqEndX;
m_tiFreqStartY->GetTextCtrl()->SetValue(m_tiFreqStartX->GetTextCtrl()->GetValue());
m_tiFreqEndY->GetTextCtrl()->SetValue(m_tiFreqEndX->GetTextCtrl()->GetValue());
}
read_double = read_double && m_tiDampingFactor->GetTextCtrl()->GetValue().ToDouble(&m_params.start);
if (!read_double ||
m_params.freqStartX < 0 || m_params.freqEndX > 500 ||
(!reprap_firmware && (m_params.freqStartY < 0 || m_params.freqEndY > 500)) ||
m_params.freqStartX >= m_params.freqEndX ||
(!reprap_firmware && 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 (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;
}
auto shaper_values = get_shaper_type_values();
int type_selection = m_rbType->GetSelection();
if (shaper_values.empty()) {
m_params.shaper_type.clear();
} else {
if (type_selection < 0 || type_selection >= static_cast<int>(shaper_values.size()))
type_selection = 0;
m_params.shaper_type = shaper_values[static_cast<size_t>(type_selection)];
}
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);
}
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, wxDefaultSize, wxDEFAULT_DIALOG_STYLE), m_plater(plater)
{
SetBackgroundColour(*wxWHITE); // make sure background color set for dialog
SetForegroundColour(wxColour("#363636"));
SetFont(Label::Body_14);
const auto* preset_bundle = wxGetApp().preset_bundle;
const auto* gcode_flavor_option = (preset_bundle != nullptr)
? preset_bundle->printers.get_edited_preset().config.option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")
: nullptr;
const bool reprap_firmware = gcode_flavor_option && gcode_flavor_option->value == GCodeFlavor::gcfRepRapFirmware;
wxBoxSizer* v_sizer = new wxBoxSizer(wxVERTICAL);
SetSizer(v_sizer);
// Model selection
auto labeled_box_model = new LabeledStaticBox(this, _L("Test model"));
auto model_box = new wxStaticBoxSizer(labeled_box_model, wxHORIZONTAL);
m_rbModel = new RadioGroup(this, { _L("Ringing Tower"), _L("Fast Tower") }, wxVERTICAL);
model_box->Add(m_rbModel, 0, wxALL | wxEXPAND, FromDIP(4));
v_sizer->Add(model_box, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
// Input shaper type selection
auto labeled_box_type = new LabeledStaticBox(this, _L("Input shaper type"));
auto type_box = new wxStaticBoxSizer(labeled_box_type, wxVERTICAL);
auto type_labels = make_shaper_type_labels();
m_rbType = new RadioGroup(this, type_labels, wxVERTICAL, 3);
type_box->Add(m_rbType, 0, wxALL | wxEXPAND, FromDIP(4));
m_rbType->SetSelection(0);
// Determine firmware-specific note
wxString firmware_note = _L("Check firmware compatibility.");
if (gcode_flavor_option) {
switch (gcode_flavor_option->value) {
case GCodeFlavor::gcfMarlinFirmware:
case GCodeFlavor::gcfMarlinLegacy:
firmware_note = _L("Marlin version => 2.1.2\nFixed-Time motion not yet implemented.");
break;
case GCodeFlavor::gcfKlipper:
firmware_note = _L("Klipper version => 0.9.0");
break;
case GCodeFlavor::gcfRepRapFirmware:
firmware_note = _L("RepRap firmware version => 3.4.0\nCheck your firmware documentation for supported shaper types.");
break;
default:
break;
}
}
auto type_note = new wxStaticText(this, wxID_ANY, firmware_note, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
type_note->SetForegroundColour(wxColour(128, 128, 128));
type_box->Add(type_note, 0, wxALL, FromDIP(5));
v_sizer->Add(type_box, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
// Settings
wxString freq_str = reprap_firmware ? _L("Frequency: ") : _L("Frequency") + " X / Y: ";
wxString damp_str = _L("Damp") + " " + _L("Start / End") + ": ";
int text_max = GetTextMax(this, std::vector<wxString>{freq_str, damp_str});
auto st_size = wxSize(text_max, -1);
auto ti_size = FromDIP(wxSize(120, -1));
LabeledStaticBox* stb = new LabeledStaticBox(this, _L("Frequency settings"));
wxStaticBoxSizer* settings_sizer = new wxStaticBoxSizer(stb, wxVERTICAL);
settings_sizer->AddSpacer(FromDIP(5));
auto freq_sizer = new wxBoxSizer(wxHORIZONTAL);
auto freq_text = new wxStaticText(this, wxID_ANY, freq_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
m_tiFreqX = new TextInput(this, std::to_string(30), _L("Hz"), "", wxDefaultPosition, ti_size);
m_tiFreqX->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
m_tiFreqY = new TextInput(this, std::to_string(30), _L("Hz"), "", wxDefaultPosition, ti_size);
m_tiFreqY->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
freq_sizer->Add(freq_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
freq_sizer->Add(m_tiFreqX, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
freq_sizer->Add(m_tiFreqY, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(freq_sizer, 0, wxLEFT, FromDIP(3));
if (reprap_firmware) {
m_tiFreqY->GetTextCtrl()->SetValue(m_tiFreqX->GetTextCtrl()->GetValue());
m_tiFreqY->Hide();
freq_text->SetLabel(freq_str);
m_tiFreqX->GetTextCtrl()->SetToolTip(_L("RepRap firmware uses the same frequency for both axes."));
}
// Damping Factor Start and End
auto damp_sizer = new wxBoxSizer(wxHORIZONTAL);
auto damp_text = new wxStaticText(this, wxID_ANY, damp_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
m_tiDampingFactorStart = new TextInput(this, wxString::Format("%.3f", 0.00), "", "", wxDefaultPosition, ti_size);
m_tiDampingFactorStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
m_tiDampingFactorEnd = new TextInput(this, wxString::Format("%.3f", 0.40), "", "", wxDefaultPosition, ti_size);
m_tiDampingFactorEnd->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
damp_sizer->Add(damp_text , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
damp_sizer->Add(m_tiDampingFactorStart, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
damp_sizer->Add(m_tiDampingFactorEnd , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(damp_sizer, 0, wxLEFT, FromDIP(3));
settings_sizer->AddSpacer(FromDIP(5));
// Add a note to explain users to use their previously calculated frequency
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, FromDIP(5));
settings_sizer->AddSpacer(FromDIP(5));
v_sizer->Add(settings_sizer, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
v_sizer->AddSpacer(FromDIP(5));
auto dlg_btns = new DialogButtons(this, {"OK"});
auto bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
auto wiki = new HyperLink(this, _L("Wiki Guide"), "https://www.orcaslicer.com/wiki/input_shaping_calib");
bottom_sizer->Add(wiki, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(20));
bottom_sizer->AddStretchSpacer();
bottom_sizer->Add(dlg_btns, 0, wxEXPAND);
v_sizer->Add(bottom_sizer, 0, wxEXPAND);
dlg_btns->GetOK()->Bind(wxEVT_BUTTON, &Input_Shaping_Damp_Test_Dlg::on_start, this);
wxGetApp().UpdateDlgDarkUI(this);
Layout();
Fit();
v_sizer->SetSizeHints(this);
}
Input_Shaping_Damp_Test_Dlg::~Input_Shaping_Damp_Test_Dlg() {
// Disconnect Events
}
void Input_Shaping_Damp_Test_Dlg::on_start(wxCommandEvent& event) {
bool read_double = false;
read_double = m_tiFreqX->GetTextCtrl()->GetValue().ToDouble(&m_params.freqStartX);
const auto* preset_bundle = wxGetApp().preset_bundle;
const auto* gcode_flavor_option = (preset_bundle != nullptr)
? preset_bundle->printers.get_edited_preset().config.option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")
: nullptr;
const bool reprap_firmware = gcode_flavor_option && gcode_flavor_option->value == GCodeFlavor::gcfRepRapFirmware;
if (!reprap_firmware) {
read_double = read_double && m_tiFreqY->GetTextCtrl()->GetValue().ToDouble(&m_params.freqStartY);
} else {
m_params.freqStartY = m_params.freqStartX;
m_tiFreqY->GetTextCtrl()->SetValue(m_tiFreqX->GetTextCtrl()->GetValue());
}
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.freqStartX < 0 || m_params.freqStartX > 500 ||
(!reprap_firmware && (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;
}
auto shaper_values = get_shaper_type_values();
int type_selection = m_rbType->GetSelection();
if (shaper_values.empty()) {
m_params.shaper_type.clear();
} else {
if (type_selection < 0 || type_selection >= static_cast<int>(shaper_values.size()))
type_selection = 0;
m_params.shaper_type = shaper_values[static_cast<size_t>(type_selection)];
}
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);
}
void Input_Shaping_Damp_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect) {
this->Refresh();
Fit();
}
// Cornering_Test_Dlg
//
Cornering_Test_Dlg::Cornering_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
: DPIDialog(parent, id, _L("Cornering test"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE), m_plater(plater)
{
SetBackgroundColour(*wxWHITE); // make sure background color set for dialog
SetForegroundColour(wxColour("#363636"));
SetFont(Label::Body_14);
wxBoxSizer* v_sizer = new wxBoxSizer(wxVERTICAL);
SetSizer(v_sizer);
// Model selection
auto labeled_box_model = new LabeledStaticBox(this, _L("Test model"));
auto model_box = new wxStaticBoxSizer(labeled_box_model, wxHORIZONTAL);
m_rbModel = new RadioGroup(this, { _L("Ringing Tower"), _L("Fast Tower"), _L("SCV-V2") }, wxVERTICAL);
model_box->Add(m_rbModel, 0, wxALL | wxEXPAND, FromDIP(4));
v_sizer->Add(model_box, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
// Settings
wxString start_jd_str = _L("Start: ");
wxString end_jd_str = _L("End: ");
int text_max = GetTextMax(this, std::vector<wxString>{start_jd_str, end_jd_str});
LabeledStaticBox* stb = new LabeledStaticBox(this, _L("Cornering settings"));
wxStaticBoxSizer* settings_sizer = new wxStaticBoxSizer(stb, wxVERTICAL);
settings_sizer->AddSpacer(FromDIP(5));
// Detect GCode Flavor and set appropriate values and units
const auto* preset_bundle = wxGetApp().preset_bundle;
const auto* gcode_flavor_option = (preset_bundle != nullptr)
? preset_bundle->printers.get_edited_preset().config.option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")
: nullptr;
wxString start_value_str;
wxString end_value_str;
wxString units_str;
if (gcode_flavor_option &&
gcode_flavor_option->value == GCodeFlavor::gcfMarlinFirmware &&
preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("machine_max_junction_deviation") &&
!preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("machine_max_junction_deviation")->values.empty() &&
preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("machine_max_junction_deviation")->values[0] > 0) {
// Using Junction Deviation (mm)
start_value_str = wxString::Format("%.3f", 0.000);
end_value_str = wxString::Format("%.3f", 0.250);
units_str = "mm";
} else {
// Using Classic Jerk (mm/s)
start_value_str = wxString::Format("%.3f", 1.0);
end_value_str = wxString::Format("%.3f", 15.0);
units_str = "mm/s";
}
auto st_size = wxSize(text_max, -1);
auto ti_size = FromDIP(wxSize(120, -1));
// Start cornering
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, start_value_str, units_str, "", wxDefaultPosition, ti_size);
m_tiJDStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
start_jd_sizer->Add(start_jd_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
start_jd_sizer->Add(m_tiJDStart , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(start_jd_sizer, 0, wxLEFT, FromDIP(3));
// End cornering
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, end_value_str, units_str, "", wxDefaultPosition, ti_size);
m_tiJDEnd->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
end_jd_sizer->Add(end_jd_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
end_jd_sizer->Add(m_tiJDEnd , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
settings_sizer->Add(end_jd_sizer, 0, wxLEFT, FromDIP(3));
settings_sizer->AddSpacer(FromDIP(5));
// Add note about cornering based on GCode Flavor
wxString note_msg = _L("Note: Lower values = sharper corners but slower speeds.");
auto note_text = new wxStaticText(this, wxID_ANY, note_msg, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
note_text->SetForegroundColour(wxColour(128, 128, 128));
note_text->Wrap(FromDIP(300));
settings_sizer->Add(note_text, 0, wxALL, FromDIP(5));
if (gcode_flavor_option) {
wxString note_msg_2;
switch (gcode_flavor_option->value) {
case GCodeFlavor::gcfMarlinFirmware: {
// Check if machine_max_junction_deviation is set and > 0
const auto* max_jd_option = preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("machine_max_junction_deviation");
if (max_jd_option && !max_jd_option->values.empty() && max_jd_option->values[0] > 0) {
note_msg_2 += _L("Marlin 2 Junction Deviation detected:\nTo test Classic Jerk, set 'Maximum Junction Deviation' in Motion ability to 0.");
} else {
note_msg_2 += _L("Marlin 2 Classic Jerk detected:\nTo test Junction Deviation, set 'Maximum Junction Deviation' in Motion ability to a value > 0.");
}
break;
}
case GCodeFlavor::gcfRepRapFirmware:
note_msg_2 += _L("RepRap detected: Jerk in mm/s.\nOrcaSlicer will convert the values to mm/min when necessary.");
break;
default:
break;
}
if(!note_msg_2.empty()){
auto note_text_2 = new wxStaticText(this, wxID_ANY, note_msg_2, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
note_text_2->SetForegroundColour(wxColour(128, 128, 128));
note_text_2->Wrap(FromDIP(300));
settings_sizer->Add(note_text_2, 0, wxALL, FromDIP(5));
}
}
v_sizer->Add(settings_sizer, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
v_sizer->AddSpacer(FromDIP(5));
auto dlg_btns = new DialogButtons(this, {"OK"});
auto bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
auto wiki = new HyperLink(this, _L("Wiki Guide"), "https://www.orcaslicer.com/wiki/cornering_calib");
bottom_sizer->Add(wiki, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(20));
bottom_sizer->AddStretchSpacer();
bottom_sizer->Add(dlg_btns, 0, wxEXPAND);
v_sizer->Add(bottom_sizer, 0, wxEXPAND);
dlg_btns->GetOK()->Bind(wxEVT_BUTTON, &Cornering_Test_Dlg::on_start, this);
wxGetApp().UpdateDlgDarkUI(this);
Layout();
Fit();
v_sizer->SetSizeHints(this);
}
Cornering_Test_Dlg::~Cornering_Test_Dlg() {
// Disconnect Events
}
void Cornering_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);
// Get max values based on GCode Flavor
double max_end_value = 100.0;
double warning_threshold = 20.0;
const auto* preset_bundle = wxGetApp().preset_bundle;
const auto* gcode_flavor_option = (preset_bundle != nullptr)
? preset_bundle->printers.get_edited_preset().config.option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")
: nullptr;
if (gcode_flavor_option &&
gcode_flavor_option->value == GCodeFlavor::gcfMarlinFirmware &&
preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("machine_max_junction_deviation") &&
!preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("machine_max_junction_deviation")->values.empty() &&
preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("machine_max_junction_deviation")->values[0] > 0) {
// Using Junction Deviation (mm)
max_end_value = 0.3;
warning_threshold = 0.25;
}
if (!read_double || m_params.start < 0 || m_params.end > max_end_value || m_params.start >= m_params.end) {
wxString error_msg = wxString::Format(_L("Please input valid values:\n(0 <= Cornering <= %s)"), wxString::Format("%.3f", max_end_value));
MessageDialog msg_dlg(nullptr, error_msg, wxEmptyString, wxICON_WARNING | wxOK);
msg_dlg.ShowModal();
return;
} else if (m_params.end > warning_threshold) {
wxString warning_msg = wxString::Format(_L("NOTE: High values may cause Layer shift (>%s)"), wxString::Format("%.3f", warning_threshold));
MessageDialog msg_dlg(nullptr, warning_msg, wxEmptyString, wxICON_WARNING | wxOK);
msg_dlg.ShowModal();
}
m_params.mode = CalibMode::Calib_Cornering;
// Set model type based on selection
m_params.test_model = m_rbModel->GetSelection();
m_plater->Calib_Cornering(m_params);
EndModal(wxID_OK);
}
void Cornering_Test_Dlg::on_dpi_changed(const wxRect& suggested_rect) {
this->Refresh();
Fit();
}
// FlowRateCalibrationDialog
//
FlowRateCalibrationDialog::FlowRateCalibrationDialog(wxWindow* parent, wxWindowID id, Plater* plater)
: DPIDialog(parent, id, _L("Flow Ratio Calibration"), wxDefaultPosition, parent->FromDIP(wxSize(-1, 280)), wxDEFAULT_DIALOG_STYLE), m_plater(plater)
{
SetBackgroundColour(*wxWHITE); // make sure background color set for dialog
SetForegroundColour(wxColour("#363636"));
SetFont(Label::Body_14);
wxBoxSizer* v_sizer = new wxBoxSizer(wxVERTICAL);
SetSizer(v_sizer);
// Type selection
auto labeled_box_type = new LabeledStaticBox(this, _L("Calibration Test Type"));
auto type_box = new wxStaticBoxSizer(labeled_box_type, wxVERTICAL);
m_rbType = new RadioGroup(this, { _L("Pass 1 (Coarse)"), _L("Pass 2 (Fine)"), _L("YOLO (Recommended)"), _L("YOLO (Perfectionist)") }, wxVERTICAL, 1);
m_rbType->SetSelection(2); // Default to YOLO Recommended
type_box->Add(m_rbType, 0, wxALL | wxEXPAND, FromDIP(4));
v_sizer->Add(type_box, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
// Pattern selection
auto labeled_box_pattern = new LabeledStaticBox(this, _L("Top Surface Pattern"));
auto pattern_box = new wxStaticBoxSizer(labeled_box_pattern, wxVERTICAL);
// ORCA: Use ComboBox with icons instead of RadioGroup
m_rbPattern = new ComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_READONLY);
boost::filesystem::path image_path(Slic3r::resources_dir());
image_path /= "images";
auto add_pattern_item = [&](const std::string& name, const wxString& label) {
auto icon_name = "param_" + name;
if (boost::filesystem::exists(image_path / (icon_name + ".svg"))) {
// Using 24px icon size to match other settings (Field.cpp uses 24)
ScalableBitmap bm(this, icon_name, 24);
m_rbPattern->Append(label, bm.bmp());
} else {
m_rbPattern->Append(label);
}
};
add_pattern_item("archimedeanchords", _L("Archimedean Chords"));
add_pattern_item("monotonic", _L("Monotonic"));
m_rbPattern->SetSelection(0); // Default to Archimedean Chords
// ORCA: explicit set value to ensure display on Windows
m_rbPattern->SetValue(m_rbPattern->GetString(0));
pattern_box->Add(m_rbPattern, 0, wxALL | wxEXPAND, FromDIP(4));
v_sizer->Add(pattern_box, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
v_sizer->AddSpacer(FromDIP(5));
auto dlg_btns = new DialogButtons(this, {"OK"});
auto bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
auto wiki = new HyperLink(this, _L("Wiki Guide"), "https://www.orcaslicer.com/wiki/calibration/flow-ratio-calib");
bottom_sizer->Add(wiki, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(20));
bottom_sizer->AddStretchSpacer();
bottom_sizer->Add(dlg_btns, 0, wxEXPAND);
v_sizer->Add(bottom_sizer, 0, wxEXPAND);
dlg_btns->GetOK()->Bind(wxEVT_BUTTON, &FlowRateCalibrationDialog::on_start, this);
wxGetApp().UpdateDlgDarkUI(this);
Layout();
Fit();
v_sizer->SetSizeHints(this);
}
FlowRateCalibrationDialog::~FlowRateCalibrationDialog() {
// Disconnect Events
}
void FlowRateCalibrationDialog::on_start(wxCommandEvent& event) {
int type = m_rbType->GetSelection();
int patternIdx = m_rbPattern->GetSelection();
InfillPattern pattern = ipArchimedeanChords;
if (patternIdx == 1) pattern = ipMonotonic;
bool is_linear = (type >= 2);
int pass = (type % 2) + 1;
m_plater->calib_flowrate(is_linear, pass, pattern);
EndModal(wxID_OK);
}
void FlowRateCalibrationDialog::on_dpi_changed(const wxRect& suggested_rect) {
this->Refresh();
}
}} // namespace Slic3r::GUI