#include "CalibrationBedDialog.hpp" #include "I18N.hpp" #include "libslic3r/Utils.hpp" #include "GUI.hpp" #include "GUI_ObjectList.hpp" #include "Tab.hpp" #include #include #include #include #include "wxExtensions.hpp" #if ENABLE_SCROLLABLE static wxSize get_screen_size(wxWindow* window) { const auto idx = wxDisplay::GetFromWindow(window); wxDisplay display(idx != wxNOT_FOUND ? idx : 0u); return display.GetClientArea().GetSize(); } #endif // ENABLE_SCROLLABLE namespace Slic3r { namespace GUI { CalibrationBedDialog::CalibrationBedDialog(GUI_App* app, MainFrame* mainframe) : DPIDialog(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _(L("Bed calibration - test objects generation")), #if ENABLE_SCROLLABLE wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) #else wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE) #endif // ENABLE_SCROLLABLE { this->gui_app = app; this->main_frame = mainframe; SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); // fonts const wxFont& font = wxGetApp().normal_font(); const wxFont& bold_font = wxGetApp().bold_font(); SetFont(font); auto main_sizer = new wxBoxSizer(wxVERTICAL); //html html_viewer = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxSize(800, 500), wxHW_SCROLLBAR_AUTO); html_viewer->LoadPage("./resources/calibration/bed_leveling/bed_leveling.html"); main_sizer->Add(html_viewer, 1, wxEXPAND | wxALL, 5); wxStdDialogButtonSizer* buttons = new wxStdDialogButtonSizer(); wxButton* bt = new wxButton(this, wxID_FILE1, _(L("Generate"))); bt->Bind(wxEVT_BUTTON, &CalibrationBedDialog::create_geometry, this); buttons->Add(bt); wxButton* close = new wxButton(this, wxID_CLOSE, _(L("Close"))); close->Bind(wxEVT_BUTTON, &CalibrationBedDialog::closeMe, this); buttons->AddButton(close); close->SetDefault(); close->SetFocus(); SetAffirmativeId(wxID_CLOSE); buttons->Realize(); main_sizer->Add(buttons, 0, wxEXPAND | wxALL, 5); SetSizer(main_sizer); main_sizer->SetSizeHints(this); } void CalibrationBedDialog::closeMe(wxCommandEvent& event_args) { this->Destroy(); } void CalibrationBedDialog::create_geometry(wxCommandEvent& event_args) { Plater* plat = this->main_frame->plater(); Model& model = plat->model(); plat->reset(); std::vector objs_idx = plat->load_files(std::vector{ "./resources/calibration/bed_leveling/patch.amf", "./resources/calibration/bed_leveling/patch.amf", "./resources/calibration/bed_leveling/patch.amf", "./resources/calibration/bed_leveling/patch.amf", "./resources/calibration/bed_leveling/patch.amf"}, true, false); assert(objs_idx.size() == 5); const DynamicPrintConfig* printConfig = this->gui_app->get_tab(Preset::TYPE_PRINT)->get_config(); const DynamicPrintConfig* printerConfig = this->gui_app->get_tab(Preset::TYPE_PRINTER)->get_config(); /// --- scale --- //model is created for a 0.4 nozzle, scale xy with nozzle size. const ConfigOptionFloats* nozzle_diameter = printerConfig->option("nozzle_diameter"); assert(nozzle_diameter->values.size() > 0); float xyScale = nozzle_diameter->values[0] / 0.4; //scale z with the first_layer_height const ConfigOptionFloatOrPercent* first_layer_height = printConfig->option("first_layer_height"); float zscale = first_layer_height->get_abs_value(nozzle_diameter->values[0]) / 0.2; //do scaling if (xyScale < 0.9 || 1.2 < xyScale) { for (size_t i = 0; i < 5; i++) model.objects[objs_idx[i]]->scale(xyScale, xyScale, zscale); } else { for (size_t i = 0; i < 5; i++) model.objects[objs_idx[i]]->scale(1, 1, zscale); } /// --- rotate --- const ConfigOptionPoints* bed_shape = printerConfig->option("bed_shape"); if (bed_shape->values.size() == 4) { model.objects[objs_idx[0]]->rotate(PI / 4, { 0,0,1 }); model.objects[objs_idx[1]]->rotate(5 * PI / 4, { 0,0,1 }); model.objects[objs_idx[3]]->rotate(3 * PI / 4, { 0,0,1 }); model.objects[objs_idx[4]]->rotate(7 * PI / 4, { 0,0,1 }); } else { model.objects[objs_idx[3]]->rotate(PI / 2, { 0,0,1 }); model.objects[objs_idx[4]]->rotate(PI / 2, { 0,0,1 }); } /// --- translate --- //three first will stay with this orientation (top left, middle, bottom right) //last two with 90deg (top left, middle, bottom right) //get position for patches Vec2d bed_size = BoundingBoxf(bed_shape->values).size(); Vec2d bed_min = BoundingBoxf(bed_shape->values).min; float offsetx = 10 + 10 * xyScale; float offsety = 10 + 10 * xyScale; if (bed_shape->values.size() > 4) { offsetx = bed_size.x() / 2 - bed_size.x() * std::sqrtf(2) / 4 + 10 * xyScale; offsety = bed_size.y() / 2 - bed_size.y() * std::sqrtf(2) / 4 + 10 * xyScale; } bool large_enough = bed_shape->values.size() == 4 ? (bed_size.x() > offsetx * 3 && bed_size.y() > offsety * 3) : (bed_size.x() > offsetx * 2 + 10 * xyScale && bed_size.y() > offsety * 2 + 10 * xyScale); if (!large_enough){ //problem : too small, use arrange instead and let the user place them. plat->arrange(); //TODO add message } else { model.objects[objs_idx[0]]->translate({ bed_min.x() + offsetx, bed_min.y() + bed_size.y() - offsety,0 }); model.objects[objs_idx[1]]->translate({ bed_min.x() + bed_size.x() - offsetx,bed_min.y() + offsety , 0 }); model.objects[objs_idx[2]]->translate({ bed_min.x() + bed_size.x()/2, bed_min.y() + bed_size.y() / 2, 0 }); model.objects[objs_idx[3]]->translate({ bed_min.x() + offsetx, bed_min.y() + offsety, 0 }); model.objects[objs_idx[4]]->translate({ bed_min.x() + bed_size.x() - offsetx,bed_min.y() + bed_size.y() - offsety,0 }); } /// --- main config, please modify object config when possible --- DynamicPrintConfig new_print_config = *printConfig; //make a copy new_print_config.set_key_value("complete_objects", new ConfigOptionBool(true)); new_print_config.set_key_value("skirts", new ConfigOptionInt(2)); /// --- custom config --- for (size_t i = 0; i < 5; i++) { model.objects[objs_idx[i]]->config.set_key_value("perimeters", new ConfigOptionInt(2)); model.objects[objs_idx[i]]->config.set_key_value("bottom_solid_layers", new ConfigOptionInt(2)); model.objects[objs_idx[i]]->config.set_key_value("gap_fill", new ConfigOptionBool(false)); model.objects[objs_idx[i]]->config.set_key_value("first_layer_extrusion_width", new ConfigOptionFloatOrPercent(140, true)); model.objects[objs_idx[i]]->config.set_key_value("bottom_fill_pattern", new ConfigOptionEnum(ipRectilinearWGapFill)); } if (bed_shape->values.size() == 4) { model.objects[objs_idx[0]]->config.set_key_value("fill_angle", new ConfigOptionFloat(90)); model.objects[objs_idx[1]]->config.set_key_value("fill_angle", new ConfigOptionFloat(90)); model.objects[objs_idx[2]]->config.set_key_value("fill_angle", new ConfigOptionFloat(45)); model.objects[objs_idx[3]]->config.set_key_value("fill_angle", new ConfigOptionFloat(0)); model.objects[objs_idx[4]]->config.set_key_value("fill_angle", new ConfigOptionFloat(0)); } else { for (size_t i = 0; i < 3; i++) for (size_t i = 3; i < 5; i++) model.objects[objs_idx[i]]->config.set_key_value("fill_angle", new ConfigOptionFloat(135)); } //update plater this->gui_app->get_tab(Preset::TYPE_PRINT)->load_config(new_print_config); plat->on_config_change(new_print_config); plat->changed_objects(objs_idx); this->gui_app->get_tab(Preset::TYPE_PRINT)->update_dirty(); //update everything, easier to code. this->gui_app->obj_list()->update_after_undo_redo(); //if(!plat->is_background_process_update_scheduled()) // plat->schedule_background_process(); plat->reslice(); plat->select_view_3D("Preview"); } void CalibrationBedDialog::on_dpi_changed(const wxRect& suggested_rect) { msw_buttons_rescale(this, em_unit(), { wxID_OK }); Layout(); Fit(); Refresh(); } wxPanel* CalibrationBedDialog::create_header(wxWindow* parent, const wxFont& bold_font) { wxPanel* panel = new wxPanel(parent); wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); wxFont header_font = bold_font; #ifdef __WXOSX__ header_font.SetPointSize(14); #else header_font.SetPointSize(bold_font.GetPointSize() + 2); #endif // __WXOSX__ sizer->AddStretchSpacer(); // text wxStaticText* text = new wxStaticText(panel, wxID_ANY, _(L("Keyboard shortcuts"))); text->SetFont(header_font); sizer->Add(text, 0, wxALIGN_CENTER_VERTICAL); sizer->AddStretchSpacer(); panel->SetSizer(sizer); return panel; } } // namespace GUI } // namespace Slic3r