diff --git a/resources/calibration/cube/LICENCE.txt b/resources/calibration/cube/LICENCE.txt new file mode 100644 index 000000000..219827f42 --- /dev/null +++ b/resources/calibration/cube/LICENCE.txt @@ -0,0 +1,2 @@ +voron_design_cube_v6.amf is under GPL V3, made by vorondesign (https://github.com/VoronDesign/Voron-2) +xyzCalibration_cube is under CC BY-SA 3.0, made by iDig3Dprinting (https://www.thingiverse.com/thing:1278865) \ No newline at end of file diff --git a/resources/calibration/cube/cube.html b/resources/calibration/cube/cube.html new file mode 100644 index 000000000..38ae86f35 --- /dev/null +++ b/resources/calibration/cube/cube.html @@ -0,0 +1,22 @@ + + + + + Calibration cube + + +

Calibration cube

+

This page allow you to print the loved calibration cube. It's useful to see if your printer is accurate, and calibrate some settings. First thing is to select your goal.

+

Goal: Dimensional accuracy

+

You have to print two cubes, with different sizes. If the dimensional inaccuracy scale with the size of the cubes, then it's the steps/mm of your steppers that needs some adjustments (note: if it's the case, you should also adjust the flow, and maybe redo some calibrations if the change is big). If it doesn't scale, you can correct it by adjusting your xy compensation (slicing tab). With the voron cube, you can also test the hole calibration, as these are often too small.

+

Goal: infill/perimeter overlap

+

This test is about to see if the pattern of the infill can be seen on the perimeters. Try to reduce it as low as you can but check the top solid fill, as it can create artifacts if it's too low. It's better with the standard cube, as you have a larger top surface.

+

Goal: external perimeter overlap

+

As the external perimeter is printed last, the real position depends on the position and flow of the whole layer. So you have to be sure that your flow is perfect before doing this test. The main difference is between solid infill layers and sparse infill layers, as the solid one can push the external perimeter outwards. Decreasing the infill/perimeter overlap, decreasing the external perimeter overlap can help to reduce this effect.

+

Notes

+

These tests should be the last ones.

+

Default size of the standard cube is 20mm and the standard size of the voron cube is 30mm.

+

Licence for the standard calibration cube: CC BY-SA 3.0, made by iDig3Dprinting

+

Licence for the voron calibration cube: GPL V3, made by vorondesign

+ + \ No newline at end of file diff --git a/resources/calibration/cube/voron_design_cube_v6.amf b/resources/calibration/cube/voron_design_cube_v6.amf new file mode 100644 index 000000000..d79cddc9e Binary files /dev/null and b/resources/calibration/cube/voron_design_cube_v6.amf differ diff --git a/resources/calibration/cube/xyzCalibration_cube.amf b/resources/calibration/cube/xyzCalibration_cube.amf new file mode 100644 index 000000000..35af49d2c Binary files /dev/null and b/resources/calibration/cube/xyzCalibration_cube.amf differ diff --git a/resources/calibration/filament_temp/filament_temp.html b/resources/calibration/filament_temp/filament_temp.html index 69ead7848..7f17e995b 100644 --- a/resources/calibration/filament_temp/filament_temp.html +++ b/resources/calibration/filament_temp/filament_temp.html @@ -48,7 +48,6 @@

Notes

Most of the calibrations need to done is the right order. This one should be the third.

The temperature numbers will only be shown between 180 and 285. Higher or lower values won't be displayed but the test will be conducted successfully, you just have to remember them.

-

This tower is made with the 3D model created by gaaZolee and available here https://www.thingiverse.com/thing:2729076 with the CC BY-SA 3.0 licence.

-

Licence for models used for this calibration test: CC BY-SA 3.0

+

This tower is made with the 3D model created by gaaZolee with the CC BY-SA 3.0 licence.

\ No newline at end of file diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 336c2f49e..75031f7bd 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -24,6 +24,8 @@ set(SLIC3R_GUI_SOURCES GUI/CalibrationBedDialog.hpp GUI/CalibrationBridgeDialog.cpp GUI/CalibrationBridgeDialog.hpp + GUI/CalibrationCubeDialog.cpp + GUI/CalibrationCubeDialog.hpp GUI/CalibrationFlowDialog.cpp GUI/CalibrationFlowDialog.hpp GUI/CalibrationOverBridgeDialog.cpp diff --git a/src/slic3r/GUI/CalibrationCubeDialog.cpp b/src/slic3r/GUI/CalibrationCubeDialog.cpp new file mode 100644 index 000000000..7dda4bea7 --- /dev/null +++ b/src/slic3r/GUI/CalibrationCubeDialog.cpp @@ -0,0 +1,107 @@ +#include "CalibrationCubeDialog.hpp" +#include "I18N.hpp" +#include "libslic3r/Utils.hpp" +#include "GUI.hpp" +#include "GUI_ObjectList.hpp" +#include "Tab.hpp" +#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 { + +void CalibrationCubeDialog::create_buttons(wxStdDialogButtonSizer* buttons){ + wxString choices_scale[] = { "10mm", "20mm", "30mm", "40mm" }; + scale = new wxComboBox(this, wxID_ANY, wxString{ "20mm" }, wxDefaultPosition, wxDefaultSize, 4, choices_scale); + scale->SetToolTip(_(L("You can choose the dimension of the cube. It's a simple scale, you can modify it in the right panel yourself if you prefer. It's just quicker to select it here."))); + scale->SetSelection(1); + wxString choices_goal[] = { "Dimensional accuracy (default)" , "infill/perimeter overlap", "external perimeter overlap"}; + calibrate = new wxComboBox(this, wxID_ANY, wxString{ "Dimensional accuracy (default)" }, wxDefaultPosition, wxDefaultSize, 3, choices_goal); + calibrate->SetToolTip(_(L("Select a goal, this will change settings to increase the effects to search."))); + calibrate->SetSelection(0); + + buttons->Add(new wxStaticText(this, wxID_ANY, wxString{ "dimension:" })); + buttons->Add(scale); + buttons->AddSpacer(40); + buttons->Add(new wxStaticText(this, wxID_ANY, wxString{ "goal:" })); + buttons->Add(calibrate); + buttons->AddSpacer(40); + + wxButton* bt = new wxButton(this, wxID_FILE1, _(L("Standard Cube"))); + bt->Bind(wxEVT_BUTTON, &CalibrationCubeDialog::create_geometry_standard, this); + bt->SetToolTip(_(L("Standard cubic xyz cube, with a flat top. Better for infill/perimeter overlap calibration."))); + buttons->Add(bt); + buttons->AddSpacer(10); + bt = new wxButton(this, wxID_FILE1, _(L("Voron Cube"))); + bt->Bind(wxEVT_BUTTON, &CalibrationCubeDialog::create_geometry_voron, this); + bt->SetToolTip(_(L("Voron cubic cube with many features inside, with a bearing slot on top. Better to check dimensional accuracy."))); + buttons->Add(bt); +} + +void CalibrationCubeDialog::create_geometry(std::string calibration_path) { + Plater* plat = this->main_frame->plater(); + Model& model = plat->model(); + plat->reset(); + std::vector objs_idx = plat->load_files(std::vector{ + Slic3r::resources_dir()+"/calibration/cube/"+ calibration_path}, true, false); + + assert(objs_idx.size() == 1); + const DynamicPrintConfig* printConfig = this->gui_app->get_tab(Preset::TYPE_PRINT)->get_config(); + const DynamicPrintConfig* filamentConfig = this->gui_app->get_tab(Preset::TYPE_FILAMENT)->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_config = printerConfig->option("nozzle_diameter"); + assert(nozzle_diameter_config->values.size() > 0); + float nozzle_diameter = nozzle_diameter_config->values[0]; + float cube_size = 30; + if (calibration_path == "xyzCalibration_cube.amf") + cube_size = 20; + int idx_scale = scale->GetSelection(); + float xyzScale = (10/cube_size) * (idx_scale+1); + //do scaling + model.objects[objs_idx[0]]->scale(xyzScale, xyzScale, xyzScale); + + + /// --- translate --- + const ConfigOptionPoints* bed_shape = printerConfig->option("bed_shape"); + Vec2d bed_size = BoundingBoxf(bed_shape->values).size(); + Vec2d bed_min = BoundingBoxf(bed_shape->values).min; + model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2, bed_min.y() + bed_size.y() / 2, 0 }); + + /// --- custom config --- + int idx_goal = calibrate->GetSelection(); + if (idx_goal == 1) { + model.objects[objs_idx[0]]->config.set_key_value("perimeters", new ConfigOptionInt(1)); + model.objects[objs_idx[0]]->config.set_key_value("fill_pattern", new ConfigOptionEnum(ipCubic)); + } else if (idx_goal == 2) { + model.objects[objs_idx[0]]->config.set_key_value("perimeters", new ConfigOptionInt(3)); + //add full solid layers + } + + //update plater + plat->changed_objects(objs_idx); + plat->is_preview_shown(); + //update everything, easier to code. + ObjectList* obj = this->gui_app->obj_list(); + obj->update_after_undo_redo(); + + + plat->reslice(); + plat->select_view_3D("Preview"); +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/CalibrationCubeDialog.hpp b/src/slic3r/GUI/CalibrationCubeDialog.hpp new file mode 100644 index 000000000..cb5f18f3c --- /dev/null +++ b/src/slic3r/GUI/CalibrationCubeDialog.hpp @@ -0,0 +1,30 @@ +#ifndef slic3r_GUI_CalibrationCubeDialog_hpp_ +#define slic3r_GUI_CalibrationCubeDialog_hpp_ + +#include "CalibrationAbstractDialog.hpp" + +namespace Slic3r { +namespace GUI { + +class CalibrationCubeDialog : public CalibrationAbstractDialog +{ + +public: + CalibrationCubeDialog(GUI_App* app, MainFrame* mainframe) : CalibrationAbstractDialog(app, mainframe, "Calibration cube") { create("/calibration/cube/cube.html"); } + virtual ~CalibrationCubeDialog(){ } + +protected: + void create_buttons(wxStdDialogButtonSizer* sizer) override; + void create_geometry(std::string cube_path); + void create_geometry_voron(wxCommandEvent& event_args) { create_geometry("voron_design_cube_v6.amf"); } + void create_geometry_standard(wxCommandEvent& event_args) { create_geometry("xyzCalibration_cube.amf"); } + + wxComboBox* scale; + wxComboBox* calibrate; + +}; + +} // namespace GUI +} // namespace Slic3r + +#endif diff --git a/src/slic3r/GUI/CalibrationFlowDialog.cpp b/src/slic3r/GUI/CalibrationFlowDialog.cpp index f9ca6502c..75432773a 100644 --- a/src/slic3r/GUI/CalibrationFlowDialog.cpp +++ b/src/slic3r/GUI/CalibrationFlowDialog.cpp @@ -54,9 +54,12 @@ void CalibrationFlowDialog::create_geometry(float start, float delta) { float nozzle_diameter = nozzle_diameter_config->values[0]; float xyScale = nozzle_diameter / 0.4; //scale z to have 6 layers - const ConfigOptionFloatOrPercent* first_layer_height = printConfig->option("first_layer_height"); - const ConfigOptionFloat* layer_height = printConfig->option("layer_height"); - float zscale = first_layer_height->get_abs_value(nozzle_diameter) + 5 * layer_height->value; + const ConfigOptionFloatOrPercent* first_layer_height_setting = printConfig->option("first_layer_height"); + double first_layer_height = first_layer_height_setting->get_abs_value(nozzle_diameter); + double layer_height = nozzle_diameter / 2.; + first_layer_height = std::max(first_layer_height, nozzle_diameter / 2.); + + float zscale = first_layer_height + 5 * layer_height; zscale *= (1 + 0.3 * (10. / delta)); //do scaling if (xyScale < 0.9 || 1.2 < xyScale) { @@ -69,18 +72,19 @@ void CalibrationFlowDialog::create_geometry(float start, float delta) { //add sub-part after scale float zshift = (1 - zscale) / 2; + float zscale_number = (first_layer_height + layer_height) / 0.4; if (delta == 10.f && start == 80.f) { - add_part(model.objects[objs_idx[0]], Slic3r::resources_dir()+"/calibration/filament_flow/m20.amf", Vec3d{ 9,0,zshift }); - add_part(model.objects[objs_idx[1]], Slic3r::resources_dir()+"/calibration/filament_flow/m10.amf", Vec3d{ 9,0,zshift }); - add_part(model.objects[objs_idx[2]], Slic3r::resources_dir()+"/calibration/filament_flow/_0.amf", Vec3d{ 9,0,zshift }); - add_part(model.objects[objs_idx[3]], Slic3r::resources_dir()+"/calibration/filament_flow/p10.amf", Vec3d{ 9,0,zshift }); - add_part(model.objects[objs_idx[4]], Slic3r::resources_dir()+"/calibration/filament_flow/p20.amf", Vec3d{ 9,0,zshift }); + add_part(model.objects[objs_idx[0]], Slic3r::resources_dir()+"/calibration/filament_flow/m20.amf", Vec3d{ 3 + 6 * xyScale,0,zshift }, Vec3d{ 1,1, zscale_number}); + add_part(model.objects[objs_idx[1]], Slic3r::resources_dir()+"/calibration/filament_flow/m10.amf", Vec3d{ 3 + 6 * xyScale,0,zshift }, Vec3d{ 1,1, zscale_number }); + add_part(model.objects[objs_idx[2]], Slic3r::resources_dir()+"/calibration/filament_flow/_0.amf", Vec3d{ 3 + 6 * xyScale,0,zshift }, Vec3d{ 1,1, zscale_number }); + add_part(model.objects[objs_idx[3]], Slic3r::resources_dir()+"/calibration/filament_flow/p10.amf", Vec3d{ 3 + 6 * xyScale,0,zshift }, Vec3d{ 1,1, zscale_number }); + add_part(model.objects[objs_idx[4]], Slic3r::resources_dir()+"/calibration/filament_flow/p20.amf", Vec3d{ 3 + 6 * xyScale,0,zshift }, Vec3d{ 1,1, zscale_number }); } else if (delta == 2.f && start == 92.f) { - add_part(model.objects[objs_idx[0]], Slic3r::resources_dir()+"/calibration/filament_flow/m8.amf", Vec3d{ 9,0,zshift }); - add_part(model.objects[objs_idx[1]], Slic3r::resources_dir()+"/calibration/filament_flow/m6.amf", Vec3d{ 9,0,zshift }); - add_part(model.objects[objs_idx[2]], Slic3r::resources_dir()+"/calibration/filament_flow/m4.amf", Vec3d{ 9,0,zshift }); - add_part(model.objects[objs_idx[3]], Slic3r::resources_dir()+"/calibration/filament_flow/m2.amf", Vec3d{ 9,0,zshift }); - add_part(model.objects[objs_idx[4]], Slic3r::resources_dir()+"/calibration/filament_flow/_0.amf", Vec3d{ 9,0,zshift }); + add_part(model.objects[objs_idx[0]], Slic3r::resources_dir()+"/calibration/filament_flow/m8.amf", Vec3d{ 3 + 6 * xyScale,0,zshift }, Vec3d{ 1,1, zscale_number }); + add_part(model.objects[objs_idx[1]], Slic3r::resources_dir()+"/calibration/filament_flow/m6.amf", Vec3d{ 3 + 6 * xyScale,0,zshift }, Vec3d{ 1,1, zscale_number }); + add_part(model.objects[objs_idx[2]], Slic3r::resources_dir()+"/calibration/filament_flow/m4.amf", Vec3d{ 3 + 6 * xyScale,0,zshift }, Vec3d{ 1,1, zscale_number }); + add_part(model.objects[objs_idx[3]], Slic3r::resources_dir()+"/calibration/filament_flow/m2.amf", Vec3d{ 3 + 6 * xyScale,0,zshift }, Vec3d{ 1,1, zscale_number }); + add_part(model.objects[objs_idx[4]], Slic3r::resources_dir()+"/calibration/filament_flow/_0.amf", Vec3d{ 3 + 6 * xyScale,0,zshift }, Vec3d{ 1,1, zscale_number }); } for (size_t i = 0; i < 5; i++) { add_part(model.objects[objs_idx[i]], Slic3r::resources_dir()+"/calibration/filament_flow/O.amf", Vec3d{ 0,0,zscale/2.f + 0.5 }, Vec3d{1,1,xyScale }); @@ -118,14 +122,8 @@ void CalibrationFlowDialog::create_geometry(float start, float delta) { model.objects[objs_idx[i]]->config.set_key_value("thin_walls", new ConfigOptionBool(true)); model.objects[objs_idx[i]]->config.set_key_value("thin_walls_min_width", new ConfigOptionFloatOrPercent(50,true)); model.objects[objs_idx[i]]->config.set_key_value("gap_fill", new ConfigOptionBool(true)); - model.objects[objs_idx[i]]->config.set_key_value("layer_height", new ConfigOptionFloat(nozzle_diameter/2)); - if (nozzle_diameter < 0.3) { - //ensure that the first layer is good and safe, mandatory as we mess wit the first_layer_height. - model.objects[objs_idx[i]]->config.set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(nozzle_diameter, false)); - model.objects[objs_idx[i]]->config.set_key_value("first_layer_extrusion_width", new ConfigOptionFloatOrPercent(150, true)); - } else { - model.objects[objs_idx[i]]->config.set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(std::max(0.2f, nozzle_diameter / 2), false)); - } + model.objects[objs_idx[i]]->config.set_key_value("layer_height", new ConfigOptionFloat(layer_height)); + model.objects[objs_idx[i]]->config.set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(first_layer_height, false)); model.objects[objs_idx[i]]->config.set_key_value("external_infill_margin", new ConfigOptionFloatOrPercent(100, true)); model.objects[objs_idx[i]]->config.set_key_value("solid_fill_pattern", new ConfigOptionEnum(ipRectilinearWGapFill)); model.objects[objs_idx[i]]->config.set_key_value("top_fill_pattern", new ConfigOptionEnum(ipSmooth)); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 4aaf874dd..fdaae7026 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -42,6 +42,7 @@ #include "slic3r/Config/Snapshot.hpp" #include "CalibrationBedDialog.hpp" #include "CalibrationBridgeDialog.hpp" +#include "CalibrationCubeDialog.hpp" #include "CalibrationFlowDialog.hpp" #include "CalibrationOverBridgeDialog.hpp" #include "CalibrationTempDialog.hpp" @@ -592,6 +593,10 @@ void GUI_App::filament_temperature_dialog() { change_calibration_dialog(nullptr, new CalibrationTempDialog(this, mainframe)); } +void GUI_App::calibration_cube_dialog() +{ + change_calibration_dialog(nullptr, new CalibrationCubeDialog(this, mainframe)); +} // static method accepting a wxWindow object as first parameter bool GUI_App::catch_error(std::function cb, diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 945411fcf..56ff8935f 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -137,6 +137,7 @@ public: void filament_temperature_dialog(); void bridge_tuning_dialog(); void over_bridge_dialog(); + void calibration_cube_dialog(); //void support_tuning(); //have to do multiple, in a submenu void load_project(wxWindow *parent, wxString& input_file) const; void import_model(wxWindow *parent, wxArrayString& input_files) const; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index d5c29ea5b..392ab4c4a 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -775,7 +775,9 @@ void MainFrame::init_menubar() [this](wxCommandEvent&) { wxGetApp().bridge_tuning_dialog(); }); append_menu_item(objectsMenu, wxID_ANY, _(L("Ironing pattern calibration")), _(L("Create a test print to help you to set your over-bridge flow ratio and ironing pattern.")), [this](wxCommandEvent&) { wxGetApp().over_bridge_dialog(); }); - + append_menu_item(objectsMenu, wxID_ANY, _(L("Calibration cube")), _(L("Print a calibration cube, for various calibration goals.")), + [this](wxCommandEvent&) { wxGetApp().calibration_cube_dialog(); }); + } // Help menu