Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/slic3r/GUI/Plater.cpp')
-rw-r--r--src/slic3r/GUI/Plater.cpp3027
1 files changed, 1839 insertions, 1188 deletions
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 8388c1cfd..7824dcfdf 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -12,6 +12,7 @@
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/log/trivial.hpp>
+#include <boost/nowide/convert.hpp>
#include <wx/sizer.h>
#include <wx/stattext.h>
@@ -23,7 +24,6 @@
#include <wx/dnd.h>
#include <wx/progdlg.h>
#include <wx/wupdlock.h>
-#include <wx/colordlg.h>
#include <wx/numdlg.h>
#include <wx/debug.h>
#include <wx/busyinfo.h>
@@ -32,26 +32,17 @@
#include "libslic3r/Format/STL.hpp"
#include "libslic3r/Format/AMF.hpp"
#include "libslic3r/Format/3mf.hpp"
-#include "libslic3r/GCode/PreviewData.hpp"
-#if ENABLE_THUMBNAIL_GENERATOR
#include "libslic3r/GCode/ThumbnailData.hpp"
-#endif // ENABLE_THUMBNAIL_GENERATOR
#include "libslic3r/Model.hpp"
#include "libslic3r/SLA/Hollowing.hpp"
-#include "libslic3r/SLA/Rotfinder.hpp"
#include "libslic3r/SLA/SupportPoint.hpp"
+#include "libslic3r/SLA/ReprojectPointsOnMesh.hpp"
#include "libslic3r/Polygon.hpp"
#include "libslic3r/Print.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "libslic3r/Utils.hpp"
-
-//#include "libslic3r/ClipperUtils.hpp"
-
-// #include "libnest2d/optimizers/nlopt/genetic.hpp"
-// #include "libnest2d/backends/clipper/geometries.hpp"
-// #include "libnest2d/utils/rotcalipers.hpp"
-#include "libslic3r/MinAreaBoundingBox.hpp"
+#include "libslic3r/PresetBundle.hpp"
#include "GUI.hpp"
#include "GUI_App.hpp"
@@ -61,6 +52,7 @@
#include "GUI_Utils.hpp"
#include "wxExtensions.hpp"
#include "MainFrame.hpp"
+#include "format.hpp"
#include "3DScene.hpp"
#include "GLCanvas3D.hpp"
#include "Selection.hpp"
@@ -70,8 +62,10 @@
#include "Camera.hpp"
#include "Mouse3DController.hpp"
#include "Tab.hpp"
-#include "Job.hpp"
-#include "PresetBundle.hpp"
+#include "Jobs/ArrangeJob.hpp"
+#include "Jobs/FillBedJob.hpp"
+#include "Jobs/RotoptimizeJob.hpp"
+#include "Jobs/SLAImportJob.hpp"
#include "BackgroundSlicingProcess.hpp"
#include "ProgressStatusBar.hpp"
#include "PrintHostDialogs.hpp"
@@ -80,8 +74,16 @@
#include "../Utils/PrintHost.hpp"
#include "../Utils/FixModelByWin10.hpp"
#include "../Utils/UndoRedo.hpp"
-#include "../Utils/Thread.hpp"
+#include "../Utils/PresetUpdater.hpp"
+#include "../Utils/Process.hpp"
#include "RemovableDriveManager.hpp"
+#include "InstanceCheck.hpp"
+#include "NotificationManager.hpp"
+#include "PresetComboBoxes.hpp"
+
+#ifdef __APPLE__
+#include "Gizmos/GLGizmosManager.hpp"
+#endif // __APPLE__
#include <wx/glcanvas.h> // Needs to be last because reasons :-/
#include "WipeTowerDialog.hpp"
@@ -92,10 +94,9 @@ namespace fs = boost::filesystem;
using Slic3r::_3DScene;
using Slic3r::Preset;
using Slic3r::PrintHostJob;
+using Slic3r::GUI::format_wxstr;
-#if ENABLE_THUMBNAIL_GENERATOR
static const std::pair<unsigned int, unsigned int> THUMBNAIL_SIZE_3MF = { 256, 256 };
-#endif // ENABLE_THUMBNAIL_GENERATOR
namespace Slic3r {
namespace GUI {
@@ -103,7 +104,8 @@ namespace GUI {
wxDEFINE_EVENT(EVT_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
wxDEFINE_EVENT(EVT_SLICING_UPDATE, SlicingStatusEvent);
wxDEFINE_EVENT(EVT_SLICING_COMPLETED, wxCommandEvent);
-wxDEFINE_EVENT(EVT_PROCESS_COMPLETED, wxCommandEvent);
+wxDEFINE_EVENT(EVT_PROCESS_COMPLETED, SlicingProcessCompletedEvent);
+wxDEFINE_EVENT(EVT_EXPORT_BEGAN, wxCommandEvent);
// Sidebar widgets
@@ -138,7 +140,7 @@ public:
};
ObjectInfo::ObjectInfo(wxWindow *parent) :
- wxStaticBoxSizer(new wxStaticBox(parent, wxID_ANY, _(L("Info"))), wxVERTICAL)
+ wxStaticBoxSizer(new wxStaticBox(parent, wxID_ANY, _L("Info")), wxVERTICAL)
{
GetStaticBox()->SetFont(wxGetApp().bold_font());
@@ -157,13 +159,13 @@ ObjectInfo::ObjectInfo(wxWindow *parent) :
return text;
};
- init_info_label(&info_size, _(L("Size")));
- label_volume = init_info_label(&info_volume, _(L("Volume")));
- init_info_label(&info_facets, _(L("Facets")));
- label_materials = init_info_label(&info_materials, _(L("Materials")));
+ init_info_label(&info_size, _L("Size"));
+ label_volume = init_info_label(&info_volume, _L("Volume"));
+ init_info_label(&info_facets, _L("Facets"));
+ label_materials = init_info_label(&info_materials, _L("Materials"));
Add(grid_sizer, 0, wxEXPAND);
- auto *info_manifold_text = new wxStaticText(parent, wxID_ANY, _(L("Manifold")) + ":");
+ auto *info_manifold_text = new wxStaticText(parent, wxID_ANY, _L("Manifold") + ":");
info_manifold_text->SetFont(wxGetApp().small_font());
info_manifold = new wxStaticText(parent, wxID_ANY, "");
info_manifold->SetFont(wxGetApp().small_font());
@@ -213,7 +215,7 @@ private:
};
SlicedInfo::SlicedInfo(wxWindow *parent) :
- wxStaticBoxSizer(new wxStaticBox(parent, wxID_ANY, _(L("Sliced Info"))), wxVERTICAL)
+ wxStaticBoxSizer(new wxStaticBox(parent, wxID_ANY, _L("Sliced Info")), wxVERTICAL)
{
GetStaticBox()->SetFont(wxGetApp().bold_font());
@@ -232,13 +234,13 @@ SlicedInfo::SlicedInfo(wxWindow *parent) :
info_vec.push_back(std::pair<wxStaticText*, wxStaticText*>(text, info_label));
};
- init_info_label(_(L("Used Filament (m)")));
- init_info_label(_(L("Used Filament (mm³)")));
- init_info_label(_(L("Used Filament (g)")));
- init_info_label(_(L("Used Material (unit)")));
- init_info_label(_(L("Cost (money)")));
- init_info_label(_(L("Estimated printing time")));
- init_info_label(_(L("Number of tool changes")));
+ init_info_label(_L("Used Filament (m)"));
+ init_info_label(_L("Used Filament (mm³)"));
+ init_info_label(_L("Used Filament (g)"));
+ init_info_label(_L("Used Material (unit)"));
+ init_info_label(_L("Cost (money)"));
+ init_info_label(_L("Estimated printing time"));
+ init_info_label(_L("Number of tool changes"));
Add(grid_sizer, 0, wxEXPAND);
this->Show(false);
@@ -255,150 +257,6 @@ void SlicedInfo::SetTextAndShow(SlicedInfoIdx idx, const wxString& text, const w
info_vec[idx].second->Show(show);
}
-PresetComboBox::PresetComboBox(wxWindow *parent, Preset::Type preset_type) :
-PresetBitmapComboBox(parent, wxSize(15 * wxGetApp().em_unit(), -1)),
- preset_type(preset_type),
- last_selected(wxNOT_FOUND),
- m_em_unit(wxGetApp().em_unit())
-{
- SetFont(wxGetApp().normal_font());
-#ifdef _WIN32
- // Workaround for ignoring CBN_EDITCHANGE events, which are processed after the content of the combo box changes, so that
- // the index of the item inside CBN_EDITCHANGE may no more be valid.
- EnableTextChangedEvents(false);
-#endif /* _WIN32 */
- Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &evt) {
- auto selected_item = this->GetSelection();
-
- auto marker = reinterpret_cast<Marker>(this->GetClientData(selected_item));
- if (marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX) {
- this->SetSelection(this->last_selected);
- evt.StopPropagation();
- if (marker >= LABEL_ITEM_WIZARD_PRINTERS) {
- ConfigWizard::StartPage sp = ConfigWizard::SP_WELCOME;
- switch (marker) {
- case LABEL_ITEM_WIZARD_PRINTERS: sp = ConfigWizard::SP_PRINTERS; break;
- case LABEL_ITEM_WIZARD_FILAMENTS: sp = ConfigWizard::SP_FILAMENTS; break;
- case LABEL_ITEM_WIZARD_MATERIALS: sp = ConfigWizard::SP_MATERIALS; break;
- }
- wxTheApp->CallAfter([sp]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, sp); });
- }
- } else if ( this->last_selected != selected_item ||
- wxGetApp().get_tab(this->preset_type)->get_presets()->current_is_dirty() ) {
- this->last_selected = selected_item;
- evt.SetInt(this->preset_type);
- evt.Skip();
- } else {
- evt.StopPropagation();
- }
- });
-
- if (preset_type == Slic3r::Preset::TYPE_FILAMENT)
- {
- Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &event) {
- PresetBundle* preset_bundle = wxGetApp().preset_bundle;
- const Preset* selected_preset = preset_bundle->filaments.find_preset(preset_bundle->filament_presets[extruder_idx]);
- // Wide icons are shown if the currently selected preset is not compatible with the current printer,
- // and red flag is drown in front of the selected preset.
- bool wide_icons = selected_preset != nullptr && !selected_preset->is_compatible;
- float scale = m_em_unit*0.1f;
-
- int shifl_Left = wide_icons ? int(scale * 16 + 0.5) : 0;
-#if defined(wxBITMAPCOMBOBOX_OWNERDRAWN_BASED)
- shifl_Left += int(scale * 4 + 0.5f); // IMAGE_SPACING_RIGHT = 4 for wxBitmapComboBox -> Space left of image
-#endif
- int icon_right_pos = shifl_Left + int(scale * (24+4) + 0.5);
- int mouse_pos = event.GetLogicalPosition(wxClientDC(this)).x;
- if (mouse_pos < shifl_Left || mouse_pos > icon_right_pos ) {
- // Let the combo box process the mouse click.
- event.Skip();
- return;
- }
-
- // Swallow the mouse click and open the color picker.
-
- // get current color
- DynamicPrintConfig* cfg = wxGetApp().get_tab(Preset::TYPE_PRINTER)->get_config();
- auto colors = static_cast<ConfigOptionStrings*>(cfg->option("extruder_colour")->clone());
- wxColour clr(colors->values[extruder_idx]);
- if (!clr.IsOk())
- clr = wxColour(0,0,0); // Don't set alfa to transparence
-
- auto data = new wxColourData();
- data->SetChooseFull(1);
- data->SetColour(clr);
-
- wxColourDialog dialog(this, data);
- dialog.CenterOnParent();
- if (dialog.ShowModal() == wxID_OK)
- {
- colors->values[extruder_idx] = dialog.GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString();
-
- DynamicPrintConfig cfg_new = *cfg;
- cfg_new.set_key_value("extruder_colour", colors);
-
- wxGetApp().get_tab(Preset::TYPE_PRINTER)->load_config(cfg_new);
- preset_bundle->update_plater_filament_ui(extruder_idx, this);
- wxGetApp().plater()->on_config_change(cfg_new);
- }
- });
- }
-
- edit_btn = new ScalableButton(parent, wxID_ANY, "cog");
- edit_btn->SetToolTip(_(L("Click to edit preset")));
-
- edit_btn->Bind(wxEVT_BUTTON, ([preset_type, this](wxCommandEvent)
- {
- Tab* tab = wxGetApp().get_tab(preset_type);
- if (!tab)
- return;
-
- int page_id = wxGetApp().tab_panel()->FindPage(tab);
- if (page_id == wxNOT_FOUND)
- return;
-
- wxGetApp().tab_panel()->ChangeSelection(page_id);
-
- /* In a case of a multi-material printing, for editing another Filament Preset
- * it's needed to select this preset for the "Filament settings" Tab
- */
- if (preset_type == Preset::TYPE_FILAMENT && wxGetApp().extruders_edited_cnt() > 1)
- {
- const std::string& selected_preset = GetString(GetSelection()).ToUTF8().data();
-
- // Call select_preset() only if there is new preset and not just modified
- if ( !boost::algorithm::ends_with(selected_preset, Preset::suffix_modified()) )
- {
- const std::string& preset_name = wxGetApp().preset_bundle->filaments.get_preset_name_by_alias(selected_preset);
- tab->select_preset(/*selected_preset*/preset_name);
- }
- }
- }));
-}
-
-PresetComboBox::~PresetComboBox()
-{
- if (edit_btn)
- edit_btn->Destroy();
-}
-
-
-void PresetComboBox::set_label_marker(int item, LabelItemType label_item_type)
-{
- this->SetClientData(item, (void*)label_item_type);
-}
-
-void PresetComboBox::check_selection()
-{
- this->last_selected = GetSelection();
-}
-
-void PresetComboBox::msw_rescale()
-{
- m_em_unit = wxGetApp().em_unit();
- edit_btn->msw_rescale();
-}
-
// Frequently changed parameters
class FreqChangedParams : public OG_Settings
@@ -444,11 +302,12 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) :
if (!tab_print) return;
if (opt_key == "fill_density") {
- value = m_og->get_config_value(*config, opt_key);
- tab_print->set_value(opt_key, value);
+ tab_print->update_dirty();
+ tab_print->reload_config();
tab_print->update();
}
- else{
+ else
+ {
DynamicPrintConfig new_conf = *config;
if (opt_key == "brim") {
double new_val;
@@ -489,8 +348,6 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) :
}
tab_print->load_config(new_conf);
}
-
- tab_print->update_dirty();
};
@@ -530,7 +387,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) :
option = m_og->get_option("fill_density");
option.opt.label = L("Infill");
- option.opt.width = 7/*6*/;
+ option.opt.width = 8;
option.opt.sidetext = " ";
line.append_option(option);
@@ -546,7 +403,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) :
line.append_option(option);
auto wiping_dialog_btn = [this](wxWindow* parent) {
- m_wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
+ m_wiping_dialog_button = new wxButton(parent, wxID_ANY, _L("Purging volumes") + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
m_wiping_dialog_button->SetFont(wxGetApp().normal_font());
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(m_wiping_dialog_button, 0, wxALIGN_CENTER_VERTICAL);
@@ -578,9 +435,12 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) :
return sizer;
};
line.append_widget(wiping_dialog_btn);
-
m_og->append_line(line);
+ m_og->activate();
+
+ Choice* choice = dynamic_cast<Choice*>(m_og->get_field("support"));
+ choice->suppress_scroll();
// Frequently changed parameters for SLA_technology
m_og_sla = std::make_shared<ConfigOptionsGroup>(parent, "");
@@ -588,7 +448,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) :
DynamicPrintConfig* config_sla = &wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
m_og_sla->set_config(config_sla);
- m_og_sla->m_on_change = [config_sla, this](t_config_option_key opt_key, boost::any value) {
+ m_og_sla->m_on_change = [config_sla](t_config_option_key opt_key, boost::any value) {
Tab* tab = wxGetApp().get_tab(Preset::TYPE_SLA_PRINT);
if (!tab) return;
@@ -652,6 +512,12 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) :
m_og_sla->append_line(line);
+ m_og_sla->activate();
+ choice = dynamic_cast<Choice*>(m_og_sla->get_field("support"));
+ choice->suppress_scroll();
+ choice = dynamic_cast<Choice*>(m_og_sla->get_field("pad"));
+ choice->suppress_scroll();
+
m_sizer = new wxBoxSizer(wxVERTICAL);
m_sizer->Add(m_og->sizer, 0, wxEXPAND);
m_sizer->Add(m_og_sla->sizer, 0, wxEXPAND);
@@ -696,12 +562,12 @@ struct Sidebar::priv
ModeSizer *mode_sizer;
wxFlexGridSizer *sizer_presets;
- PresetComboBox *combo_print;
- std::vector<PresetComboBox*> combos_filament;
+ PlaterPresetComboBox *combo_print;
+ std::vector<PlaterPresetComboBox*> combos_filament;
wxBoxSizer *sizer_filaments;
- PresetComboBox *combo_sla_print;
- PresetComboBox *combo_sla_material;
- PresetComboBox *combo_printer;
+ PlaterPresetComboBox *combo_sla_print;
+ PlaterPresetComboBox *combo_sla_material;
+ PlaterPresetComboBox *combo_printer;
wxBoxSizer *sizer_params;
FreqChangedParams *frequently_changed_parameters{ nullptr };
@@ -715,9 +581,12 @@ struct Sidebar::priv
wxButton *btn_export_gcode;
wxButton *btn_reslice;
ScalableButton *btn_send_gcode;
- ScalableButton *btn_remove_device;
+ //ScalableButton *btn_eject_device;
ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected)
+ bool is_collapsed {false};
+ Search::OptionsSearcher searcher;
+
priv(Plater *plater) : plater(plater) {}
~priv();
@@ -761,11 +630,15 @@ void Sidebar::priv::show_preset_comboboxes()
// Sidebar / public
Sidebar::Sidebar(Plater *parent)
- : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(40 * wxGetApp().em_unit(), -1)), p(new priv(parent))
+ : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(42 * wxGetApp().em_unit(), -1)), p(new priv(parent))
{
p->scrolled = new wxScrolledWindow(this);
p->scrolled->SetScrollbars(0, 100, 1, 2);
+ SetFont(wxGetApp().normal_font());
+#ifndef __APPLE__
+ SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
+#endif
// Sizer in the scrolled area
auto *scrolled_sizer = new wxBoxSizer(wxVERTICAL);
@@ -793,10 +666,10 @@ Sidebar::Sidebar(Plater *parent)
p->sizer_filaments = new wxBoxSizer(wxVERTICAL);
- auto init_combo = [this](PresetComboBox **combo, wxString label, Preset::Type preset_type, bool filament) {
+ auto init_combo = [this](PlaterPresetComboBox **combo, wxString label, Preset::Type preset_type, bool filament) {
auto *text = new wxStaticText(p->presets_panel, wxID_ANY, label + " :");
text->SetFont(wxGetApp().small_font());
- *combo = new PresetComboBox(p->presets_panel, preset_type);
+ *combo = new PlaterPresetComboBox(p->presets_panel, preset_type);
auto combo_and_btn_sizer = new wxBoxSizer(wxHORIZONTAL);
combo_and_btn_sizer->Add(*combo, 1, wxEXPAND);
@@ -817,11 +690,11 @@ Sidebar::Sidebar(Plater *parent)
};
p->combos_filament.push_back(nullptr);
- init_combo(&p->combo_print, _(L("Print settings")), Preset::TYPE_PRINT, false);
- init_combo(&p->combos_filament[0], _(L("Filament")), Preset::TYPE_FILAMENT, true);
- init_combo(&p->combo_sla_print, _(L("SLA print settings")), Preset::TYPE_SLA_PRINT, false);
- init_combo(&p->combo_sla_material, _(L("SLA material")), Preset::TYPE_SLA_MATERIAL, false);
- init_combo(&p->combo_printer, _(L("Printer")), Preset::TYPE_PRINTER, false);
+ init_combo(&p->combo_print, _L("Print settings"), Preset::TYPE_PRINT, false);
+ init_combo(&p->combos_filament[0], _L("Filament"), Preset::TYPE_FILAMENT, true);
+ init_combo(&p->combo_sla_print, _L("SLA print settings"), Preset::TYPE_SLA_PRINT, false);
+ init_combo(&p->combo_sla_material, _L("SLA material"), Preset::TYPE_SLA_MATERIAL, false);
+ init_combo(&p->combo_printer, _L("Printer"), Preset::TYPE_PRINTER, false);
const int margin_5 = int(0.5*wxGetApp().em_unit());// 5;
@@ -880,21 +753,22 @@ Sidebar::Sidebar(Plater *parent)
(*btn)->Hide();
};
- init_scalable_btn(&p->btn_send_gcode , "export_gcode", _(L("Send to printer")) + "\tCtrl+Shift+G");
- init_scalable_btn(&p->btn_remove_device, "eject_sd" , _(L("Remove device")) + "\tCtrl+T");
- init_scalable_btn(&p->btn_export_gcode_removable, "export_to_sd", _(L("Export to SD card / Flash drive")) + "\tCtrl+U");
+ init_scalable_btn(&p->btn_send_gcode , "export_gcode", _L("Send to printer") + " " +GUI::shortkey_ctrl_prefix() + "Shift+G");
+// init_scalable_btn(&p->btn_eject_device, "eject_sd" , _L("Remove device ") + GUI::shortkey_ctrl_prefix() + "T");
+ init_scalable_btn(&p->btn_export_gcode_removable, "export_to_sd", _L("Export to SD card / Flash drive") + " " + GUI::shortkey_ctrl_prefix() + "U");
// regular buttons "Slice now" and "Export G-code"
- const int scaled_height = p->btn_remove_device->GetBitmapHeight() + 4;
+// const int scaled_height = p->btn_eject_device->GetBitmapHeight() + 4;
+ const int scaled_height = p->btn_export_gcode_removable->GetBitmapHeight() + 4;
auto init_btn = [this](wxButton **btn, wxString label, const int button_height) {
*btn = new wxButton(this, wxID_ANY, label, wxDefaultPosition,
wxSize(-1, button_height), wxBU_EXACTFIT);
(*btn)->SetFont(wxGetApp().bold_font());
};
- init_btn(&p->btn_export_gcode, _(L("Export G-code")) + dots , scaled_height);
- init_btn(&p->btn_reslice , _(L("Slice now")) , scaled_height);
+ init_btn(&p->btn_export_gcode, _L("Export G-code") + dots , scaled_height);
+ init_btn(&p->btn_reslice , _L("Slice now") , scaled_height);
enable_buttons(false);
@@ -904,7 +778,7 @@ Sidebar::Sidebar(Plater *parent)
complect_btns_sizer->Add(p->btn_export_gcode, 1, wxEXPAND);
complect_btns_sizer->Add(p->btn_send_gcode);
complect_btns_sizer->Add(p->btn_export_gcode_removable);
- complect_btns_sizer->Add(p->btn_remove_device);
+// complect_btns_sizer->Add(p->btn_eject_device);
btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, margin_5);
@@ -921,20 +795,20 @@ Sidebar::Sidebar(Plater *parent)
{
const bool export_gcode_after_slicing = wxGetKeyState(WXK_SHIFT);
if (export_gcode_after_slicing)
- p->plater->export_gcode();
+ p->plater->export_gcode(true);
else
p->plater->reslice();
p->plater->select_view_3D("Preview");
});
p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); });
- p->btn_remove_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); });
+// p->btn_eject_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); });
p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); });
}
Sidebar::~Sidebar() {}
-void Sidebar::init_filament_combo(PresetComboBox **combo, const int extr_idx) {
- *combo = new PresetComboBox(p->presets_panel, Slic3r::Preset::TYPE_FILAMENT);
+void Sidebar::init_filament_combo(PlaterPresetComboBox **combo, const int extr_idx) {
+ *combo = new PlaterPresetComboBox(p->presets_panel, Slic3r::Preset::TYPE_FILAMENT);
// # copy icons from first choice
// $choice->SetItemBitmap($_, $choices->[0]->GetItemBitmap($_)) for 0..$#presets;
@@ -969,18 +843,18 @@ void Sidebar::update_all_preset_comboboxes()
// Update the print choosers to only contain the compatible presets, update the dirty flags.
if (print_tech == ptFFF)
- preset_bundle.prints.update_plater_ui(p->combo_print);
+ p->combo_print->update();
else {
- preset_bundle.sla_prints.update_plater_ui(p->combo_sla_print);
- preset_bundle.sla_materials.update_plater_ui(p->combo_sla_material);
+ p->combo_sla_print->update();
+ p->combo_sla_material->update();
}
// Update the printer choosers, update the dirty flags.
- preset_bundle.printers.update_plater_ui(p->combo_printer);
+ p->combo_printer->update();
// Update the filament choosers to only contain the compatible presets, update the color preview,
// update the dirty flags.
if (print_tech == ptFFF) {
- for (size_t i = 0; i < p->combos_filament.size(); ++i)
- preset_bundle.update_plater_filament_ui(i, p->combos_filament[i]);
+ for (PlaterPresetComboBox* cb : p->combos_filament)
+ cb->update();
}
}
@@ -1002,23 +876,22 @@ void Sidebar::update_presets(Preset::Type preset_type)
preset_bundle.set_filament_preset(0, name);
}
- for (size_t i = 0; i < filament_cnt; i++) {
- preset_bundle.update_plater_filament_ui(i, p->combos_filament[i]);
- }
+ for (size_t i = 0; i < filament_cnt; i++)
+ p->combos_filament[i]->update();
break;
}
case Preset::TYPE_PRINT:
- preset_bundle.prints.update_plater_ui(p->combo_print);
+ p->combo_print->update();
break;
case Preset::TYPE_SLA_PRINT:
- preset_bundle.sla_prints.update_plater_ui(p->combo_sla_print);
+ p->combo_sla_print->update();
break;
case Preset::TYPE_SLA_MATERIAL:
- preset_bundle.sla_materials.update_plater_ui(p->combo_sla_material);
+ p->combo_sla_material->update();
break;
case Preset::TYPE_PRINTER:
@@ -1040,11 +913,17 @@ void Sidebar::update_mode_sizer() const
p->mode_sizer->SetMode(m_mode);
}
+void Sidebar::change_top_border_for_mode_sizer(bool increase_border)
+{
+ p->mode_sizer->set_items_flag(increase_border ? wxTOP : 0);
+ p->mode_sizer->set_items_border(increase_border ? int(0.5 * wxGetApp().em_unit()) : 0);
+}
+
void Sidebar::update_reslice_btn_tooltip() const
{
wxString tooltip = wxString("Slice") + " [" + GUI::shortkey_ctrl_prefix() + "R]";
if (m_mode != comSimple)
- tooltip += wxString("\n") + _(L("Hold Shift to Slice & Export G-code"));
+ tooltip += wxString("\n") + _L("Hold Shift to Slice & Export G-code");
p->btn_reslice->SetToolTip(tooltip);
}
@@ -1054,18 +933,14 @@ void Sidebar::msw_rescale()
p->mode_sizer->msw_rescale();
- // Rescale preset comboboxes in respect to the current em_unit ...
- for (PresetComboBox* combo : std::vector<PresetComboBox*> { p->combo_print,
+ for (PlaterPresetComboBox* combo : std::vector<PlaterPresetComboBox*> { p->combo_print,
p->combo_sla_print,
p->combo_sla_material,
p->combo_printer } )
combo->msw_rescale();
- for (PresetComboBox* combo : p->combos_filament)
+ for (PlaterPresetComboBox* combo : p->combos_filament)
combo->msw_rescale();
- // ... then refill them and set min size to correct layout of the sidebar
- update_all_preset_comboboxes();
-
p->frequently_changed_parameters->msw_rescale();
p->object_list->msw_rescale();
p->object_manipulation->msw_rescale();
@@ -1075,15 +950,51 @@ void Sidebar::msw_rescale()
p->object_info->msw_rescale();
p->btn_send_gcode->msw_rescale();
- p->btn_remove_device->msw_rescale();
+// p->btn_eject_device->msw_rescale();
p->btn_export_gcode_removable->msw_rescale();
- const int scaled_height = p->btn_remove_device->GetBitmap().GetHeight() + 4;
+ const int scaled_height = p->btn_export_gcode_removable->GetBitmap().GetHeight() + 4;
p->btn_export_gcode->SetMinSize(wxSize(-1, scaled_height));
p->btn_reslice ->SetMinSize(wxSize(-1, scaled_height));
p->scrolled->Layout();
}
+void Sidebar::sys_color_changed()
+{
+ for (PlaterPresetComboBox* combo : std::vector<PlaterPresetComboBox*>{ p->combo_print,
+ p->combo_sla_print,
+ p->combo_sla_material,
+ p->combo_printer })
+ combo->msw_rescale();
+ for (PlaterPresetComboBox* combo : p->combos_filament)
+ combo->msw_rescale();
+
+ p->object_list->sys_color_changed();
+ p->object_manipulation->sys_color_changed();
+ p->object_layers->sys_color_changed();
+
+ // btn...->msw_rescale() updates icon on button, so use it
+ p->btn_send_gcode->msw_rescale();
+// p->btn_eject_device->msw_rescale();
+ p->btn_export_gcode_removable->msw_rescale();
+
+ p->scrolled->Layout();
+}
+
+void Sidebar::search()
+{
+ p->searcher.search();
+}
+
+void Sidebar::jump_to_option(size_t selected)
+{
+ const Search::Option& opt = p->searcher.get_option(selected);
+ wxGetApp().get_tab(opt.type)->activate_option(boost::nowide::narrow(opt.opt_key), boost::nowide::narrow(opt.category));
+
+ // Switch to the Settings NotePad
+// wxGetApp().mainframe->select_tab();
+}
+
ObjectManipulation* Sidebar::obj_manipul()
{
return p->object_manipulation;
@@ -1148,22 +1059,25 @@ void Sidebar::show_info_sizer()
return;
}
+ bool imperial_units = wxGetApp().app_config->get("use_inches") == "1";
+ double koef = imperial_units ? ObjectManipulation::mm_to_in : 1.0f;
+
auto size = model_object->bounding_box().size();
- p->object_info->info_size->SetLabel(wxString::Format("%.2f x %.2f x %.2f",size(0), size(1), size(2)));
+ p->object_info->info_size->SetLabel(wxString::Format("%.2f x %.2f x %.2f",size(0)*koef, size(1)*koef, size(2)*koef));
p->object_info->info_materials->SetLabel(wxString::Format("%d", static_cast<int>(model_object->materials_count())));
const auto& stats = model_object->get_object_stl_stats();//model_object->volumes.front()->mesh.stl.stats;
- p->object_info->info_volume->SetLabel(wxString::Format("%.2f", stats.volume));
- p->object_info->info_facets->SetLabel(wxString::Format(_(L("%d (%d shells)")), static_cast<int>(model_object->facets_count()), stats.number_of_parts));
+ p->object_info->info_volume->SetLabel(wxString::Format("%.2f", stats.volume*pow(koef,3)));
+ p->object_info->info_facets->SetLabel(wxString::Format(_L("%d (%d shells)"), static_cast<int>(model_object->facets_count()), stats.number_of_parts));
int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
stats.facets_added + stats.facets_reversed + stats.backwards_edges;
if (errors > 0) {
- wxString tooltip = wxString::Format(_(L("Auto-repaired (%d errors)")), errors);
+ wxString tooltip = wxString::Format(_L("Auto-repaired (%d errors)"), errors);
p->object_info->info_manifold->SetLabel(tooltip);
- tooltip += ":\n" + wxString::Format(_(L("%d degenerate facets, %d edges fixed, %d facets removed, "
- "%d facets added, %d facets reversed, %d backwards edges")),
+ tooltip += ":\n" + wxString::Format(_L("%d degenerate facets, %d edges fixed, %d facets removed, "
+ "%d facets added, %d facets reversed, %d backwards edges"),
stats.degenerate_facets, stats.edges_fixed, stats.facets_removed,
stats.facets_added, stats.facets_reversed, stats.backwards_edges);
@@ -1172,7 +1086,7 @@ void Sidebar::show_info_sizer()
p->object_info->manifold_warning_icon->SetToolTip(tooltip);
}
else {
- p->object_info->info_manifold->SetLabel(_(L("Yes")));
+ p->object_info->info_manifold->SetLabel(_L("Yes"));
p->object_info->showing_manifold_warning_icon = false;
p->object_info->info_manifold->SetToolTip("");
p->object_info->manifold_warning_icon->SetToolTip("");
@@ -1193,10 +1107,10 @@ void Sidebar::update_sliced_info_sizer()
if (p->plater->printer_technology() == ptSLA)
{
const SLAPrintStatistics& ps = p->plater->sla_print().print_statistics();
- wxString new_label = _(L("Used Material (ml)")) + ":";
+ wxString new_label = _L("Used Material (ml)") + ":";
const bool is_supports = ps.support_used_material > 0.0;
if (is_supports)
- new_label += from_u8((boost::format("\n - %s\n - %s") % _utf8(L("object(s)")) % _utf8(L("supports and pad"))).str());
+ new_label += format_wxstr("\n - %s\n - %s", _L("object(s)"), _L("supports and pad"));
wxString info_text = is_supports ?
wxString::Format("%.2f \n%.2f \n%.2f", (ps.objects_used_material + ps.support_used_material) / 1000,
@@ -1218,7 +1132,7 @@ void Sidebar::update_sliced_info_sizer()
p->sliced_info->SetTextAndShow(siCost, str_total_cost, "Cost");
wxString t_est = std::isnan(ps.estimated_print_time) ? "N/A" : get_time_dhms(float(ps.estimated_print_time));
- p->sliced_info->SetTextAndShow(siEstimatedTime, t_est, _(L("Estimated printing time")) + ":");
+ p->sliced_info->SetTextAndShow(siEstimatedTime, t_est, _L("Estimated printing time") + ":");
// Hide non-SLA sliced info parameters
p->sliced_info->SetTextAndShow(siFilament_m, "N/A");
@@ -1231,23 +1145,65 @@ void Sidebar::update_sliced_info_sizer()
const PrintStatistics& ps = p->plater->fff_print().print_statistics();
const bool is_wipe_tower = ps.total_wipe_tower_filament > 0;
- wxString new_label = _(L("Used Filament (m)"));
+ bool imperial_units = wxGetApp().app_config->get("use_inches") == "1";
+ double koef = imperial_units ? ObjectManipulation::in_to_mm : 1000.0;
+
+ wxString new_label = imperial_units ? _L("Used Filament (in)") : _L("Used Filament (m)");
if (is_wipe_tower)
- new_label += from_u8((boost::format(":\n - %1%\n - %2%") % _utf8(L("objects")) % _utf8(L("wipe tower"))).str());
+ new_label += format_wxstr(":\n - %1%\n - %2%", _L("objects"), _L("wipe tower"));
wxString info_text = is_wipe_tower ?
- wxString::Format("%.2f \n%.2f \n%.2f", ps.total_used_filament / 1000,
- (ps.total_used_filament - ps.total_wipe_tower_filament) / 1000,
- ps.total_wipe_tower_filament / 1000) :
- wxString::Format("%.2f", ps.total_used_filament / 1000);
+ wxString::Format("%.2f \n%.2f \n%.2f", ps.total_used_filament / /*1000*/koef,
+ (ps.total_used_filament - ps.total_wipe_tower_filament) / /*1000*/koef,
+ ps.total_wipe_tower_filament / /*1000*/koef) :
+ wxString::Format("%.2f", ps.total_used_filament / /*1000*/koef);
p->sliced_info->SetTextAndShow(siFilament_m, info_text, new_label);
- p->sliced_info->SetTextAndShow(siFilament_mm3, wxString::Format("%.2f", ps.total_extruded_volume));
- p->sliced_info->SetTextAndShow(siFilament_g, ps.total_weight == 0.0 ? "N/A" : wxString::Format("%.2f", ps.total_weight));
+ koef = imperial_units ? pow(ObjectManipulation::mm_to_in, 3) : 1.0f;
+ new_label = imperial_units ? _L("Used Filament (in³)") : _L("Used Filament (mm³)");
+ info_text = wxString::Format("%.2f", imperial_units ? ps.total_extruded_volume * koef : ps.total_extruded_volume);
+ p->sliced_info->SetTextAndShow(siFilament_mm3, info_text, new_label);
+
+ if (ps.total_weight == 0.0)
+ p->sliced_info->SetTextAndShow(siFilament_g, "N/A");
+ else {
+ new_label = _L("Used Filament (g)");
+ info_text = wxString::Format("%.2f", ps.total_weight);
+
+ const std::vector<std::string>& filament_presets = wxGetApp().preset_bundle->filament_presets;
+ const PresetCollection& filaments = wxGetApp().preset_bundle->filaments;
+
+ if (ps.filament_stats.size() > 1)
+ new_label += ":";
+
+ for (auto filament : ps.filament_stats) {
+ const Preset* filament_preset = filaments.find_preset(filament_presets[filament.first], false);
+ if (filament_preset) {
+ double filament_weight;
+ if (ps.filament_stats.size() == 1)
+ filament_weight = ps.total_weight;
+ else {
+ double filament_density = filament_preset->config.opt_float("filament_density", 0);
+ filament_weight = filament.second * filament_density * 2.4052f * 0.001; // assumes 1.75mm filament diameter;
+
+ new_label += "\n - " + format_wxstr(_L("Filament at extruder %1%"), filament.first + 1);
+ info_text += wxString::Format("\n%.2f", filament_weight);
+ }
+
+ double spool_weight = filament_preset->config.opt_float("filament_spool_weight", 0);
+ if (spool_weight != 0.0) {
+ new_label += "\n " + _L("(including spool)");
+ info_text += wxString::Format(" (%.2f)\n", filament_weight + spool_weight);
+ }
+ }
+ }
+
+ p->sliced_info->SetTextAndShow(siFilament_g, info_text, new_label);
+ }
- new_label = _(L("Cost"));
+ new_label = _L("Cost");
if (is_wipe_tower)
- new_label += from_u8((boost::format(":\n - %1%\n - %2%") % _utf8(L("objects")) % _utf8(L("wipe tower"))).str());
+ new_label += format_wxstr(":\n - %1%\n - %2%", _L("objects"), _L("wipe tower"));
info_text = ps.total_cost == 0.0 ? "N/A" :
is_wipe_tower ?
@@ -1260,44 +1216,23 @@ void Sidebar::update_sliced_info_sizer()
if (ps.estimated_normal_print_time == "N/A" && ps.estimated_silent_print_time == "N/A")
p->sliced_info->SetTextAndShow(siEstimatedTime, "N/A");
else {
- new_label = _(L("Estimated printing time")) +":";
info_text = "";
- wxString str_color = _(L("Color"));
- wxString str_pause = _(L("Pause"));
-
- auto fill_labels = [str_color, str_pause](const std::vector<std::pair<CustomGcodeType, std::string>>& times,
- wxString& new_label, wxString& info_text)
- {
- int color_change_count = 0;
- for (auto time : times)
- if (time.first == cgtColorChange)
- color_change_count++;
-
- for (int i = (int)times.size() - 1; i >= 0; --i)
- {
- if (i == 0 || times[i - 1].first == cgtPausePrint)
- new_label += from_u8((boost::format("\n - %1%%2%") % (std::string(str_color.ToUTF8()) + " ") % color_change_count).str());
- else if (times[i - 1].first == cgtColorChange)
- new_label += from_u8((boost::format("\n - %1%%2%") % (std::string(str_color.ToUTF8()) + " ") % color_change_count--).str());
-
- if (i != (int)times.size() - 1 && times[i].first == cgtPausePrint)
- new_label += from_u8((boost::format(" -> %1%") % std::string(str_pause.ToUTF8())).str());
+ new_label = _L("Estimated printing time") + ":";
+ if (ps.estimated_normal_print_time != "N/A") {
+ new_label += format_wxstr("\n - %1%", _L("normal mode"));
+ info_text += format_wxstr("\n%1%", short_time(ps.estimated_normal_print_time));
- info_text += from_u8((boost::format("\n%1%") % times[i].second).str());
- }
- };
+ // uncomment next line to not disappear slicing finished notif when colapsing sidebar before time estimate
+ //if (p->plater->is_sidebar_collapsed())
+ p->plater->get_notification_manager()->set_slicing_complete_large(p->plater->is_sidebar_collapsed());
+ p->plater->get_notification_manager()->set_slicing_complete_print_time("Estimated printing time: " + ps.estimated_normal_print_time);
- if (ps.estimated_normal_print_time != "N/A") {
- new_label += from_u8((boost::format("\n - %1%") % _utf8(L("normal mode"))).str());
- info_text += from_u8((boost::format("\n%1%") % ps.estimated_normal_print_time).str());
- fill_labels(ps.estimated_normal_custom_gcode_print_times, new_label, info_text);
}
if (ps.estimated_silent_print_time != "N/A") {
- new_label += from_u8((boost::format("\n - %1%") % _utf8(L("stealth mode"))).str());
- info_text += from_u8((boost::format("\n%1%") % ps.estimated_silent_print_time).str());
- fill_labels(ps.estimated_silent_custom_gcode_print_times, new_label, info_text);
+ new_label += format_wxstr("\n - %1%", _L("stealth mode"));
+ info_text += format_wxstr("\n%1%", short_time(ps.estimated_silent_print_time));
}
- p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label);
+ p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label);
}
// if there is a wipe tower, insert number of toolchanges info into the array:
@@ -1307,6 +1242,8 @@ void Sidebar::update_sliced_info_sizer()
p->sliced_info->SetTextAndShow(siMateril_unit, "N/A");
}
}
+
+ Layout();
}
void Sidebar::show_sliced_info_sizer(const bool show)
@@ -1326,21 +1263,39 @@ void Sidebar::enable_buttons(bool enable)
p->btn_reslice->Enable(enable);
p->btn_export_gcode->Enable(enable);
p->btn_send_gcode->Enable(enable);
- p->btn_remove_device->Enable(enable);
+// p->btn_eject_device->Enable(enable);
p->btn_export_gcode_removable->Enable(enable);
}
-bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Show(show); }
-bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); }
-bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); }
-bool Sidebar::show_disconnect(bool show) const { return p->btn_remove_device->Show(show); }
-bool Sidebar::show_export_removable(bool show)const { return p->btn_export_gcode_removable->Show(show); }
+bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Show(show); }
+bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); }
+bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); }
+bool Sidebar::show_export_removable(bool show) const { return p->btn_export_gcode_removable->Show(show); }
+//bool Sidebar::show_eject(bool show) const { return p->btn_eject_device->Show(show); }
+//bool Sidebar::get_eject_shown() const { return p->btn_eject_device->IsShown(); }
bool Sidebar::is_multifilament()
{
return p->combos_filament.size() > 1;
}
+static std::vector<Search::InputInfo> get_search_inputs(ConfigOptionMode mode)
+{
+ std::vector<Search::InputInfo> ret {};
+
+ auto& tabs_list = wxGetApp().tabs_list;
+ auto print_tech = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology();
+ for (auto tab : tabs_list)
+ if (tab->supports_printer_technology(print_tech))
+ ret.emplace_back(Search::InputInfo {tab->get_config(), tab->type(), mode});
+
+ return ret;
+}
+
+void Sidebar::update_searcher()
+{
+ p->searcher.init(get_search_inputs(m_mode));
+}
void Sidebar::update_mode()
{
@@ -1348,6 +1303,7 @@ void Sidebar::update_mode()
update_reslice_btn_tooltip();
update_mode_sizer();
+ update_searcher();
wxWindowUpdateLocker noUpdates(this);
@@ -1360,81 +1316,260 @@ void Sidebar::update_mode()
Layout();
}
-std::vector<PresetComboBox*>& Sidebar::combos_filament()
+bool Sidebar::is_collapsed() { return p->is_collapsed; }
+
+void Sidebar::collapse(bool collapse)
+{
+ p->is_collapsed = collapse;
+
+ this->Show(!collapse);
+ p->plater->Layout();
+
+ // save collapsing state to the AppConfig
+ if (wxGetApp().is_editor())
+ wxGetApp().app_config->set("collapsed_sidebar", collapse ? "1" : "0");
+}
+
+
+void Sidebar::update_ui_from_settings()
+{
+ p->object_manipulation->update_ui_from_settings();
+ show_info_sizer();
+ update_sliced_info_sizer();
+ // update Cut gizmo, if it's open
+ p->plater->canvas3D()->update_gizmos_on_off_state();
+ p->plater->canvas3D()->request_extra_frame();
+}
+
+std::vector<PlaterPresetComboBox*>& Sidebar::combos_filament()
{
return p->combos_filament;
}
+Search::OptionsSearcher& Sidebar::get_searcher()
+{
+ return p->searcher;
+}
+
+std::string& Sidebar::get_search_line()
+{
+ return p->searcher.search_string();
+}
+
// Plater::DropTarget
class PlaterDropTarget : public wxFileDropTarget
{
public:
- PlaterDropTarget(Plater *plater) : plater(plater) { this->SetDefaultAction(wxDragCopy); }
+ PlaterDropTarget(Plater* plater) : m_plater(plater) { this->SetDefaultAction(wxDragCopy); }
virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames);
private:
- Plater *plater;
+ Plater* m_plater;
+#if !ENABLE_DRAG_AND_DROP_FIX
static const std::regex pattern_drop;
+ static const std::regex pattern_gcode_drop;
+#endif // !ENABLE_DRAG_AND_DROP_FIX
};
+#if !ENABLE_DRAG_AND_DROP_FIX
const std::regex PlaterDropTarget::pattern_drop(".*[.](stl|obj|amf|3mf|prusa)", std::regex::icase);
+const std::regex PlaterDropTarget::pattern_gcode_drop(".*[.](gcode|g)", std::regex::icase);
+
+enum class LoadType : unsigned char
+{
+ Unknown,
+ OpenProject,
+ LoadGeometry,
+ LoadConfig
+};
+
+class ProjectDropDialog : public DPIDialog
+{
+ wxRadioBox* m_action{ nullptr };
+public:
+ ProjectDropDialog(const std::string& filename);
+
+ int get_action() const { return m_action->GetSelection() + 1; }
+
+protected:
+ void on_dpi_changed(const wxRect& suggested_rect) override;
+};
+
+ProjectDropDialog::ProjectDropDialog(const std::string& filename)
+ : DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY,
+ from_u8((boost::format(_utf8(L("%s - Drop project file"))) % SLIC3R_APP_NAME).str()), wxDefaultPosition,
+ wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
+{
+ SetFont(wxGetApp().normal_font());
+
+ wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
+
+ const wxString choices[] = { _L("Open as project"),
+ _L("Import geometry only"),
+ _L("Import config only") };
+
+ main_sizer->Add(new wxStaticText(this, wxID_ANY,
+ _L("Select an action to apply to the file") + ": " + from_u8(filename)), 0, wxEXPAND | wxALL, 10);
+ m_action = new wxRadioBox(this, wxID_ANY, _L("Action"), wxDefaultPosition, wxDefaultSize,
+ WXSIZEOF(choices), choices, 0, wxRA_SPECIFY_ROWS);
+ int action = std::clamp(std::stoi(wxGetApp().app_config->get("drop_project_action")),
+ static_cast<int>(LoadType::OpenProject), static_cast<int>(LoadType::LoadConfig)) - 1;
+ m_action->SetSelection(action);
+ main_sizer->Add(m_action, 1, wxEXPAND | wxRIGHT | wxLEFT, 10);
+
+ wxBoxSizer* bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
+ wxCheckBox* check = new wxCheckBox(this, wxID_ANY, _L("Don't show again"));
+ check->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& evt) {
+ wxGetApp().app_config->set("show_drop_project_dialog", evt.IsChecked() ? "0" : "1");
+ });
+
+ bottom_sizer->Add(check, 0, wxEXPAND | wxRIGHT, 5);
+ bottom_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxLEFT, 5);
+ main_sizer->Add(bottom_sizer, 0, wxEXPAND | wxALL, 10);
+
+ SetSizer(main_sizer);
+ main_sizer->SetSizeHints(this);
+}
+
+void ProjectDropDialog::on_dpi_changed(const wxRect& suggested_rect)
+{
+ const int em = em_unit();
+ SetMinSize(wxSize(65 * em, 30 * em));
+ Fit();
+ Refresh();
+}
+#endif // !ENABLE_DRAG_AND_DROP_FIX
bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames)
{
+#if !ENABLE_DRAG_AND_DROP_FIX
std::vector<fs::path> paths;
+#endif // !ENABLE_DRAG_AND_DROP_FIX
+
+#ifdef WIN32
+ // hides the system icon
+ this->MSWUpdateDragImageOnLeave();
+#endif // WIN32
+
+#if ENABLE_DRAG_AND_DROP_FIX
+ return (m_plater != nullptr) ? m_plater->load_files(filenames) : false;
+#else
+ // gcode viewer section
+ if (wxGetApp().is_gcode_viewer()) {
+ for (const auto& filename : filenames) {
+ fs::path path(into_path(filename));
+ if (std::regex_match(path.string(), pattern_gcode_drop))
+ paths.push_back(std::move(path));
+ }
+
+ if (paths.size() > 1) {
+ wxMessageDialog(static_cast<wxWindow*>(m_plater), _L("You can open only one .gcode file at a time."),
+ wxString(SLIC3R_APP_NAME) + " - " + _L("Drag and drop G-code file"), wxCLOSE | wxICON_WARNING | wxCENTRE).ShowModal();
+ return false;
+ }
+ else if (paths.size() == 1) {
+ m_plater->load_gcode(from_path(paths.front()));
+ return true;
+ }
+ return false;
+ }
+
+ // editor section
for (const auto &filename : filenames) {
fs::path path(into_path(filename));
- if (std::regex_match(path.string(), pattern_drop)) {
+ if (std::regex_match(path.string(), pattern_drop))
paths.push_back(std::move(path));
- } else {
+ else if (std::regex_match(path.string(), pattern_gcode_drop))
+ start_new_gcodeviewer(&filename);
+ else
return false;
+ }
+ if (paths.empty())
+ // Likely all paths processed were gcodes, for which a G-code viewer instance has hopefully been started.
+ return false;
+
+ // searches for project files
+ for (std::vector<fs::path>::const_reverse_iterator it = paths.rbegin(); it != paths.rend(); ++it) {
+ std::string filename = (*it).filename().string();
+ if (boost::algorithm::iends_with(filename, ".3mf") || boost::algorithm::iends_with(filename, ".amf")) {
+ LoadType load_type = LoadType::Unknown;
+ if (!m_plater->model().objects.empty()) {
+ if (wxGetApp().app_config->get("show_drop_project_dialog") == "1") {
+ ProjectDropDialog dlg(filename);
+ if (dlg.ShowModal() == wxID_OK) {
+ int choice = dlg.get_action();
+ load_type = static_cast<LoadType>(choice);
+ wxGetApp().app_config->set("drop_project_action", std::to_string(choice));
+ }
+ }
+ else
+ load_type = static_cast<LoadType>(std::clamp(std::stoi(wxGetApp().app_config->get("drop_project_action")),
+ static_cast<int>(LoadType::OpenProject), static_cast<int>(LoadType::LoadConfig)));
+ }
+ else
+ load_type = LoadType::OpenProject;
+
+ if (load_type == LoadType::Unknown)
+ return false;
+
+ switch (load_type) {
+ case LoadType::OpenProject: {
+ m_plater->load_project(from_path(*it));
+ break;
+ }
+ case LoadType::LoadGeometry: {
+ Plater::TakeSnapshot snapshot(m_plater, _L("Import Object"));
+ std::vector<fs::path> in_paths;
+ in_paths.emplace_back(*it);
+ m_plater->load_files(in_paths, true, false);
+ break;
+ }
+ case LoadType::LoadConfig: {
+ std::vector<fs::path> in_paths;
+ in_paths.emplace_back(*it);
+ m_plater->load_files(in_paths, false, true);
+ break;
+ }
+ }
+
+ return true;
}
}
+ // other files
wxString snapshot_label;
- assert(! paths.empty());
+ assert(!paths.empty());
if (paths.size() == 1) {
- snapshot_label = _(L("Load File"));
+ snapshot_label = _L("Load File");
snapshot_label += ": ";
snapshot_label += wxString::FromUTF8(paths.front().filename().string().c_str());
- } else {
- snapshot_label = _(L("Load Files"));
+ }
+ else {
+ snapshot_label = _L("Load Files");
snapshot_label += ": ";
snapshot_label += wxString::FromUTF8(paths.front().filename().string().c_str());
- for (size_t i = 1; i < paths.size(); ++ i) {
+ for (size_t i = 1; i < paths.size(); ++i) {
snapshot_label += ", ";
snapshot_label += wxString::FromUTF8(paths[i].filename().string().c_str());
}
}
- Plater::TakeSnapshot snapshot(plater, snapshot_label);
-
- // FIXME: when drag and drop is done on a .3mf or a .amf file we should clear the plater for consistence with the open project command
- // (the following call to plater->load_files() will load the config data, if present)
-
- std::vector<size_t> res = plater->load_files(paths);
-
- // because right now the plater is not cleared, we set the project file (from the latest imported .3mf or .amf file)
- // only if not set yet
- // if res is empty no data has been loaded
- if (!res.empty() && plater->get_project_filename().empty())
- {
- for (std::vector<fs::path>::const_reverse_iterator it = paths.rbegin(); it != paths.rend(); ++it)
- {
- std::string filename = (*it).filename().string();
- if (boost::algorithm::iends_with(filename, ".3mf") || boost::algorithm::iends_with(filename, ".amf"))
- {
- plater->set_project_filename(from_path(*it));
- break;
- }
- }
- }
+ Plater::TakeSnapshot snapshot(m_plater, snapshot_label);
+ m_plater->load_files(paths);
return true;
+#endif // ENABLE_DRAG_AND_DROP_FIX
}
+// State to manage showing after export notifications and device ejecting
+enum ExportingStatus{
+ NOT_EXPORTING,
+ EXPORTING_TO_REMOVABLE,
+ EXPORTING_TO_LOCAL
+};
+
// Plater / private
struct Plater::priv
{
@@ -1466,7 +1601,7 @@ struct Plater::priv
Slic3r::SLAPrint sla_print;
Slic3r::Model model;
PrinterTechnology printer_technology = ptFFF;
- Slic3r::GCodePreviewData gcode_preview_data;
+ Slic3r::GCodeProcessor::Result gcode_result;
// GUI elements
wxSizer* panel_sizer{ nullptr };
@@ -1475,338 +1610,64 @@ struct Plater::priv
Sidebar *sidebar;
Bed3D bed;
Camera camera;
+#if ENABLE_ENVIRONMENT_MAP
+ GLTexture environment_texture;
+#endif // ENABLE_ENVIRONMENT_MAP
Mouse3DController mouse3d_controller;
View3D* view3D;
GLToolbar view_toolbar;
+ GLToolbar collapse_toolbar;
Preview *preview;
+ NotificationManager* notification_manager { nullptr };
BackgroundSlicingProcess background_process;
bool suppressed_backround_processing_update { false };
- // Cache the wti info
- class WipeTower: public GLCanvas3D::WipeTowerInfo {
- using ArrangePolygon = arrangement::ArrangePolygon;
- friend priv;
- public:
-
- void apply_arrange_result(const Vec2crd& tr, double rotation)
- {
- m_pos = unscaled(tr); m_rotation = rotation;
- apply_wipe_tower();
- }
-
- ArrangePolygon get_arrange_polygon() const
- {
- Polygon p({
- {coord_t(0), coord_t(0)},
- {scaled(m_bb_size(X)), coord_t(0)},
- {scaled(m_bb_size)},
- {coord_t(0), scaled(m_bb_size(Y))},
- {coord_t(0), coord_t(0)},
- });
-
- ArrangePolygon ret;
- ret.poly.contour = std::move(p);
- ret.translation = scaled(m_pos);
- ret.rotation = m_rotation;
- ret.priority++;
- return ret;
- }
- } wipetower;
-
- WipeTower& updated_wipe_tower() {
- auto wti = view3D->get_canvas3d()->get_wipe_tower_info();
- wipetower.m_pos = wti.pos();
- wipetower.m_rotation = wti.rotation();
- wipetower.m_bb_size = wti.bb_size();
- return wipetower;
- }
-
- // A class to handle UI jobs like arranging and optimizing rotation.
- // These are not instant jobs, the user has to be informed about their
- // state in the status progress indicator. On the other hand they are
- // separated from the background slicing process. Ideally, these jobs should
- // run when the background process is not running.
- //
- // TODO: A mechanism would be useful for blocking the plater interactions:
- // objects would be frozen for the user. In case of arrange, an animation
- // could be shown, or with the optimize orientations, partial results
- // could be displayed.
- class PlaterJob: public Job
+ // Jobs defined inside the group class will be managed so that only one can
+ // run at a time. Also, the background process will be stopped if a job is
+ // started. It is up the the plater to ensure that the background slicing
+ // can't be restarted while a ui job is still running.
+ class Jobs: public ExclusiveJobGroup
{
- priv *m_plater;
- protected:
-
- priv & plater() { return *m_plater; }
- const priv &plater() const { return *m_plater; }
-
- // Launched when the job is finished. It refreshes the 3Dscene by def.
- void finalize() override
- {
- // Do a full refresh of scene tree, including regenerating
- // all the GLVolumes. FIXME The update function shall just
- // reload the modified matrices.
- if (!Job::was_canceled())
- plater().update(unsigned(UpdateParams::FORCE_FULL_SCREEN_REFRESH));
-
- Job::finalize();
- }
-
+ priv *m;
+ size_t m_arrange_id, m_fill_bed_id, m_rotoptimize_id, m_sla_import_id;
+
+ void before_start() override { m->background_process.stop(); }
+
public:
- PlaterJob(priv *_plater)
- : Job(_plater->statusbar()), m_plater(_plater)
- {}
- };
-
- enum class Jobs : size_t {
- Arrange,
- Rotoptimize,
- Hollow
- };
-
- class ArrangeJob : public PlaterJob
- {
- using ArrangePolygon = arrangement::ArrangePolygon;
- using ArrangePolygons = arrangement::ArrangePolygons;
-
- // The gap between logical beds in the x axis expressed in ratio of
- // the current bed width.
- static const constexpr double LOGICAL_BED_GAP = 1. / 5.;
-
- ArrangePolygons m_selected, m_unselected, m_unprintable;
-
- // clear m_selected and m_unselected, reserve space for next usage
- void clear_input() {
- const Model &model = plater().model;
-
- size_t count = 0, cunprint = 0; // To know how much space to reserve
- for (auto obj : model.objects)
- for (auto mi : obj->instances)
- mi->printable ? count++ : cunprint++;
-
- m_selected.clear();
- m_unselected.clear();
- m_unprintable.clear();
- m_selected.reserve(count + 1 /* for optional wti */);
- m_unselected.reserve(count + 1 /* for optional wti */);
- m_unprintable.reserve(cunprint /* for optional wti */);
- }
-
- // Stride between logical beds
- coord_t bed_stride() const {
- double bedwidth = plater().bed_shape_bb().size().x();
- return scaled((1. + LOGICAL_BED_GAP) * bedwidth);
- }
-
- // Set up arrange polygon for a ModelInstance and Wipe tower
- template<class T> ArrangePolygon get_arrange_poly(T *obj) const {
- ArrangePolygon ap = obj->get_arrange_polygon();
- ap.priority = 0;
- ap.bed_idx = ap.translation.x() / bed_stride();
- ap.setter = [obj, this](const ArrangePolygon &p) {
- if (p.is_arranged()) {
- auto t = p.translation;
- t.x() += p.bed_idx * bed_stride();
- obj->apply_arrange_result(t, p.rotation);
- }
- };
- return ap;
- }
-
- // Prepare all objects on the bed regardless of the selection
- void prepare_all() {
- clear_input();
-
- for (ModelObject *obj: plater().model.objects)
- for (ModelInstance *mi : obj->instances) {
- ArrangePolygons & cont = mi->printable ? m_selected : m_unprintable;
- cont.emplace_back(get_arrange_poly(mi));
- }
-
- auto& wti = plater().updated_wipe_tower();
- if (wti) m_selected.emplace_back(get_arrange_poly(&wti));
- }
-
- // Prepare the selected and unselected items separately. If nothing is
- // selected, behaves as if everything would be selected.
- void prepare_selected() {
- clear_input();
-
- Model &model = plater().model;
- coord_t stride = bed_stride();
-
- std::vector<const Selection::InstanceIdxsList *>
- obj_sel(model.objects.size(), nullptr);
-
- for (auto &s : plater().get_selection().get_content())
- if (s.first < int(obj_sel.size()))
- obj_sel[size_t(s.first)] = &s.second;
-
- // Go through the objects and check if inside the selection
- for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) {
- const Selection::InstanceIdxsList * instlist = obj_sel[oidx];
- ModelObject *mo = model.objects[oidx];
-
- std::vector<bool> inst_sel(mo->instances.size(), false);
-
- if (instlist)
- for (auto inst_id : *instlist)
- inst_sel[size_t(inst_id)] = true;
-
- for (size_t i = 0; i < inst_sel.size(); ++i) {
- ArrangePolygon &&ap = get_arrange_poly(mo->instances[i]);
-
- ArrangePolygons &cont = mo->instances[i]->printable ?
- (inst_sel[i] ? m_selected :
- m_unselected) :
- m_unprintable;
-
- cont.emplace_back(std::move(ap));
- }
- }
-
- auto& wti = plater().updated_wipe_tower();
- if (wti) {
- ArrangePolygon &&ap = get_arrange_poly(&wti);
-
- plater().get_selection().is_wipe_tower() ?
- m_selected.emplace_back(std::move(ap)) :
- m_unselected.emplace_back(std::move(ap));
- }
-
- // If the selection was empty arrange everything
- if (m_selected.empty()) m_selected.swap(m_unselected);
-
- // The strides have to be removed from the fixed items. For the
- // arrangeable (selected) items bed_idx is ignored and the
- // translation is irrelevant.
- for (auto &p : m_unselected) p.translation(X) -= p.bed_idx * stride;
- }
-
- protected:
-
- void prepare() override
+ Jobs(priv *_m) : m(_m)
{
- wxGetKeyState(WXK_SHIFT) ? prepare_selected() : prepare_all();
+ m_arrange_id = add_job(std::make_unique<ArrangeJob>(m->statusbar(), m->q));
+ m_fill_bed_id = add_job(std::make_unique<FillBedJob>(m->statusbar(), m->q));
+ m_rotoptimize_id = add_job(std::make_unique<RotoptimizeJob>(m->statusbar(), m->q));
+ m_sla_import_id = add_job(std::make_unique<SLAImportJob>(m->statusbar(), m->q));
}
-
- public:
- using PlaterJob::PlaterJob;
-
- int status_range() const override
+
+ void arrange()
{
- return int(m_selected.size() + m_unprintable.size());
+ m->take_snapshot(_(L("Arrange")));
+ start(m_arrange_id);
}
- void process() override;
-
- void finalize() override {
- // Ignore the arrange result if aborted.
- if (was_canceled()) return;
-
- // Unprintable items go to the last virtual bed
- int beds = 0;
-
- // Apply the arrange result to all selected objects
- for (ArrangePolygon &ap : m_selected) {
- beds = std::max(ap.bed_idx, beds);
- ap.apply();
- }
-
- // Get the virtual beds from the unselected items
- for (ArrangePolygon &ap : m_unselected)
- beds = std::max(ap.bed_idx, beds);
-
- // Move the unprintable items to the last virtual bed.
- for (ArrangePolygon &ap : m_unprintable) {
- ap.bed_idx += beds + 1;
- ap.apply();
- }
-
- plater().update();
+ void fill_bed()
+ {
+ m->take_snapshot(_(L("Fill bed")));
+ start(m_fill_bed_id);
}
- };
-
- class RotoptimizeJob : public PlaterJob
- {
- public:
- using PlaterJob::PlaterJob;
- void process() override;
- };
-
- class HollowJob : public PlaterJob
- {
- public:
- using PlaterJob::PlaterJob;
- void prepare() override;
- void process() override;
- void finalize() override;
- private:
- GLGizmoHollow * get_gizmo();
- const GLGizmoHollow * get_gizmo() const;
- std::unique_ptr<TriangleMesh> m_output_mesh;
- std::unique_ptr<MeshRaycaster> m_output_raycaster;
- const TriangleMesh* m_object_mesh = nullptr;
- sla::HollowingConfig m_cfg;
- };
-
- // Jobs defined inside the group class will be managed so that only one can
- // run at a time. Also, the background process will be stopped if a job is
- // started.
- class ExclusiveJobGroup {
-
- static const int ABORT_WAIT_MAX_MS = 10000;
-
- priv * m_plater;
-
- ArrangeJob arrange_job{m_plater};
- RotoptimizeJob rotoptimize_job{m_plater};
- HollowJob hollow_job{m_plater};
-
- // To create a new job, just define a new subclass of Job, implement
- // the process and the optional prepare() and finalize() methods
- // Register the instance of the class in the m_jobs container
- // if it cannot run concurrently with other jobs in this group
-
- std::vector<std::reference_wrapper<Job>> m_jobs{arrange_job,
- rotoptimize_job,
- hollow_job};
-
- public:
- ExclusiveJobGroup(priv *_plater) : m_plater(_plater) {}
-
- void start(Jobs jid) {
- m_plater->background_process.stop();
- stop_all();
- m_jobs[size_t(jid)].get().start();
- }
-
- void cancel_all() { for (Job& j : m_jobs) j.cancel(); }
-
- void join_all(int wait_ms = 0)
+ void optimize_rotation()
{
- std::vector<bool> aborted(m_jobs.size(), false);
-
- for (size_t jid = 0; jid < m_jobs.size(); ++jid)
- aborted[jid] = m_jobs[jid].get().join(wait_ms);
-
- if (!all_of(aborted))
- BOOST_LOG_TRIVIAL(error) << "Could not abort a job!";
+ m->take_snapshot(_(L("Optimize Rotation")));
+ start(m_rotoptimize_id);
}
-
- void stop_all() { cancel_all(); join_all(ABORT_WAIT_MAX_MS); }
-
- const Job& get(Jobs jobid) const { return m_jobs[size_t(jobid)]; }
-
- bool is_any_running() const
+
+ void import_sla_arch()
{
- return std::any_of(m_jobs.begin(),
- m_jobs.end(),
- [](const Job &j) { return j.is_running(); });
+ m->take_snapshot(_(L("Import SLA archive")));
+ start(m_sla_import_id);
}
-
- } m_ui_jobs{this};
+
+ } m_ui_jobs;
bool delayed_scene_refresh;
std::string delayed_error_message;
@@ -1816,6 +1677,10 @@ struct Plater::priv
std::string label_btn_export;
std::string label_btn_send;
+#if ENABLE_RENDER_STATISTICS
+ bool show_render_statistic_dialog{ false };
+#endif // ENABLE_RENDER_STATISTICS
+
static const std::regex pattern_bundle;
static const std::regex pattern_3mf;
static const std::regex pattern_zip_amf;
@@ -1825,10 +1690,10 @@ struct Plater::priv
priv(Plater *q, MainFrame *main_frame);
~priv();
- enum class UpdateParams {
- FORCE_FULL_SCREEN_REFRESH = 1,
- FORCE_BACKGROUND_PROCESSING_UPDATE = 2,
- POSTPONE_VALIDATION_ERROR_MESSAGE = 4,
+ enum class UpdateParams {
+ FORCE_FULL_SCREEN_REFRESH = 1,
+ FORCE_BACKGROUND_PROCESSING_UPDATE = 2,
+ POSTPONE_VALIDATION_ERROR_MESSAGE = 4,
};
void update(unsigned int flags = 0);
void select_view(const std::string& direction);
@@ -1842,21 +1707,34 @@ struct Plater::priv
bool are_view3D_labels_shown() const { return (current_panel == view3D) && view3D->get_canvas3d()->are_labels_shown(); }
void show_view3D_labels(bool show) { if (current_panel == view3D) view3D->get_canvas3d()->show_labels(show); }
+ bool is_sidebar_collapsed() const { return sidebar->is_collapsed(); }
+ void collapse_sidebar(bool collapse);
+
+ bool is_view3D_layers_editing_enabled() const { return (current_panel == view3D) && view3D->get_canvas3d()->is_layers_editing_enabled(); }
+
void set_current_canvas_as_dirty();
GLCanvas3D* get_current_canvas3D();
+ void unbind_canvas_event_handlers();
+ void reset_canvas_volumes();
bool init_view_toolbar();
+ bool init_collapse_toolbar();
+
+ void update_preview_bottom_toolbar();
+ void update_preview_moves_slider();
+ void enable_preview_moves_slider(bool enable);
+
+ void reset_gcode_toolpaths();
void reset_all_gizmos();
- void update_ui_from_settings();
+ void update_ui_from_settings(bool apply_free_camera_correction = true);
+ void update_main_toolbar_tooltips();
std::shared_ptr<ProgressStatusBar> statusbar();
std::string get_config(const std::string &key) const;
BoundingBoxf bed_shape_bb() const;
BoundingBox scaled_bed_shape_bb() const;
- arrangement::BedShapeHint get_bed_shape_hint() const;
- void find_new_position(const ModelInstancePtrs &instances, coord_t min_d);
- std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config);
+ std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config, bool used_inches = false);
std::vector<size_t> load_model_objects(const ModelObjectPtrs &model_objects);
wxString get_export_file(GUI::FileType file_type);
@@ -1873,9 +1751,6 @@ struct Plater::priv
void delete_object_from_model(size_t obj_idx);
void reset();
void mirror(Axis axis);
- void arrange();
- void hollow();
- void sla_optimize_rotation();
void split_object();
void split_volume();
void scale_selection_to_fit_print_volume();
@@ -1939,8 +1814,19 @@ struct Plater::priv
void on_select_preset(wxCommandEvent&);
void on_slicing_update(SlicingStatusEvent&);
void on_slicing_completed(wxCommandEvent&);
- void on_process_completed(wxCommandEvent&);
+ void on_process_completed(SlicingProcessCompletedEvent&);
+ void on_export_began(wxCommandEvent&);
void on_layer_editing_toggled(bool enable);
+ void on_slicing_began();
+
+ void clear_warnings();
+ void add_warning(const Slic3r::PrintStateBase::Warning &warning, size_t oid);
+ // Update notification manager with the current state of warnings produced by the background process (slicing).
+ void actualize_slicing_warnings(const PrintBase &print);
+ // Displays dialog window with list of warnings.
+ // Returns true if user clicks OK.
+ // Returns true if current_warnings vector is empty without showning the dialog
+ bool warnings_dialog();
void on_action_add(SimpleEvent&);
void on_action_split_objects(SimpleEvent&);
@@ -1961,7 +1847,7 @@ struct Plater::priv
// triangulate the bed and store the triangles into m_bed.m_triangles,
// fills the m_bed.m_grid_lines and sets m_bed.m_origin.
// Sets m_bed.m_polygon to limit the object placement.
- void set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model);
+ void set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false);
bool can_delete() const;
bool can_delete_all() const;
@@ -1976,13 +1862,13 @@ struct Plater::priv
bool can_mirror() const;
bool can_reload_from_disk() const;
-#if ENABLE_THUMBNAIL_GENERATOR
void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background);
void generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background);
-#endif // ENABLE_THUMBNAIL_GENERATOR
void msw_rescale_object_menu();
+ void bring_instance_forward() const;
+
// returns the path to project file with the given extension (none if extension == wxEmptyString)
// extension should contain the leading dot, i.e.: ".3mf"
wxString get_project_filename(const wxString& extension = wxEmptyString) const;
@@ -1991,9 +1877,11 @@ struct Plater::priv
// Caching last value of show_action_buttons parameter for show_action_buttons(), so that a callback which does not know this state will not override it.
mutable bool ready_to_slice = { false };
// Flag indicating that the G-code export targets a removable device, therefore the show_action_buttons() needs to be called at any case when the background processing finishes.
- bool writing_to_removable_device = { false };
+ ExportingStatus exporting_status { NOT_EXPORTING };
+ std::string last_output_path;
+ std::string last_output_dir_path;
bool inside_snapshot_capture() { return m_prevent_snapshots != 0; }
-
+ bool process_completed_with_error { false };
private:
bool init_object_menu();
bool init_common_menu(wxMenu* menu, const bool is_part = false);
@@ -2021,6 +1909,11 @@ private:
* */
std::string m_last_fff_printer_profile_name;
std::string m_last_sla_printer_profile_name;
+
+ // vector of all warnings generated by last slicing
+ std::vector<std::pair<Slic3r::PrintStateBase::Warning, size_t>> current_warnings;
+ bool show_warning_dialog { false };
+
};
const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase);
@@ -2034,8 +1927,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
, main_frame(main_frame)
, config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({
"bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance",
- "brim_width", "variable_layer_height", "serial_port", "serial_speed", "host_type", "print_host",
- "printhost_apikey", "printhost_cafile", "nozzle_diameter", "single_extruder_multi_material",
+ "brim_width", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material",
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle",
"extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology",
// These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor.
@@ -2044,16 +1936,17 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
"support_material", "support_material_extruder", "support_material_interface_extruder", "support_material_contact_distance", "raft_layers"
}))
, sidebar(new Sidebar(q))
+ , m_ui_jobs(this)
, delayed_scene_refresh(false)
, view_toolbar(GLToolbar::Radio, "View")
+ , collapse_toolbar(GLToolbar::Normal, "Collapse")
, m_project_filename(wxEmptyString)
{
this->q->SetFont(Slic3r::GUI::wxGetApp().normal_font());
background_process.set_fff_print(&fff_print);
background_process.set_sla_print(&sla_print);
- background_process.set_gcode_preview_data(&gcode_preview_data);
-#if ENABLE_THUMBNAIL_GENERATOR
+ background_process.set_gcode_result(&gcode_result);
background_process.set_thumbnail_cb([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)
{
std::packaged_task<void(ThumbnailsList&, const Vec2ds&, bool, bool, bool, bool)> task([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) {
@@ -2063,9 +1956,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
wxTheApp->CallAfter([&]() { task(thumbnails, sizes, printable_only, parts_only, show_bed, transparent_background); });
result.wait();
});
-#endif // ENABLE_THUMBNAIL_GENERATOR
background_process.set_slicing_completed_event(EVT_SLICING_COMPLETED);
background_process.set_finished_event(EVT_PROCESS_COMPLETED);
+ background_process.set_export_began_event(EVT_EXPORT_BEGAN);
// Default printer technology for default config.
background_process.select_technology(this->printer_technology);
// Register progress callback from the Print class to the Plater.
@@ -2077,8 +1970,13 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
sla_print.set_status_callback(statuscb);
this->q->Bind(EVT_SLICING_UPDATE, &priv::on_slicing_update, this);
- view3D = new View3D(q, bed, camera, view_toolbar, &model, config, &background_process);
- preview = new Preview(q, bed, camera, view_toolbar, &model, config, &background_process, &gcode_preview_data, [this](){ schedule_background_process(); });
+ view3D = new View3D(q, &model, config, &background_process);
+ preview = new Preview(q, &model, config, &background_process, &gcode_result, [this]() { schedule_background_process(); });
+
+#ifdef __APPLE__
+ // set default view_toolbar icons size equal to GLGizmosManager::Default_Icons_Size
+ view_toolbar.set_icons_size(GLGizmosManager::Default_Icons_Size);
+#endif // __APPLE__
panels.push_back(view3D);
panels.push_back(preview);
@@ -2104,86 +2002,98 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
// Events:
- // Preset change event
- sidebar->Bind(wxEVT_COMBOBOX, &priv::on_select_preset, this);
-
- sidebar->Bind(EVT_OBJ_LIST_OBJECT_SELECT, [this](wxEvent&) { priv::selection_changed(); });
- sidebar->Bind(EVT_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { this->schedule_background_process(); });
+ if (wxGetApp().is_editor()) {
+ // Preset change event
+ sidebar->Bind(wxEVT_COMBOBOX, &priv::on_select_preset, this);
+ sidebar->Bind(EVT_OBJ_LIST_OBJECT_SELECT, [this](wxEvent&) { priv::selection_changed(); });
+ sidebar->Bind(EVT_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { this->schedule_background_process(); });
+ }
wxGLCanvas* view3D_canvas = view3D->get_wxglcanvas();
- // 3DScene events:
- view3D_canvas->Bind(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { this->schedule_background_process(); });
- view3D_canvas->Bind(EVT_GLCANVAS_OBJECT_SELECT, &priv::on_object_select, this);
- view3D_canvas->Bind(EVT_GLCANVAS_RIGHT_CLICK, &priv::on_right_click, this);
- view3D_canvas->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); });
- view3D_canvas->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); });
- view3D_canvas->Bind(EVT_GLCANVAS_SELECT_ALL, [this](SimpleEvent&) { this->q->select_all(); });
- view3D_canvas->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); });
- view3D_canvas->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [this](Event<int> &evt)
- { if (evt.data == 1) this->q->increase_instances(); else if (this->can_decrease_instances()) this->q->decrease_instances(); });
- view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); });
- view3D_canvas->Bind(EVT_GLCANVAS_FORCE_UPDATE, [this](SimpleEvent&) { update(); });
- view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this);
- view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_ROTATED, &priv::on_wipetower_rotated, this);
- view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_ROTATED, [this](SimpleEvent&) { update(); });
- view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_SCALED, [this](SimpleEvent&) { update(); });
- view3D_canvas->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, [this](Event<bool> &evt) { this->sidebar->enable_buttons(evt.data); });
- view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_GEOMETRY, &priv::on_update_geometry, this);
- view3D_canvas->Bind(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, &priv::on_3dcanvas_mouse_dragging_finished, this);
- view3D_canvas->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); });
- view3D_canvas->Bind(EVT_GLCANVAS_RESETGIZMOS, [this](SimpleEvent&) { reset_all_gizmos(); });
- view3D_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); });
- view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); });
- view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); });
- view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event<float>& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); });
- view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](HeightProfileSmoothEvent& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); });
- view3D_canvas->Bind(EVT_GLCANVAS_RELOAD_FROM_DISK, [this](SimpleEvent&) { this->reload_all_from_disk(); });
-
- // 3DScene/Toolbar:
- view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this);
- view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE, [q](SimpleEvent&) { q->remove_selected(); });
- view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE_ALL, [q](SimpleEvent&) { q->reset_with_confirm(); });
- view3D_canvas->Bind(EVT_GLTOOLBAR_ARRANGE, [this](SimpleEvent&) { arrange(); });
- view3D_canvas->Bind(EVT_GLTOOLBAR_COPY, [q](SimpleEvent&) { q->copy_selection_to_clipboard(); });
- view3D_canvas->Bind(EVT_GLTOOLBAR_PASTE, [q](SimpleEvent&) { q->paste_from_clipboard(); });
- view3D_canvas->Bind(EVT_GLTOOLBAR_MORE, [q](SimpleEvent&) { q->increase_instances(); });
- view3D_canvas->Bind(EVT_GLTOOLBAR_FEWER, [q](SimpleEvent&) { q->decrease_instances(); });
- view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_OBJECTS, &priv::on_action_split_objects, this);
- view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_VOLUMES, &priv::on_action_split_volumes, this);
- view3D_canvas->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this);
- view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&)
- {
- set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values,
- config->option<ConfigOptionString>("bed_custom_texture")->value,
- config->option<ConfigOptionString>("bed_custom_model")->value);
- });
+
+ if (wxGetApp().is_editor()) {
+ // 3DScene events:
+ view3D_canvas->Bind(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { this->schedule_background_process(); });
+ view3D_canvas->Bind(EVT_GLCANVAS_OBJECT_SELECT, &priv::on_object_select, this);
+ view3D_canvas->Bind(EVT_GLCANVAS_RIGHT_CLICK, &priv::on_right_click, this);
+ view3D_canvas->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); });
+ view3D_canvas->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { this->q->arrange(); });
+ view3D_canvas->Bind(EVT_GLCANVAS_SELECT_ALL, [this](SimpleEvent&) { this->q->select_all(); });
+ view3D_canvas->Bind(EVT_GLCANVAS_QUESTION_MARK, [](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); });
+ view3D_canvas->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [this](Event<int>& evt)
+ { if (evt.data == 1) this->q->increase_instances(); else if (this->can_decrease_instances()) this->q->decrease_instances(); });
+ view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); });
+ view3D_canvas->Bind(EVT_GLCANVAS_FORCE_UPDATE, [this](SimpleEvent&) { update(); });
+ view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this);
+ view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_ROTATED, &priv::on_wipetower_rotated, this);
+ view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_ROTATED, [this](SimpleEvent&) { update(); });
+ view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_SCALED, [this](SimpleEvent&) { update(); });
+ view3D_canvas->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, [this](Event<bool>& evt) { this->sidebar->enable_buttons(evt.data); });
+ view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_GEOMETRY, &priv::on_update_geometry, this);
+ view3D_canvas->Bind(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, &priv::on_3dcanvas_mouse_dragging_finished, this);
+ view3D_canvas->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); });
+ view3D_canvas->Bind(EVT_GLCANVAS_RESETGIZMOS, [this](SimpleEvent&) { reset_all_gizmos(); });
+ view3D_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); });
+ view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); });
+ view3D_canvas->Bind(EVT_GLCANVAS_COLLAPSE_SIDEBAR, [this](SimpleEvent&) { this->q->collapse_sidebar(!this->q->is_sidebar_collapsed()); });
+ view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); });
+ view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event<float>& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); });
+ view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](HeightProfileSmoothEvent& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); });
+ view3D_canvas->Bind(EVT_GLCANVAS_RELOAD_FROM_DISK, [this](SimpleEvent&) { this->reload_all_from_disk(); });
+
+ // 3DScene/Toolbar:
+ view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this);
+ view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE, [q](SimpleEvent&) { q->remove_selected(); });
+ view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE_ALL, [q](SimpleEvent&) { q->reset_with_confirm(); });
+ view3D_canvas->Bind(EVT_GLTOOLBAR_ARRANGE, [this](SimpleEvent&) { this->q->arrange(); });
+ view3D_canvas->Bind(EVT_GLTOOLBAR_COPY, [q](SimpleEvent&) { q->copy_selection_to_clipboard(); });
+ view3D_canvas->Bind(EVT_GLTOOLBAR_PASTE, [q](SimpleEvent&) { q->paste_from_clipboard(); });
+ view3D_canvas->Bind(EVT_GLTOOLBAR_MORE, [q](SimpleEvent&) { q->increase_instances(); });
+ view3D_canvas->Bind(EVT_GLTOOLBAR_FEWER, [q](SimpleEvent&) { q->decrease_instances(); });
+ view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_OBJECTS, &priv::on_action_split_objects, this);
+ view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_VOLUMES, &priv::on_action_split_volumes, this);
+ view3D_canvas->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this);
+ }
+ view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [q](SimpleEvent&) { q->set_bed_shape(); });
// Preview events:
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); });
- preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&)
- {
- set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values,
- config->option<ConfigOptionString>("bed_custom_texture")->value,
- config->option<ConfigOptionString>("bed_custom_model")->value);
+ preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [q](SimpleEvent&) { q->set_bed_shape(); });
+ if (wxGetApp().is_editor()) {
+ preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); });
+ preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_COLLAPSE_SIDEBAR, [this](SimpleEvent&) { this->q->collapse_sidebar(!this->q->is_sidebar_collapsed()); });
+ }
+ preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_JUMP_TO, [this](wxKeyEvent& evt) { preview->jump_layers_slider(evt); });
+#if ENABLE_ARROW_KEYS_WITH_SLIDERS
+ preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_SLIDERS, [this](wxKeyEvent& evt) {
+ preview->move_layers_slider(evt);
+ preview->move_moves_slider(evt);
});
- preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); });
- preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, [this](wxKeyEvent& evt) { preview->move_double_slider(evt); });
- preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_EDIT_COLOR_CHANGE, [this](wxKeyEvent& evt) { preview->edit_double_slider(evt); });
-
- q->Bind(EVT_SLICING_COMPLETED, &priv::on_slicing_completed, this);
- q->Bind(EVT_PROCESS_COMPLETED, &priv::on_process_completed, this);
- q->Bind(EVT_GLVIEWTOOLBAR_3D, [q](SimpleEvent&) { q->select_view_3D("3D"); });
- q->Bind(EVT_GLVIEWTOOLBAR_PREVIEW, [q](SimpleEvent&) { q->select_view_3D("Preview"); });
+#else
+ preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_LAYERS_SLIDER, [this](wxKeyEvent& evt) { preview->move_layers_slider(evt); });
+#endif // ENABLE_ARROW_KEYS_WITH_SLIDERS
+ preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_EDIT_COLOR_CHANGE, [this](wxKeyEvent& evt) { preview->edit_layers_slider(evt); });
+ if (wxGetApp().is_gcode_viewer())
+ preview->Bind(EVT_GLCANVAS_RELOAD_FROM_DISK, [this](SimpleEvent&) { this->q->reload_gcode_from_disk(); });
+
+ if (wxGetApp().is_editor()) {
+ q->Bind(EVT_SLICING_COMPLETED, &priv::on_slicing_completed, this);
+ q->Bind(EVT_PROCESS_COMPLETED, &priv::on_process_completed, this);
+ q->Bind(EVT_EXPORT_BEGAN, &priv::on_export_began, this);
+ q->Bind(EVT_GLVIEWTOOLBAR_3D, [q](SimpleEvent&) { q->select_view_3D("3D"); });
+ q->Bind(EVT_GLVIEWTOOLBAR_PREVIEW, [q](SimpleEvent&) { q->select_view_3D("Preview"); });
+ }
// Drop target:
q->SetDropTarget(new PlaterDropTarget(q)); // if my understanding is right, wxWindow takes the owenership
-
- update_ui_from_settings();
q->Layout();
- set_current_panel(view3D);
+ set_current_panel(wxGetApp().is_editor() ? static_cast<wxPanel*>(view3D) : static_cast<wxPanel*>(preview));
+ if (wxGetApp().is_gcode_viewer())
+ preview->hide_layers_slider();
// updates camera type from .ini file
+ camera.enable_update_config_on_type_change(true);
camera.set_type(get_config("use_perspective_camera"));
// Load the 3DConnexion device database.
@@ -2197,29 +2107,77 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
// is one of the 3D Mouse vendors (3DConnexion or Logitech).
this->q->Bind(EVT_HID_DEVICE_ATTACHED, [this](HIDDeviceAttachedEvent &evt) {
mouse3d_controller.device_attached(evt.data);
- });
+ });
+#if ENABLE_CTRL_M_ON_WINDOWS
+ this->q->Bind(EVT_HID_DEVICE_DETACHED, [this](HIDDeviceAttachedEvent& evt) {
+ mouse3d_controller.device_detached(evt.data);
+ });
+#endif // ENABLE_CTRL_M_ON_WINDOWS
#endif /* _WIN32 */
- this->q->Bind(EVT_REMOVABLE_DRIVE_EJECTED, [this](RemovableDriveEjectEvent &evt) {
- if (evt.data.second) {
- this->show_action_buttons(this->ready_to_slice);
- Slic3r::GUI::show_info(this->q, (boost::format(_utf8(L("Unmounting successful. The device %s(%s) can now be safely removed from the computer.")))
- % evt.data.first.name % evt.data.first.path).str());
- } else
- Slic3r::GUI::show_info(this->q, (boost::format(_utf8(L("Ejecting of device %s(%s) has failed.")))
- % evt.data.first.name % evt.data.first.path).str());
- });
- this->q->Bind(EVT_REMOVABLE_DRIVES_CHANGED, [this](RemovableDrivesChangedEvent &) { this->show_action_buttons(this->ready_to_slice); });
- // Start the background thread and register this window as a target for update events.
- wxGetApp().removable_drive_manager()->init(this->q);
+ notification_manager = new NotificationManager(this->q);
+ if (wxGetApp().is_editor()) {
+ this->q->Bind(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED, [this](EjectDriveNotificationClickedEvent&) { this->q->eject_drive(); });
+ this->q->Bind(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, [this](ExportGcodeNotificationClickedEvent&) { this->q->export_gcode(true); });
+ this->q->Bind(EVT_PRESET_UPDATE_AVAILABLE_CLICKED, [this](PresetUpdateAvailableClickedEvent&) { wxGetApp().get_preset_updater()->on_update_notification_confirm(); });
+ this->q->Bind(EVT_REMOVABLE_DRIVE_EJECTED, [this, q](RemovableDriveEjectEvent &evt) {
+ if (evt.data.second) {
+ this->show_action_buttons(this->ready_to_slice);
+ notification_manager->close_notification_of_type(NotificationType::ExportFinished);
+ notification_manager->push_notification(NotificationType::CustomNotification,
+ NotificationManager::NotificationLevel::RegularNotification,
+ format(_L("Successfully unmounted. The device %s(%s) can now be safely removed from the computer."), evt.data.first.name, evt.data.first.path)
+ );
+ } else {
+ notification_manager->push_notification(NotificationType::CustomNotification,
+ NotificationManager::NotificationLevel::ErrorNotification,
+ format(_L("Ejecting of device %s(%s) has failed."), evt.data.first.name, evt.data.first.path)
+ );
+ }
+ });
+ this->q->Bind(EVT_REMOVABLE_DRIVES_CHANGED, [this, q](RemovableDrivesChangedEvent &) {
+ this->show_action_buttons(this->ready_to_slice);
+ // Close notification ExportingFinished but only if last export was to removable
+ notification_manager->device_ejected();
+ });
+ // Start the background thread and register this window as a target for update events.
+ wxGetApp().removable_drive_manager()->init(this->q);
#ifdef _WIN32
- // Trigger enumeration of removable media on Win32 notification.
- this->q->Bind(EVT_VOLUME_ATTACHED, [this](VolumeAttachedEvent &evt) { wxGetApp().removable_drive_manager()->volumes_changed(); });
- this->q->Bind(EVT_VOLUME_DETACHED, [this](VolumeDetachedEvent &evt) { wxGetApp().removable_drive_manager()->volumes_changed(); });
+ // Trigger enumeration of removable media on Win32 notification.
+ this->q->Bind(EVT_VOLUME_ATTACHED, [this](VolumeAttachedEvent &evt) { wxGetApp().removable_drive_manager()->volumes_changed(); });
+ this->q->Bind(EVT_VOLUME_DETACHED, [this](VolumeDetachedEvent &evt) { wxGetApp().removable_drive_manager()->volumes_changed(); });
#endif /* _WIN32 */
+ }
// Initialize the Undo / Redo stack with a first snapshot.
- this->take_snapshot(_(L("New Project")));
+ this->take_snapshot(_L("New Project"));
+
+#if ENABLE_DRAG_AND_DROP_FIX
+ this->q->Bind(EVT_LOAD_MODEL_OTHER_INSTANCE, [this](LoadFromOtherInstanceEvent& evt) {
+ BOOST_LOG_TRIVIAL(trace) << "Received load from other instance event.";
+ wxArrayString input_files;
+ for (size_t i = 0; i < evt.data.size(); ++i) {
+ input_files.push_back(from_u8(evt.data[i].string()));
+ }
+ wxGetApp().mainframe->Raise();
+ this->q->load_files(input_files);
+ });
+#else
+ this->q->Bind(EVT_LOAD_MODEL_OTHER_INSTANCE, [this](LoadFromOtherInstanceEvent &evt) {
+ BOOST_LOG_TRIVIAL(trace) << "Received load from other instance event.";
+ this->load_files(evt.data, true, true);
+ });
+#endif // ENABLE_DRAG_AND_DROP_FIX
+ this->q->Bind(EVT_INSTANCE_GO_TO_FRONT, [this](InstanceGoToFrontEvent &) {
+ bring_instance_forward();
+ });
+ wxGetApp().other_instance_message_handler()->init(this->q);
+
+ // collapse sidebar according to saved value
+ if (wxGetApp().is_editor()) {
+ bool is_collapsed = wxGetApp().app_config->get("collapsed_sidebar") == "1";
+ sidebar->collapse(is_collapsed);
+ }
}
Plater::priv::~priv()
@@ -2267,6 +2225,8 @@ void Plater::priv::select_view_3D(const std::string& name)
set_current_panel(view3D);
else if (name == "Preview")
set_current_panel(preview);
+
+ wxGetApp().update_ui_from_settings(false);
}
void Plater::priv::select_next_view_3D()
@@ -2277,6 +2237,20 @@ void Plater::priv::select_next_view_3D()
set_current_panel(view3D);
}
+void Plater::priv::collapse_sidebar(bool collapse)
+{
+ sidebar->collapse(collapse);
+
+ // Now update the tooltip in the toolbar.
+ std::string new_tooltip = collapse
+ ? _utf8(L("Expand sidebar"))
+ : _utf8(L("Collapse sidebar"));
+ new_tooltip += " [Shift+Tab]";
+ int id = collapse_toolbar.get_item_id("collapse_sidebar");
+ collapse_toolbar.set_tooltip(id, new_tooltip);
+}
+
+
void Plater::priv::reset_all_gizmos()
{
view3D->get_canvas3d()->reset_all_gizmos();
@@ -2284,14 +2258,23 @@ void Plater::priv::reset_all_gizmos()
// Called after the Preferences dialog is closed and the program settings are saved.
// Update the UI based on the current preferences.
-void Plater::priv::update_ui_from_settings()
+void Plater::priv::update_ui_from_settings(bool apply_free_camera_correction)
{
camera.set_type(wxGetApp().app_config->get("use_perspective_camera"));
- if (wxGetApp().app_config->get("use_free_camera") != "1")
+ if (apply_free_camera_correction && wxGetApp().app_config->get("use_free_camera") != "1")
camera.recover_from_free_camera();
view3D->get_canvas3d()->update_ui_from_settings();
preview->get_canvas3d()->update_ui_from_settings();
+
+ sidebar->update_ui_from_settings();
+}
+
+// Called after the print technology was changed.
+// Update the tooltips for "Switch to Settings" button in maintoolbar
+void Plater::priv::update_main_toolbar_tooltips()
+{
+ view3D->get_canvas3d()->update_tooltip_for_settings_item_in_main_toolbar();
}
std::shared_ptr<ProgressStatusBar> Plater::priv::statusbar()
@@ -2317,7 +2300,7 @@ BoundingBox Plater::priv::scaled_bed_shape_bb() const
return bed_shape.bounding_box();
}
-std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config)
+std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config, bool imperial_units/* = false*/)
{
if (input_files.empty()) { return std::vector<size_t>(); }
@@ -2333,18 +2316,18 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
}
}
- const auto loading = _(L("Loading")) + dots;
- wxProgressDialog dlg(loading, loading);
+ const auto loading = _L("Loading") + dots;
+ wxProgressDialog dlg(loading, "", 100, q, wxPD_AUTO_HIDE);
dlg.Pulse();
auto *new_model = (!load_model || one_by_one) ? nullptr : new Slic3r::Model();
std::vector<size_t> obj_idxs;
- for (size_t i = 0; i < input_files.size(); i++) {
+ for (size_t i = 0; i < input_files.size(); ++i) {
const auto &path = input_files[i];
const auto filename = path.filename();
- const auto dlg_info = from_u8((boost::format(_utf8(L("Processing input file %s"))) % from_path(filename)).str()) + "\n";
- dlg.Update(100 * i / input_files.size(), dlg_info);
+ const auto dlg_info = _L("Loading file") + ": " + from_path(filename);
+ dlg.Update(static_cast<int>(100.0f * static_cast<float>(i) / static_cast<float>(input_files.size())), dlg_info);
const bool type_3mf = std::regex_match(path.string(), pattern_3mf);
const bool type_zip_amf = !type_3mf && std::regex_match(path.string(), pattern_zip_amf);
@@ -2371,9 +2354,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
if (object->volumes.size() > 1)
{
Slic3r::GUI::show_info(nullptr,
- _(L("You cannot load SLA project with a multi-part object on the bed")) + "\n\n" +
- _(L("Please check your object list before preset changing.")),
- _(L("Attention!")));
+ _L("You cannot load SLA project with a multi-part object on the bed") + "\n\n" +
+ _L("Please check your object list before preset changing."),
+ _L("Attention!"));
return obj_idxs;
}
}
@@ -2393,7 +2376,10 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
if (!config.empty()) {
Preset::normalize(config);
wxGetApp().preset_bundle->load_config_model(filename.string(), std::move(config));
- wxGetApp().load_current_presets();
+ if (printer_technology == ptFFF)
+ CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, &wxGetApp().preset_bundle->project_config);
+ // For exporting from the amf/3mf we shouldn't check printer_presets for the containing information about "Print Host upload"
+ wxGetApp().load_current_presets(false);
is_project_file = true;
}
wxGetApp().app_config->update_config_dir(path.parent_path().string());
@@ -2414,21 +2400,40 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
{
// The model should now be initialized
- if (! is_project_file) {
+ auto convert_from_imperial_units = [](Model& model, bool only_small_volumes) {
+ model.convert_from_imperial_units(only_small_volumes);
+// wxGetApp().app_config->set("use_inches", "1");
+ wxGetApp().sidebar().update_ui_from_settings();
+ };
+
+ if (!is_project_file) {
+ if (imperial_units)
+ // Convert even if the object is big.
+ convert_from_imperial_units(model, false);
+ else if (model.looks_like_imperial_units()) {
+ wxMessageDialog msg_dlg(q, format_wxstr(_L(
+ "Some object(s) in file %s looks like saved in inches.\n"
+ "Should I consider them as a saved in inches and convert them?"), from_path(filename)) + "\n",
+ _L("The object appears to be saved in inches"), wxICON_WARNING | wxYES | wxNO);
+ if (msg_dlg.ShowModal() == wxID_YES)
+ //FIXME up-scale only the small parts?
+ convert_from_imperial_units(model, true);
+ }
+
if (model.looks_like_multipart_object()) {
- wxMessageDialog msg_dlg(q, _(L(
+ wxMessageDialog msg_dlg(q, _L(
"This file contains several objects positioned at multiple heights.\n"
"Instead of considering them as multiple objects, should I consider\n"
- "this file as a single object having multiple parts?")) + "\n",
- _(L("Multi-part object detected")), wxICON_WARNING | wxYES | wxNO);
+ "this file as a single object having multiple parts?") + "\n",
+ _L("Multi-part object detected"), wxICON_WARNING | wxYES | wxNO);
if (msg_dlg.ShowModal() == wxID_YES) {
model.convert_multipart_object(nozzle_dmrs->values.size());
}
}
}
else if ((wxGetApp().get_mode() == comSimple) && (type_3mf || type_any_amf) && model_has_advanced_features(model)) {
- wxMessageDialog msg_dlg(q, _(L("This file cannot be loaded in a simple mode. Do you want to switch to an advanced mode?"))+"\n",
- _(L("Detected advanced data")), wxICON_WARNING | wxYES | wxNO);
+ wxMessageDialog msg_dlg(q, _L("This file cannot be loaded in a simple mode. Do you want to switch to an advanced mode?")+"\n",
+ _L("Detected advanced data"), wxICON_WARNING | wxYES | wxNO);
if (msg_dlg.ShowModal() == wxID_YES)
{
Slic3r::GUI::wxGetApp().save_mode(comAdvanced);
@@ -2450,8 +2455,8 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
for (auto obj : model.objects)
if ( obj->volumes.size()>1 ) {
Slic3r::GUI::show_error(nullptr,
- from_u8((boost::format(_utf8(L("You can't to add the object(s) from %s because of one or some of them is(are) multi-part")))
- % from_path(filename)).str()));
+ format_wxstr(_L("You can't to add the object(s) from %s because of one or some of them is(are) multi-part"),
+ from_path(filename)));
return obj_idxs;
}
}
@@ -2469,11 +2474,11 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
}
if (new_model != nullptr && new_model->objects.size() > 1) {
- wxMessageDialog msg_dlg(q, _(L(
+ wxMessageDialog msg_dlg(q, _L(
"Multiple objects were loaded for a multi-material printer.\n"
"Instead of considering them as multiple objects, should I consider\n"
- "these files to represent a single object having multiple parts?")) + "\n",
- _(L("Multi-part object detected")), wxICON_WARNING | wxYES | wxNO);
+ "these files to represent a single object having multiple parts?") + "\n",
+ _L("Multi-part object detected"), wxICON_WARNING | wxYES | wxNO);
if (msg_dlg.ShowModal() == wxID_YES) {
new_model->convert_multipart_object(nozzle_dmrs->values.size());
}
@@ -2486,7 +2491,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
{
wxGetApp().app_config->update_skein_dir(input_files[input_files.size() - 1].parent_path().string());
// XXX: Plater.pm had @loaded_files, but didn't seem to fill them with the filenames...
- statusbar()->set_status_text(_(L("Loaded")));
+ statusbar()->set_status_text(_L("Loaded"));
}
// automatic selection of added objects
@@ -2588,8 +2593,8 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
if (scaled_down) {
GUI::show_info(q,
- _(L("Your object appears to be too large, so it was automatically scaled down to fit your print bed.")),
- _(L("Object too large?")));
+ _L("Your object appears to be too large, so it was automatically scaled down to fit your print bed."),
+ _L("Object too large?"));
}
for (const size_t idx : obj_idxs) {
@@ -2651,26 +2656,26 @@ wxString Plater::priv::get_export_file(GUI::FileType file_type)
case FT_STL:
{
output_file.replace_extension("stl");
- dlg_title = _(L("Export STL file:"));
+ dlg_title = _L("Export STL file:");
break;
}
case FT_AMF:
{
// XXX: Problem on OS X with double extension?
output_file.replace_extension("zip.amf");
- dlg_title = _(L("Export AMF file:"));
+ dlg_title = _L("Export AMF file:");
break;
}
case FT_3MF:
{
output_file.replace_extension("3mf");
- dlg_title = _(L("Save file as:"));
+ dlg_title = _L("Save file as:");
break;
}
case FT_OBJ:
{
output_file.replace_extension("obj");
- dlg_title = _(L("Export OBJ file:"));
+ dlg_title = _L("Export OBJ file:");
break;
}
default: break;
@@ -2735,30 +2740,24 @@ void Plater::priv::object_list_changed()
{
const bool export_in_progress = this->background_process.is_export_scheduled(); // || ! send_gcode_file.empty());
// XXX: is this right?
- const bool model_fits = view3D->check_volumes_outside_state() == ModelInstance::PVS_Inside;
+ const bool model_fits = view3D->check_volumes_outside_state() == ModelInstancePVS_Inside;
sidebar->enable_buttons(!model.objects.empty() && !export_in_progress && model_fits);
}
void Plater::priv::select_all()
{
-// this->take_snapshot(_(L("Select All")));
-
view3D->select_all();
this->sidebar->obj_list()->update_selections();
}
void Plater::priv::deselect_all()
{
-// this->take_snapshot(_(L("Deselect All")));
view3D->deselect_all();
}
void Plater::priv::remove(size_t obj_idx)
{
- // Prevent toolpaths preview from rendering while we modify the Print object
- preview->set_enabled(false);
-
if (view3D->is_layers_editing_enabled())
view3D->enable_layers_editing(false);
@@ -2772,7 +2771,7 @@ void Plater::priv::remove(size_t obj_idx)
void Plater::priv::delete_object_from_model(size_t obj_idx)
{
- wxString snapshot_label = _(L("Delete Object"));
+ wxString snapshot_label = _L("Delete Object");
if (! model.objects[obj_idx]->name.empty())
snapshot_label += ": " + wxString::FromUTF8(model.objects[obj_idx]->name.c_str());
Plater::TakeSnapshot snapshot(q, snapshot_label);
@@ -2783,16 +2782,18 @@ void Plater::priv::delete_object_from_model(size_t obj_idx)
void Plater::priv::reset()
{
- Plater::TakeSnapshot snapshot(q, _(L("Reset Project")));
+ Plater::TakeSnapshot snapshot(q, _L("Reset Project"));
- set_project_filename(wxEmptyString);
+ clear_warnings();
- // Prevent toolpaths preview from rendering while we modify the Print object
- preview->set_enabled(false);
+ set_project_filename(wxEmptyString);
if (view3D->is_layers_editing_enabled())
view3D->enable_layers_editing(false);
+ reset_gcode_toolpaths();
+ gcode_result.reset();
+
// Stop and reset the Print content.
this->background_process.reset();
model.clear_objects();
@@ -2812,45 +2813,12 @@ void Plater::priv::mirror(Axis axis)
view3D->mirror_selection(axis);
}
-void Plater::priv::arrange()
-{
- this->take_snapshot(_(L("Arrange")));
- m_ui_jobs.start(Jobs::Arrange);
-}
-
-void Plater::priv::hollow()
-{
- this->take_snapshot(_(L("Hollow")));
- m_ui_jobs.start(Jobs::Hollow);
-}
-
-// This method will find an optimal orientation for the currently selected item
-// Very similar in nature to the arrange method above...
-void Plater::priv::sla_optimize_rotation() {
- this->take_snapshot(_(L("Optimize Rotation")));
- m_ui_jobs.start(Jobs::Rotoptimize);
-}
-
-arrangement::BedShapeHint Plater::priv::get_bed_shape_hint() const {
-
- const auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape");
- assert(bed_shape_opt);
-
- if (!bed_shape_opt) return {};
-
- auto &bedpoints = bed_shape_opt->values;
- Polyline bedpoly; bedpoly.points.reserve(bedpoints.size());
- for (auto &v : bedpoints) bedpoly.append(scaled(v));
-
- return arrangement::BedShapeHint(bedpoly);
-}
-
-void Plater::priv::find_new_position(const ModelInstancePtrs &instances,
+void Plater::find_new_position(const ModelInstancePtrs &instances,
coord_t min_d)
{
arrangement::ArrangePolygons movable, fixed;
-
- for (const ModelObject *mo : model.objects)
+
+ for (const ModelObject *mo : p->model.objects)
for (const ModelInstance *inst : mo->instances) {
auto it = std::find(instances.begin(), instances.end(), inst);
auto arrpoly = inst->get_arrange_polygon();
@@ -2860,168 +2828,19 @@ void Plater::priv::find_new_position(const ModelInstancePtrs &instances,
else
movable.emplace_back(std::move(arrpoly));
}
-
- if (updated_wipe_tower())
- fixed.emplace_back(wipetower.get_arrange_polygon());
-
- arrangement::arrange(movable, fixed, min_d, get_bed_shape_hint());
+
+ if (auto wt = get_wipe_tower_arrangepoly(*this))
+ fixed.emplace_back(*wt);
+
+ arrangement::arrange(movable, fixed, get_bed_shape(*config()),
+ arrangement::ArrangeParams{min_d});
for (size_t i = 0; i < instances.size(); ++i)
if (movable[i].bed_idx == 0)
- instances[i]->apply_arrange_result(movable[i].translation,
+ instances[i]->apply_arrange_result(movable[i].translation.cast<double>(),
movable[i].rotation);
}
-void Plater::priv::ArrangeJob::process() {
- static const auto arrangestr = _(L("Arranging"));
-
- // FIXME: I don't know how to obtain the minimum distance, it depends
- // on printer technology. I guess the following should work but it crashes.
- double dist = 6; // PrintConfig::min_object_distance(config);
- if (plater().printer_technology == ptFFF) {
- dist = PrintConfig::min_object_distance(plater().config);
- }
-
- coord_t min_d = scaled(dist);
- auto count = unsigned(m_selected.size() + m_unprintable.size());
- arrangement::BedShapeHint bedshape = plater().get_bed_shape_hint();
-
- auto stopfn = [this]() { return was_canceled(); };
-
- try {
- arrangement::arrange(m_selected, m_unselected, min_d, bedshape,
- [this, count](unsigned st) {
- st += m_unprintable.size();
- if (st > 0) update_status(int(count - st), arrangestr);
- }, stopfn);
- arrangement::arrange(m_unprintable, {}, min_d, bedshape,
- [this, count](unsigned st) {
- if (st > 0) update_status(int(count - st), arrangestr);
- }, stopfn);
- } catch (std::exception & /*e*/) {
- GUI::show_error(plater().q,
- _(L("Could not arrange model objects! "
- "Some geometries may be invalid.")));
- }
-
- // finalize just here.
- update_status(int(count),
- was_canceled() ? _(L("Arranging canceled."))
- : _(L("Arranging done.")));
-}
-
-void Plater::priv::RotoptimizeJob::process()
-{
- int obj_idx = plater().get_selected_object_idx();
- if (obj_idx < 0) { return; }
-
- ModelObject *o = plater().model.objects[size_t(obj_idx)];
-
- auto r = sla::find_best_rotation(
- *o,
- .005f,
- [this](unsigned s) {
- if (s < 100)
- update_status(int(s),
- _(L("Searching for optimal orientation")));
- },
- [this]() { return was_canceled(); });
-
-
- double mindist = 6.0; // FIXME
-
- if (!was_canceled()) {
- for(ModelInstance * oi : o->instances) {
- oi->set_rotation({r[X], r[Y], r[Z]});
-
- auto trmatrix = oi->get_transformation().get_matrix();
- Polygon trchull = o->convex_hull_2d(trmatrix);
-
- MinAreaBoundigBox rotbb(trchull, MinAreaBoundigBox::pcConvex);
- double r = rotbb.angle_to_X();
-
- // The box should be landscape
- if(rotbb.width() < rotbb.height()) r += PI / 2;
-
- Vec3d rt = oi->get_rotation(); rt(Z) += r;
-
- oi->set_rotation(rt);
- }
-
- plater().find_new_position(o->instances, scaled(mindist));
-
- // Correct the z offset of the object which was corrupted be
- // the rotation
- o->ensure_on_bed();
- }
-
- update_status(100,
- was_canceled() ? _(L("Orientation search canceled."))
- : _(L("Orientation found.")));
-}
-
-void Plater::priv::HollowJob::prepare()
-{
- const GLGizmosManager& gizmo_manager = plater().q->canvas3D()->get_gizmos_manager();
- const GLGizmoHollow* gizmo_hollow = dynamic_cast<const GLGizmoHollow*>(gizmo_manager.get_current());
- assert(gizmo_hollow);
- auto hlw_data = gizmo_hollow->get_hollowing_parameters();
- m_object_mesh = hlw_data.first;
- m_cfg = hlw_data.second;
- m_output_mesh.reset();
-}
-
-void Plater::priv::HollowJob::process()
-{
- sla::JobController ctl;
- ctl.stopcondition = [this]{ return was_canceled(); };
- ctl.statuscb = [this](unsigned st, const std::string &s) {
- if (st < 100) update_status(int(st), s);
- };
-
- std::unique_ptr<TriangleMesh> omesh =
- sla::generate_interior(*m_object_mesh, m_cfg, ctl);
-
- if (omesh && !omesh->empty()) {
- m_output_mesh.reset(new TriangleMesh{*m_object_mesh});
- m_output_mesh->merge(*omesh);
- m_output_mesh->require_shared_vertices();
-
- update_status(90, _(L("Indexing hollowed object")));
-
- m_output_raycaster.reset(new MeshRaycaster(*m_output_mesh));
-
- update_status(100, was_canceled() ? _(L("Hollowing cancelled.")) :
- _(L("Hollowing done.")));
- } else {
- update_status(100, _(L("Hollowing failed.")));
- }
-}
-
-void Plater::priv::HollowJob::finalize()
-{
- if (auto gizmo = get_gizmo()) {
- gizmo->update_mesh_raycaster(std::move(m_output_raycaster));
- gizmo->update_hollowed_mesh(std::move(m_output_mesh));
- }
-}
-
-GLGizmoHollow *Plater::priv::HollowJob::get_gizmo()
-{
- const GLGizmosManager& gizmo_manager = plater().q->canvas3D()->get_gizmos_manager();
- auto ret = dynamic_cast<GLGizmoHollow*>(gizmo_manager.get_current());
- assert(ret);
- return ret;
-}
-
-const GLGizmoHollow *Plater::priv::HollowJob::get_gizmo() const
-{
- const GLGizmosManager& gizmo_manager = plater().q->canvas3D()->get_gizmos_manager();
- auto ret = dynamic_cast<const GLGizmoHollow*>(gizmo_manager.get_current());
- assert(ret);
- return ret;
-}
-
void Plater::priv::split_object()
{
int obj_idx = get_selected_object_idx();
@@ -3035,7 +2854,7 @@ void Plater::priv::split_object()
if (current_model_object->volumes.size() > 1)
{
- Slic3r::GUI::warning_catcher(q, _(L("The selected object can't be split because it contains more than one volume/material.")));
+ Slic3r::GUI::warning_catcher(q, _L("The selected object can't be split because it contains more than one volume/material."));
return;
}
@@ -3043,10 +2862,10 @@ void Plater::priv::split_object()
ModelObjectPtrs new_objects;
current_model_object->split(&new_objects);
if (new_objects.size() == 1)
- Slic3r::GUI::warning_catcher(q, _(L("The selected object couldn't be split because it contains only one part.")));
+ Slic3r::GUI::warning_catcher(q, _L("The selected object couldn't be split because it contains only one part."));
else
{
- Plater::TakeSnapshot snapshot(q, _(L("Split to Objects")));
+ Plater::TakeSnapshot snapshot(q, _L("Split to Objects"));
unsigned int counter = 1;
for (ModelObject* m : new_objects)
@@ -3120,10 +2939,12 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
this->sidebar->show_sliced_info_sizer(false);
// Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared.
// Otherwise they will be just refreshed.
- if (this->preview != nullptr)
+ if (this->preview != nullptr) {
// If the preview is not visible, the following line just invalidates the preview,
// but the G-code paths or SLA preview are calculated first once the preview is made visible.
+ reset_gcode_toolpaths();
this->preview->reload_print();
+ }
// In FDM mode, we need to reload the 3D scene because of the wipe tower preview box.
// In SLA mode, we need to reload the 3D scene every time to show the support structures.
if (this->printer_technology == ptSLA || (this->printer_technology == ptFFF && this->config->opt_bool("wipe_tower")))
@@ -3136,22 +2957,13 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
// The state of the Print changed, and it is non-zero. Let's validate it and give the user feedback on errors.
std::string err = this->background_process.validate();
if (err.empty()) {
+ notification_manager->set_all_slicing_errors_gray(true);
if (invalidated != Print::APPLY_STATUS_UNCHANGED && this->background_processing_enabled())
return_state |= UPDATE_BACKGROUND_PROCESS_RESTART;
} else {
- // The print is not valid.
- // Only show the error message immediately, if the top level parent of this window is active.
- auto p = dynamic_cast<wxWindow*>(this->q);
- while (p->GetParent())
- p = p->GetParent();
- auto *top_level_wnd = dynamic_cast<wxTopLevelWindow*>(p);
- if (! postpone_error_messages && top_level_wnd && top_level_wnd->IsActive()) {
- // The error returned from the Print needs to be translated into the local language.
- GUI::show_error(this->q, err);
- } else {
- // Show the error message once the main window gets activated.
- this->delayed_error_message = err;
- }
+ // The print is not valid.
+ // Show error as notification.
+ notification_manager->push_slicing_error_notification(err);
return_state |= UPDATE_BACKGROUND_PROCESS_INVALID;
}
} else if (! this->delayed_error_message.empty()) {
@@ -3159,6 +2971,13 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
return_state |= UPDATE_BACKGROUND_PROCESS_INVALID;
}
+ //actualizate warnings
+ if (invalidated != Print::APPLY_STATUS_UNCHANGED) {
+ actualize_slicing_warnings(*this->background_process.current_print());
+ show_warning_dialog = false;
+ process_completed_with_error = false;
+ }
+
if (invalidated != Print::APPLY_STATUS_UNCHANGED && was_running && ! this->background_process.running() &&
(return_state & UPDATE_BACKGROUND_PROCESS_RESTART) == 0) {
// The background processing was killed and it will not be restarted.
@@ -3171,22 +2990,23 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
if ((return_state & UPDATE_BACKGROUND_PROCESS_INVALID) != 0)
{
// Validation of the background data failed.
- const wxString invalid_str = _(L("Invalid data"));
+ const wxString invalid_str = _L("Invalid data");
for (auto btn : {ActionButtonType::abReslice, ActionButtonType::abSendGCode, ActionButtonType::abExport})
sidebar->set_btn_label(btn, invalid_str);
+ process_completed_with_error = true;
}
else
{
// Background data is valid.
if ((return_state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 ||
(return_state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0 )
- this->statusbar()->set_status_text(_(L("Ready to slice")));
+ this->statusbar()->set_status_text(_L("Ready to slice"));
sidebar->set_btn_label(ActionButtonType::abExport, _(label_btn_export));
sidebar->set_btn_label(ActionButtonType::abSendGCode, _(label_btn_send));
const wxString slice_string = background_process.running() && wxGetApp().get_mode() == comSimple ?
- _(L("Slicing")) + dots : _(L("Slice now"));
+ _L("Slicing") + dots : _L("Slice now");
sidebar->set_btn_label(ActionButtonType::abReslice, slice_string);
if (background_process.finished())
@@ -3218,9 +3038,11 @@ bool Plater::priv::restart_background_process(unsigned int state)
// The print is valid and it can be started.
if (this->background_process.start()) {
this->statusbar()->set_cancel_callback([this]() {
- this->statusbar()->set_status_text(_(L("Cancelling")));
+ this->statusbar()->set_status_text(_L("Cancelling"));
this->background_process.stop();
});
+ if (!show_warning_dialog)
+ on_slicing_began();
return true;
}
}
@@ -3235,7 +3057,7 @@ void Plater::priv::export_gcode(fs::path output_path, bool output_path_on_remova
return;
if (background_process.is_export_scheduled()) {
- GUI::show_error(q, _(L("Another export job is currently running.")));
+ GUI::show_error(q, _L("Another export job is currently running."));
return;
}
@@ -3247,6 +3069,7 @@ void Plater::priv::export_gcode(fs::path output_path, bool output_path_on_remova
if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0)
return;
+ show_warning_dialog = true;
if (! output_path.empty()) {
background_process.schedule_export(output_path.string(), output_path_on_removable_media);
} else {
@@ -3290,7 +3113,7 @@ void Plater::priv::update_sla_scene()
void Plater::priv::reload_from_disk()
{
- Plater::TakeSnapshot snapshot(q, _(L("Reload from disk")));
+ Plater::TakeSnapshot snapshot(q, _L("Reload from disk"));
const Selection& selection = get_selection();
@@ -3351,7 +3174,7 @@ void Plater::priv::reload_from_disk()
{
// ask user to select the missing file
fs::path search = missing_input_paths.back();
- wxString title = _(L("Please select the file to reload"));
+ wxString title = _L("Please select the file to reload");
#if defined(__APPLE__)
title += " (" + from_u8(search.filename().string()) + ")";
#endif // __APPLE__
@@ -3386,7 +3209,7 @@ void Plater::priv::reload_from_disk()
}
else
{
- wxString message = _(L("It is not allowed to change the file to reload")) + " (" + from_u8(search.filename().string()) + ").\n" + _(L("Do you want to retry")) + " ?";
+ wxString message = _L("It is not allowed to change the file to reload") + " (" + from_u8(search.filename().string()) + ").\n" + _L("Do you want to retry") + " ?";
wxMessageDialog dlg(q, message, wxMessageBoxCaptionStr, wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION);
if (dlg.ShowModal() != wxID_YES)
return;
@@ -3404,7 +3227,7 @@ void Plater::priv::reload_from_disk()
const auto& path = input_paths[i].string();
wxBusyCursor wait;
- wxBusyInfo info(_(L("Reload from:")) + " " + from_u8(path), q->get_current_canvas3D()->get_wxglcanvas());
+ wxBusyInfo info(_L("Reload from:") + " " + from_u8(path), q->get_current_canvas3D()->get_wxglcanvas());
Model new_model;
try
@@ -3483,9 +3306,13 @@ void Plater::priv::reload_from_disk()
new_volume->set_material_id(old_volume->material_id());
new_volume->set_transformation(old_volume->get_transformation() * old_volume->source.transform);
new_volume->translate(new_volume->get_transformation().get_matrix(true) * (new_volume->source.mesh_offset - old_volume->source.mesh_offset));
+ if (old_volume->source.is_converted_from_inches)
+ new_volume->convert_from_imperial_units();
std::swap(old_model_object->volumes[sel_v.volume_idx], old_model_object->volumes.back());
old_model_object->delete_volume(old_model_object->volumes.size() - 1);
old_model_object->ensure_on_bed();
+
+ sla::reproject_points_and_holes(old_model_object);
}
}
}
@@ -3493,12 +3320,12 @@ void Plater::priv::reload_from_disk()
if (!fail_list.empty())
{
- wxString message = _(L("Unable to reload:")) + "\n";
+ wxString message = _L("Unable to reload:") + "\n";
for (const wxString& s : fail_list)
{
message += s + "\n";
}
- wxMessageDialog dlg(q, message, _(L("Error during reload")), wxOK | wxOK_DEFAULT | wxICON_WARNING);
+ wxMessageDialog dlg(q, message, _L("Error during reload"), wxOK | wxOK_DEFAULT | wxICON_WARNING);
dlg.ShowModal();
}
@@ -3517,7 +3344,7 @@ void Plater::priv::reload_all_from_disk()
if (model.objects.empty())
return;
- Plater::TakeSnapshot snapshot(q, _(L("Reload all from disk")));
+ Plater::TakeSnapshot snapshot(q, _L("Reload all from disk"));
Plater::SuppressSnapshots suppress(q);
Selection& selection = get_selection();
@@ -3538,9 +3365,54 @@ void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* =
if (obj_idx < 0)
return;
- Plater::TakeSnapshot snapshot(q, _(L("Fix Throught NetFabb")));
+ // Do not fix anything when a gizmo is open. There might be issues with updates
+ // and what is worse, the snapshot time would refer to the internal stack.
+ if (q->canvas3D()->get_gizmos_manager().get_current_type() != GLGizmosManager::Undefined) {
+ notification_manager->push_notification(
+ NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
+ NotificationManager::NotificationLevel::RegularNotification,
+ _u8L("ERROR: Please close all manipulators available from "
+ "the left toolbar before fixing the mesh."));
+ return;
+ }
+
+ // size_t snapshot_time = undo_redo_stack().active_snapshot_time();
+ Plater::TakeSnapshot snapshot(q, _L("Fix through NetFabb"));
+
+ ModelObject* mo = model.objects[obj_idx];
- fix_model_by_win10_sdk_gui(*model.objects[obj_idx], vol_idx);
+ // If there are custom supports/seams, remove them. Fixed mesh
+ // may be different and they would make no sense.
+ bool paint_removed = false;
+ for (ModelVolume* mv : mo->volumes) {
+ paint_removed |= ! mv->supported_facets.empty() || ! mv->seam_facets.empty();
+ mv->supported_facets.clear();
+ mv->seam_facets.clear();
+ }
+ if (paint_removed) {
+ // snapshot_time is captured by copy so the lambda knows where to undo/redo to.
+ notification_manager->push_notification(
+ NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
+ NotificationManager::NotificationLevel::RegularNotification,
+ _u8L("Custom supports and seams were removed after repairing the mesh."));
+// _u8L("Undo the repair"),
+// [this, snapshot_time](wxEvtHandler*){
+// // Make sure the snapshot is still available and that
+// // we are in the main stack and not in a gizmo-stack.
+// if (undo_redo_stack().has_undo_snapshot(snapshot_time)
+// && q->canvas3D()->get_gizmos_manager().get_current() == nullptr)
+// undo_redo_to(snapshot_time);
+// else
+// notification_manager->push_notification(
+// NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
+// NotificationManager::NotificationLevel::RegularNotification,
+// _u8L("Cannot undo to before the mesh repair!"));
+// return true;
+// });
+ }
+
+ fix_model_by_win10_sdk_gui(*mo, vol_idx);
+ sla::reproject_points_and_holes(mo);
this->update();
this->object_list_changed();
this->schedule_background_process();
@@ -3558,16 +3430,14 @@ void Plater::priv::set_current_panel(wxPanel* panel)
if (current_panel == panel)
return;
+ wxPanel* old_panel = current_panel;
current_panel = panel;
// to reduce flickering when changing view, first set as visible the new current panel
- for (wxPanel* p : panels)
- {
- if (p == current_panel)
- {
+ for (wxPanel* p : panels) {
+ if (p == current_panel) {
#ifdef __WXMAC__
// On Mac we need also to force a render to avoid flickering when changing view
- if (force_render)
- {
+ if (force_render) {
if (p == view3D)
dynamic_cast<View3D*>(p)->get_canvas3d()->render();
else if (p == preview)
@@ -3578,21 +3448,22 @@ void Plater::priv::set_current_panel(wxPanel* panel)
}
}
// then set to invisible the other
- for (wxPanel* p : panels)
- {
+ for (wxPanel* p : panels) {
if (p != current_panel)
p->Hide();
}
panel_sizer->Layout();
- if (current_panel == view3D)
- {
- if (view3D->is_reload_delayed())
- {
+ if (current_panel == view3D) {
+ if (old_panel == preview)
+ preview->get_canvas3d()->unbind_event_handlers();
+
+ view3D->get_canvas3d()->bind_event_handlers();
+
+ if (view3D->is_reload_delayed()) {
// Delayed loading of the 3D scene.
- if (this->printer_technology == ptSLA)
- {
+ if (this->printer_technology == ptSLA) {
// Update the SLAPrint from the current Model, so that the reload_scene()
// pulls the correct data.
this->update_restart_background_process(true, false);
@@ -3603,13 +3474,19 @@ void Plater::priv::set_current_panel(wxPanel* panel)
// sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably)
view3D->set_as_dirty();
view_toolbar.select_item("3D");
+ if(notification_manager != nullptr)
+ notification_manager->set_in_preview(false);
}
- else if (current_panel == preview)
- {
+ else if (current_panel == preview) {
+ if (old_panel == view3D)
+ view3D->get_canvas3d()->unbind_event_handlers();
+
+ preview->get_canvas3d()->bind_event_handlers();
+
// see: Plater::priv::object_list_changed()
// FIXME: it may be better to have a single function making this check and let it be called wherever needed
bool export_in_progress = this->background_process.is_export_scheduled();
- bool model_fits = view3D->check_volumes_outside_state() != ModelInstance::PVS_Partly_Outside;
+ bool model_fits = view3D->check_volumes_outside_state() != ModelInstancePVS_Partly_Outside;
if (!model.objects.empty() && !export_in_progress && model_fits)
this->q->reslice();
// keeps current gcode preview, if any
@@ -3617,6 +3494,8 @@ void Plater::priv::set_current_panel(wxPanel* panel)
preview->set_as_dirty();
view_toolbar.select_item("Preview");
+ if (notification_manager != nullptr)
+ notification_manager->set_in_preview(true);
}
current_panel->SetFocusFromKbd();
@@ -3625,7 +3504,15 @@ void Plater::priv::set_current_panel(wxPanel* panel)
void Plater::priv::on_select_preset(wxCommandEvent &evt)
{
auto preset_type = static_cast<Preset::Type>(evt.GetInt());
- auto *combo = static_cast<PresetComboBox*>(evt.GetEventObject());
+ auto *combo = static_cast<PlaterPresetComboBox*>(evt.GetEventObject());
+
+ // see https://github.com/prusa3d/PrusaSlicer/issues/3889
+ // Under OSX: in case of use of a same names written in different case (like "ENDER" and "Ender"),
+ // m_presets_choice->GetSelection() will return first item, because search in PopupListCtrl is case-insensitive.
+ // So, use GetSelection() from event parameter
+ // But in this function we couldn't use evt.GetSelection(), because m_commandInt is used for preset_type
+ // Thus, get selection in this way:
+ int selection = combo->FindString(evt.GetString(), true);
auto idx = combo->get_extruder_idx();
@@ -3636,32 +3523,49 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
//! instead of
//! combo->GetStringSelection().ToUTF8().data());
- const std::string preset_name = wxGetApp().preset_bundle->get_preset_name_by_alias(preset_type,
- Preset::remove_suffix_modified(combo->GetString(combo->GetSelection()).ToUTF8().data()));
+ std::string preset_name = wxGetApp().preset_bundle->get_preset_name_by_alias(preset_type,
+ Preset::remove_suffix_modified(combo->GetString(selection).ToUTF8().data()));
if (preset_type == Preset::TYPE_FILAMENT) {
wxGetApp().preset_bundle->set_filament_preset(idx, preset_name);
}
+ bool select_preset = !combo->selection_is_changed_according_to_physical_printers();
// TODO: ?
if (preset_type == Preset::TYPE_FILAMENT && sidebar->is_multifilament()) {
// Only update the plater UI for the 2nd and other filaments.
- wxGetApp().preset_bundle->update_plater_filament_ui(idx, combo);
+ combo->update();
}
- else {
+ else if (select_preset) {
+ if (preset_type == Preset::TYPE_PRINTER) {
+ PhysicalPrinterCollection& physical_printers = wxGetApp().preset_bundle->physical_printers;
+ if(combo->is_selected_physical_printer())
+ preset_name = physical_printers.get_selected_printer_preset_name();
+ else
+ physical_printers.unselect_printer();
+ }
wxWindowUpdateLocker noUpdates(sidebar->presets_panel());
wxGetApp().get_tab(preset_type)->select_preset(preset_name);
}
// update plater with new config
- wxGetApp().plater()->on_config_change(wxGetApp().preset_bundle->full_config());
+ q->on_config_change(wxGetApp().preset_bundle->full_config());
+ if (preset_type == Preset::TYPE_PRINTER) {
/* Settings list can be changed after printer preset changing, so
* update all settings items for all item had it.
* Furthermore, Layers editing is implemented only for FFF printers
* and for SLA presets they should be deleted
*/
- if (preset_type == Preset::TYPE_PRINTER)
wxGetApp().obj_list()->update_object_list_by_printer_technology();
+ }
+
+#ifdef __WXMSW__
+ // From the Win 2004 preset combobox lose a focus after change the preset selection
+ // and that is why the up/down arrow doesn't work properly
+ // (see https://github.com/prusa3d/PrusaSlicer/issues/5531 ).
+ // So, set the focus to the combobox explicitly
+ combo->SetFocus();
+#endif
}
void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
@@ -3674,6 +3578,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
this->statusbar()->set_progress(evt.status.percent);
this->statusbar()->set_status_text(_(evt.status.text) + wxString::FromUTF8("…"));
+ //notification_manager->set_progress_bar_percentage("Slicing progress", (float)evt.status.percent / 100.0f);
}
if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE | PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) {
switch (this->printer_technology) {
@@ -3693,10 +3598,38 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
// Update the SLA preview. Only called if not RELOAD_SLA_SUPPORT_POINTS, as the block above will refresh the preview anyways.
this->preview->reload_print();
}
+
+ if (evt.status.flags & (PrintBase::SlicingStatus::UPDATE_PRINT_STEP_WARNINGS | PrintBase::SlicingStatus::UPDATE_PRINT_OBJECT_STEP_WARNINGS)) {
+ // Update notification center with warnings of object_id and its warning_step.
+ ObjectID object_id = evt.status.warning_object_id;
+ int warning_step = evt.status.warning_step;
+ PrintStateBase::StateWithWarnings state;
+ if (evt.status.flags & PrintBase::SlicingStatus::UPDATE_PRINT_STEP_WARNINGS) {
+ state = this->printer_technology == ptFFF ?
+ this->fff_print.step_state_with_warnings(static_cast<PrintStep>(warning_step)) :
+ this->sla_print.step_state_with_warnings(static_cast<SLAPrintStep>(warning_step));
+ } else if (this->printer_technology == ptFFF) {
+ const PrintObject *print_object = this->fff_print.get_object(object_id);
+ if (print_object)
+ state = print_object->step_state_with_warnings(static_cast<PrintObjectStep>(warning_step));
+ } else {
+ const SLAPrintObject *print_object = this->sla_print.get_object(object_id);
+ if (print_object)
+ state = print_object->step_state_with_warnings(static_cast<SLAPrintObjectStep>(warning_step));
+ }
+ // Now process state.warnings.
+ for (auto const& warning : state.warnings) {
+ if (warning.current) {
+ notification_manager->push_slicing_warning_notification(warning.message, false, object_id, warning_step);
+ add_warning(warning, object_id.id);
+ }
+ }
+ }
}
-void Plater::priv::on_slicing_completed(wxCommandEvent &)
+void Plater::priv::on_slicing_completed(wxCommandEvent & evt)
{
+ notification_manager->push_slicing_complete_notification(evt.GetInt(), is_sidebar_collapsed());
switch (this->printer_technology) {
case ptFFF:
this->update_fff_scene();
@@ -3709,9 +3642,66 @@ void Plater::priv::on_slicing_completed(wxCommandEvent &)
break;
default: break;
}
+
+}
+void Plater::priv::on_export_began(wxCommandEvent& evt)
+{
+ if (show_warning_dialog)
+ warnings_dialog();
+}
+void Plater::priv::on_slicing_began()
+{
+ clear_warnings();
+ notification_manager->close_notification_of_type(NotificationType::SlicingComplete);
+}
+void Plater::priv::add_warning(const Slic3r::PrintStateBase::Warning& warning, size_t oid)
+{
+ for (auto const& it : current_warnings) {
+ if (warning.message_id == it.first.message_id) {
+ if (warning.message_id != 0 || (warning.message_id == 0 && warning.message == it.first.message))
+ return;
+ }
+ }
+ current_warnings.emplace_back(std::pair<Slic3r::PrintStateBase::Warning, size_t>(warning, oid));
}
+void Plater::priv::actualize_slicing_warnings(const PrintBase &print)
+{
+ std::vector<ObjectID> ids = print.print_object_ids();
+ if (ids.empty()) {
+ clear_warnings();
+ return;
+ }
+ ids.emplace_back(print.id());
+ std::sort(ids.begin(), ids.end());
+ notification_manager->remove_slicing_warnings_of_released_objects(ids);
+ notification_manager->set_all_slicing_warnings_gray(true);
+}
+void Plater::priv::clear_warnings()
+{
+ notification_manager->close_slicing_errors_and_warnings();
+ this->current_warnings.clear();
+}
+bool Plater::priv::warnings_dialog()
+{
+ if (current_warnings.empty())
+ return true;
+ std::string text = _u8L("There are active warnings concerning sliced models:") + "\n";
+ bool empt = true;
+ for (auto const& it : current_warnings) {
+ int next_n = it.first.message.find_first_of('\n', 0);
+ text += "\n";
+ if (next_n != std::string::npos)
+ text += it.first.message.substr(0, next_n);
+ else
+ text += it.first.message;
+ }
+ //text += "\n\nDo you still wish to export?";
+ wxMessageDialog msg_wingow(this->q, text, wxString(SLIC3R_APP_NAME " ") + _L("generated warnings"), wxOK);
+ const auto res = msg_wingow.ShowModal();
+ return res == wxID_OK;
-void Plater::priv::on_process_completed(wxCommandEvent &evt)
+}
+void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
{
// Stop the background task, wait until the thread goes into the "Idle" state.
// At this point of time the thread should be either finished or canceled,
@@ -3720,28 +3710,35 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
this->statusbar()->reset_cancel_callback();
this->statusbar()->stop_busy();
- const bool canceled = evt.GetInt() < 0;
- const bool error = evt.GetInt() == 0;
- const bool success = evt.GetInt() > 0;
// Reset the "export G-code path" name, so that the automatic background processing will be enabled again.
this->background_process.reset_export();
-
- if (error) {
- wxString message = evt.GetString();
- if (message.IsEmpty())
- message = _(L("Export failed"));
- if (q->m_tracking_popup_menu)
- // We don't want to pop-up a message box when tracking a pop-up menu.
- // We postpone the error message instead.
- q->m_tracking_popup_menu_error_message = message;
- else
- show_error(q, message);
- this->statusbar()->set_status_text(message);
+ // This bool stops showing export finished notification even when process_completed_with_error is false
+ bool has_error = false;
+ if (evt.error()) {
+ std::pair<std::string, bool> message = evt.format_error_message();
+ if (evt.critical_error()) {
+ if (q->m_tracking_popup_menu)
+ // We don't want to pop-up a message box when tracking a pop-up menu.
+ // We postpone the error message instead.
+ q->m_tracking_popup_menu_error_message = message.first;
+ else
+ show_error(q, message.first, message.second);
+ } else
+ notification_manager->push_slicing_error_notification(message.first);
+ this->statusbar()->set_status_text(from_u8(message.first));
+ if (evt.invalidate_plater())
+ {
+ const wxString invalid_str = _L("Invalid data");
+ for (auto btn : { ActionButtonType::abReslice, ActionButtonType::abSendGCode, ActionButtonType::abExport })
+ sidebar->set_btn_label(btn, invalid_str);
+ process_completed_with_error = true;
+ }
+ has_error = true;
}
- if (canceled)
- this->statusbar()->set_status_text(_(L("Cancelled")));
+ if (evt.cancelled())
+ this->statusbar()->set_status_text(_L("Cancelled"));
- this->sidebar->show_sliced_info_sizer(success);
+ this->sidebar->show_sliced_info_sizer(evt.success());
// This updates the "Slice now", "Export G-code", "Arrange" buttons status.
// Namely, it refreshes the "Out of print bed" property of all the ModelObjects, and it enables
@@ -3762,15 +3759,23 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
default: break;
}
-
- if (canceled) {
+ if (evt.cancelled()) {
if (wxGetApp().get_mode() == comSimple)
sidebar->set_btn_label(ActionButtonType::abReslice, "Slice now");
show_action_buttons(true);
+ } else {
+ if(wxGetApp().get_mode() == comSimple) {
+ show_action_buttons(false);
+ }
+ // If writing to removable drive was scheduled, show notification with eject button
+ if (exporting_status == ExportingStatus::EXPORTING_TO_REMOVABLE && !has_error) {
+ show_action_buttons(false);
+ notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, true);
+ wxGetApp().removable_drive_manager()->set_exporting_finished(true);
+ }else if (exporting_status == ExportingStatus::EXPORTING_TO_LOCAL && !has_error)
+ notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, false);
}
- else if (this->writing_to_removable_device || wxGetApp().get_mode() == comSimple)
- show_action_buttons(false);
- this->writing_to_removable_device = false;
+ exporting_status = ExportingStatus::NOT_EXPORTING;
}
void Plater::priv::on_layer_editing_toggled(bool enable)
@@ -3798,12 +3803,11 @@ void Plater::priv::on_action_split_volumes(SimpleEvent&)
void Plater::priv::on_action_layersediting(SimpleEvent&)
{
view3D->enable_layers_editing(!view3D->is_layers_editing_enabled());
+ notification_manager->set_move_from_overlay(view3D->is_layers_editing_enabled());
}
void Plater::priv::on_object_select(SimpleEvent& evt)
{
-// this->take_snapshot(_(L("Object Selection")));
-
wxGetApp().obj_list()->update_selections();
selection_changed();
}
@@ -3830,6 +3834,8 @@ void Plater::priv::on_right_click(RBtnEvent& evt)
if (evt.data.second)
return;
+ int menu_item_convert_unit_position = 11;
+
if (printer_technology == ptSLA)
menu = &sla_object_menu;
else
@@ -3839,8 +3845,11 @@ void Plater::priv::on_right_click(RBtnEvent& evt)
get_selection().is_single_full_object() ||
get_selection().is_multiple_full_instance();
menu = is_some_full_instances ? &object_menu : &part_menu;
+ if (!is_some_full_instances)
+ menu_item_convert_unit_position = 2;
}
+ sidebar->obj_list()->append_menu_item_convert_unit(menu, menu_item_convert_unit_position);
sidebar->obj_list()->append_menu_item_settings(menu);
if (printer_technology != ptSLA)
@@ -3853,7 +3862,7 @@ void Plater::priv::on_right_click(RBtnEvent& evt)
*/
const MenuIdentifier id = printer_technology == ptSLA ? miObjectSLA : miObjectFFF;
if (wxGetApp().get_mode() == comSimple) {
- if (menu->FindItem(_(L("Add instance"))) != wxNOT_FOUND)
+ if (menu->FindItem(_L("Add instance")) != wxNOT_FOUND)
{
/* Detach an items from the menu, but don't delete them
* so that they can be added back later
@@ -3865,7 +3874,7 @@ void Plater::priv::on_right_click(RBtnEvent& evt)
}
}
else {
- if (menu->FindItem(_(L("Add instance"))) == wxNOT_FOUND)
+ if (menu->FindItem(_L("Add instance")) == wxNOT_FOUND)
{
// Prepend items to the menu, if those aren't not there
menu->Prepend(items_set_number_of_copies[id]);
@@ -3939,7 +3948,6 @@ bool Plater::priv::init_object_menu()
return true;
}
-#if ENABLE_THUMBNAIL_GENERATOR
void Plater::priv::generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)
{
view3D->get_canvas3d()->render_thumbnail(data, w, h, printable_only, parts_only, show_bed, transparent_background);
@@ -3957,7 +3965,6 @@ void Plater::priv::generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds&
thumbnails.pop_back();
}
}
-#endif // ENABLE_THUMBNAIL_GENERATOR
void Plater::priv::msw_rescale_object_menu()
{
@@ -3996,21 +4003,23 @@ void Plater::priv::set_project_filename(const wxString& filename)
bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/)
{
if (is_part) {
- append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")),
+ append_menu_item(menu, wxID_ANY, _L("Delete") + "\tDel", _L("Remove the selected object"),
[this](wxCommandEvent&) { q->remove_selected(); }, "delete", nullptr, [this]() { return can_delete(); }, q);
- append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")),
+ append_menu_item(menu, wxID_ANY, _L("Reload from disk"), _L("Reload the selected volumes from disk"),
[this](wxCommandEvent&) { q->reload_from_disk(); }, "", menu, [this]() { return can_reload_from_disk(); }, q);
sidebar->obj_list()->append_menu_item_export_stl(menu);
}
else {
- wxMenuItem* item_increase = append_menu_item(menu, wxID_ANY, _(L("Add instance")) + "\t+", _(L("Add one more instance of the selected object")),
+ wxMenuItem* item_increase = append_menu_item(menu, wxID_ANY, _L("Add instance") + "\t+", _L("Add one more instance of the selected object"),
[this](wxCommandEvent&) { q->increase_instances(); }, "add_copies", nullptr, [this]() { return can_increase_instances(); }, q);
- wxMenuItem* item_decrease = append_menu_item(menu, wxID_ANY, _(L("Remove instance")) + "\t-", _(L("Remove one instance of the selected object")),
+ wxMenuItem* item_decrease = append_menu_item(menu, wxID_ANY, _L("Remove instance") + "\t-", _L("Remove one instance of the selected object"),
[this](wxCommandEvent&) { q->decrease_instances(); }, "remove_copies", nullptr, [this]() { return can_decrease_instances(); }, q);
- wxMenuItem* item_set_number_of_copies = append_menu_item(menu, wxID_ANY, _(L("Set number of instances")) + dots, _(L("Change the number of instances of the selected object")),
+ wxMenuItem* item_set_number_of_copies = append_menu_item(menu, wxID_ANY, _L("Set number of instances") + dots, _L("Change the number of instances of the selected object"),
[this](wxCommandEvent&) { q->set_number_of_copies(); }, "number_of_copies", nullptr, [this]() { return can_increase_instances(); }, q);
+ append_menu_item(menu, wxID_ANY, _L("Fill bed with instances") + dots, _L("Fill the remaining area of bed with instances of the selected object"),
+ [this](wxCommandEvent&) { q->fill_bed_with_instances(); }, "", nullptr, [this]() { return can_increase_instances(); }, q);
items_increase.push_back(item_increase);
@@ -4018,7 +4027,7 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
items_set_number_of_copies.push_back(item_set_number_of_copies);
// Delete menu was moved to be after +/- instace to make it more difficult to be selected by mistake.
- append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")),
+ append_menu_item(menu, wxID_ANY, _L("Delete") + "\tDel", _L("Remove the selected object"),
[this](wxCommandEvent&) { q->remove_selected(); }, "delete", nullptr, [this]() { return can_delete(); }, q);
menu->AppendSeparator();
@@ -4028,10 +4037,10 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
wxMenuItem* menu_item_printable = sidebar->obj_list()->append_menu_item_printable(menu, q);
menu->AppendSeparator();
- append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected object from disk")),
+ append_menu_item(menu, wxID_ANY, _L("Reload from disk"), _L("Reload the selected object from disk"),
[this](wxCommandEvent&) { reload_from_disk(); }, "", nullptr, [this]() { return can_reload_from_disk(); }, q);
- append_menu_item(menu, wxID_ANY, _(L("Export as STL")) + dots, _(L("Export the selected object as STL file")),
+ append_menu_item(menu, wxID_ANY, _L("Export as STL") + dots, _L("Export the selected object as STL file"),
[this](wxCommandEvent&) { q->export_stl(false, true); }, "", nullptr,
[this]() {
const Selection& selection = get_selection();
@@ -4040,6 +4049,9 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
menu->AppendSeparator();
+ // "Scale to print volume" makes a sense just for whole object
+ sidebar->obj_list()->append_menu_item_scale_selection_to_fit_print_volume(menu);
+
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) {
const Selection& selection = get_selection();
int instance_idx = selection.get_instance_idx();
@@ -4054,20 +4066,18 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
sidebar->obj_list()->append_menu_item_fix_through_netfabb(menu);
- sidebar->obj_list()->append_menu_item_scale_selection_to_fit_print_volume(menu);
-
wxMenu* mirror_menu = new wxMenu();
if (mirror_menu == nullptr)
return false;
- append_menu_item(mirror_menu, wxID_ANY, _(L("Along X axis")), _(L("Mirror the selected object along the X axis")),
+ append_menu_item(mirror_menu, wxID_ANY, _L("Along X axis"), _L("Mirror the selected object along the X axis"),
[this](wxCommandEvent&) { mirror(X); }, "mark_X", menu);
- append_menu_item(mirror_menu, wxID_ANY, _(L("Along Y axis")), _(L("Mirror the selected object along the Y axis")),
+ append_menu_item(mirror_menu, wxID_ANY, _L("Along Y axis"), _L("Mirror the selected object along the Y axis"),
[this](wxCommandEvent&) { mirror(Y); }, "mark_Y", menu);
- append_menu_item(mirror_menu, wxID_ANY, _(L("Along Z axis")), _(L("Mirror the selected object along the Z axis")),
+ append_menu_item(mirror_menu, wxID_ANY, _L("Along Z axis"), _L("Mirror the selected object along the Z axis"),
[this](wxCommandEvent&) { mirror(Z); }, "mark_Z", menu);
- append_submenu(menu, mirror_menu, wxID_ANY, _(L("Mirror")), _(L("Mirror the selected object")), "",
+ append_submenu(menu, mirror_menu, wxID_ANY, _L("Mirror"), _L("Mirror the selected object"), "",
[this]() { return can_mirror(); }, q);
return true;
@@ -4079,12 +4089,12 @@ bool Plater::priv::complit_init_object_menu()
if (split_menu == nullptr)
return false;
- append_menu_item(split_menu, wxID_ANY, _(L("To objects")), _(L("Split the selected object into individual objects")),
+ append_menu_item(split_menu, wxID_ANY, _L("To objects"), _L("Split the selected object into individual objects"),
[this](wxCommandEvent&) { split_object(); }, "split_object_SMALL", &object_menu, [this]() { return can_split(); }, q);
- append_menu_item(split_menu, wxID_ANY, _(L("To parts")), _(L("Split the selected object into individual sub-parts")),
+ append_menu_item(split_menu, wxID_ANY, _L("To parts"), _L("Split the selected object into individual sub-parts"),
[this](wxCommandEvent&) { split_volume(); }, "split_parts_SMALL", &object_menu, [this]() { return can_split(); }, q);
- append_submenu(&object_menu, split_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object")), "",
+ append_submenu(&object_menu, split_menu, wxID_ANY, _L("Split"), _L("Split the selected object"), "",
[this]() { return can_split() && wxGetApp().get_mode() > comSimple; }, q);
object_menu.AppendSeparator();
@@ -4099,21 +4109,25 @@ bool Plater::priv::complit_init_object_menu()
bool Plater::priv::complit_init_sla_object_menu()
{
- append_menu_item(&sla_object_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object into individual objects")),
+ append_menu_item(&sla_object_menu, wxID_ANY, _L("Split"), _L("Split the selected object into individual objects"),
[this](wxCommandEvent&) { split_object(); }, "split_object_SMALL", nullptr, [this]() { return can_split(); }, q);
sla_object_menu.AppendSeparator();
// Add the automatic rotation sub-menu
- append_menu_item(&sla_object_menu, wxID_ANY, _(L("Optimize orientation")), _(L("Optimize the rotation of the object for better print results.")),
- [this](wxCommandEvent&) { sla_optimize_rotation(); });
+ append_menu_item(
+ &sla_object_menu, wxID_ANY, _(L("Optimize orientation")),
+ _(L("Optimize the rotation of the object for better print results.")),
+ [this](wxCommandEvent &) {
+ m_ui_jobs.optimize_rotation();
+ });
return true;
}
bool Plater::priv::complit_init_part_menu()
{
- append_menu_item(&part_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object into individual sub-parts")),
+ append_menu_item(&part_menu, wxID_ANY, _L("Split"), _L("Split the selected object into individual sub-parts"),
[this](wxCommandEvent&) { split_volume(); }, "split_parts_SMALL", nullptr, [this]() { return can_split(); }, q);
part_menu.AppendSeparator();
@@ -4137,8 +4151,29 @@ GLCanvas3D* Plater::priv::get_current_canvas3D()
return (current_panel == view3D) ? view3D->get_canvas3d() : ((current_panel == preview) ? preview->get_canvas3d() : nullptr);
}
+void Plater::priv::unbind_canvas_event_handlers()
+{
+ if (view3D != nullptr)
+ view3D->get_canvas3d()->unbind_event_handlers();
+
+ if (preview != nullptr)
+ preview->get_canvas3d()->unbind_event_handlers();
+}
+
+void Plater::priv::reset_canvas_volumes()
+{
+ if (view3D != nullptr)
+ view3D->get_canvas3d()->reset_volumes();
+
+ if (preview != nullptr)
+ preview->get_canvas3d()->reset_volumes();
+}
+
bool Plater::priv::init_view_toolbar()
{
+ if (wxGetApp().is_gcode_viewer())
+ return true;
+
if (view_toolbar.get_items_count() > 0)
// already initialized
return true;
@@ -4182,6 +4217,70 @@ bool Plater::priv::init_view_toolbar()
return true;
}
+bool Plater::priv::init_collapse_toolbar()
+{
+ if (wxGetApp().is_gcode_viewer())
+ return true;
+
+ if (collapse_toolbar.get_items_count() > 0)
+ // already initialized
+ return true;
+
+ BackgroundTexture::Metadata background_data;
+ background_data.filename = "toolbar_background.png";
+ background_data.left = 16;
+ background_data.top = 16;
+ background_data.right = 16;
+ background_data.bottom = 16;
+
+ if (!collapse_toolbar.init(background_data))
+ return false;
+
+ collapse_toolbar.set_layout_type(GLToolbar::Layout::Vertical);
+ collapse_toolbar.set_horizontal_orientation(GLToolbar::Layout::HO_Right);
+ collapse_toolbar.set_vertical_orientation(GLToolbar::Layout::VO_Top);
+ collapse_toolbar.set_border(5.0f);
+ collapse_toolbar.set_separator_size(5);
+ collapse_toolbar.set_gap_size(2);
+
+ GLToolbarItem::Data item;
+
+ item.name = "collapse_sidebar";
+ item.icon_filename = "collapse.svg";
+ item.sprite_id = 0;
+ item.left.action_callback = []() {
+ wxGetApp().plater()->collapse_sidebar(!wxGetApp().plater()->is_sidebar_collapsed());
+ };
+
+ if (!collapse_toolbar.add_item(item))
+ return false;
+
+ // Now "collapse" sidebar to current state. This is done so the tooltip
+ // is updated before the toolbar is first used.
+ wxGetApp().plater()->collapse_sidebar(wxGetApp().plater()->is_sidebar_collapsed());
+ return true;
+}
+
+void Plater::priv::update_preview_bottom_toolbar()
+{
+ preview->update_bottom_toolbar();
+}
+
+void Plater::priv::update_preview_moves_slider()
+{
+ preview->update_moves_slider();
+}
+
+void Plater::priv::enable_preview_moves_slider(bool enable)
+{
+ preview->enable_moves_slider(enable);
+}
+
+void Plater::priv::reset_gcode_toolpaths()
+{
+ preview->get_canvas3d()->reset_gcode_toolpaths();
+}
+
bool Plater::priv::can_set_instance_to_object() const
{
const int obj_idx = get_selected_object_idx();
@@ -4256,11 +4355,10 @@ bool Plater::priv::can_reload_from_disk() const
return !paths.empty();
}
-void Plater::priv::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model)
+void Plater::priv::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom)
{
- bool new_shape = bed.set_shape(shape, custom_texture, custom_model);
- if (new_shape)
- {
+ bool new_shape = bed.set_shape(shape, custom_texture, custom_model, force_as_custom);
+ if (new_shape) {
if (view3D) view3D->bed_shape_changed();
if (preview) preview->bed_shape_changed();
}
@@ -4336,8 +4434,10 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice) const
this->ready_to_slice = ready_to_slice;
wxWindowUpdateLocker noUpdater(sidebar);
- const auto prin_host_opt = config->option<ConfigOptionString>("print_host");
- const bool send_gcode_shown = prin_host_opt != nullptr && !prin_host_opt->value.empty();
+
+ DynamicPrintConfig* selected_printer_config = wxGetApp().preset_bundle->physical_printers.get_selected_printer_config();
+ const auto print_host_opt = selected_printer_config ? selected_printer_config->option<ConfigOptionString>("print_host") : nullptr;
+ const bool send_gcode_shown = print_host_opt != nullptr && !print_host_opt->value.empty();
// when a background processing is ON, export_btn and/or send_btn are showing
if (wxGetApp().app_config->get("background_processing") == "1")
@@ -4346,8 +4446,8 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice) const
if (sidebar->show_reslice(false) |
sidebar->show_export(true) |
sidebar->show_send(send_gcode_shown) |
- sidebar->show_export_removable(removable_media_status.has_removable_drives) |
- sidebar->show_disconnect(removable_media_status.has_eject))
+ sidebar->show_export_removable(removable_media_status.has_removable_drives))
+// sidebar->show_eject(removable_media_status.has_eject))
sidebar->Layout();
}
else
@@ -4358,8 +4458,8 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice) const
if (sidebar->show_reslice(ready_to_slice) |
sidebar->show_export(!ready_to_slice) |
sidebar->show_send(send_gcode_shown && !ready_to_slice) |
- sidebar->show_export_removable(!ready_to_slice && removable_media_status.has_removable_drives) |
- sidebar->show_disconnect(!ready_to_slice && removable_media_status.has_eject))
+ sidebar->show_export_removable(!ready_to_slice && removable_media_status.has_removable_drives))
+// sidebar->show_eject(!ready_to_slice && removable_media_status.has_eject))
sidebar->Layout();
}
}
@@ -4468,8 +4568,8 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
if (printer_technology_changed) {
// Switching the printer technology when jumping forwards / backwards in time. Switch to the last active printer profile of the other type.
std::string s_pt = (it_snapshot->snapshot_data.printer_technology == ptFFF) ? "FFF" : "SLA";
- if (! wxGetApp().check_unsaved_changes(from_u8((boost::format(_utf8(
- L("%1% printer was active at the time the target Undo / Redo snapshot was taken. Switching to %1% printer requires reloading of %1% presets."))) % s_pt).str())))
+ if (! wxGetApp().check_unsaved_changes(format_wxstr(_L(
+ "%1% printer was active at the time the target Undo / Redo snapshot was taken. Switching to %1% printer requires reloading of %1% presets."), s_pt)))
// Don't switch the profiles.
return;
}
@@ -4594,6 +4694,34 @@ void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bo
BOOST_LOG_TRIVIAL(info) << "Undo / Redo snapshot reloaded. Undo / Redo stack memory: " << Slic3r::format_memsize_MB(this->undo_redo_stack().memsize()) << log_memory_info();
}
+void Plater::priv::bring_instance_forward() const
+{
+#ifdef __APPLE__
+ wxGetApp().other_instance_message_handler()->bring_instance_forward();
+ return;
+#endif //__APPLE__
+ if (main_frame == nullptr) {
+ BOOST_LOG_TRIVIAL(debug) << "Couldnt bring instance forward - mainframe is null";
+ return;
+ }
+ BOOST_LOG_TRIVIAL(debug) << "prusaslicer window going forward";
+ //this code maximize app window on Fedora
+ {
+ main_frame->Iconize(false);
+ if (main_frame->IsMaximized())
+ main_frame->Maximize(true);
+ else
+ main_frame->Maximize(false);
+ }
+ //this code maximize window on Ubuntu
+ {
+ main_frame->Restore();
+ wxGetApp().GetTopWindow()->SetFocus(); // focus on my window
+ wxGetApp().GetTopWindow()->Raise(); // bring window to front
+ wxGetApp().GetTopWindow()->Show(true); // show the window
+ }
+}
+
void Sidebar::set_btn_label(const ActionButtonType btn_type, const wxString& label) const
{
switch (btn_type)
@@ -4607,7 +4735,8 @@ void Sidebar::set_btn_label(const ActionButtonType btn_type, const wxString& lab
// Plater / Public
Plater::Plater(wxWindow *parent, MainFrame *main_frame)
- : wxPanel(parent), p(new priv(this, main_frame))
+ : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxGetApp().get_min_size())
+ , p(new priv(this, main_frame))
{
// Initialization performed in the private c-tor
}
@@ -4644,7 +4773,7 @@ void Plater::load_project(const wxString& filename)
return;
// Take the Undo / Redo snapshot.
- Plater::TakeSnapshot snapshot(this, _(L("Load Project")) + ": " + wxString::FromUTF8(into_path(filename).stem().string().c_str()));
+ Plater::TakeSnapshot snapshot(this, _L("Load Project") + ": " + wxString::FromUTF8(into_path(filename).stem().string().c_str()));
p->reset();
@@ -4658,7 +4787,7 @@ void Plater::load_project(const wxString& filename)
p->set_project_filename(filename);
}
-void Plater::add_model()
+void Plater::add_model(bool imperial_units/* = false*/)
{
wxArrayString input_files;
wxGetApp().import_model(this, input_files);
@@ -4672,11 +4801,11 @@ void Plater::add_model()
wxString snapshot_label;
assert(! paths.empty());
if (paths.size() == 1) {
- snapshot_label = _(L("Import Object"));
+ snapshot_label = _L("Import Object");
snapshot_label += ": ";
snapshot_label += wxString::FromUTF8(paths.front().filename().string().c_str());
} else {
- snapshot_label = _(L("Import Objects"));
+ snapshot_label = _L("Import Objects");
snapshot_label += ": ";
snapshot_label += wxString::FromUTF8(paths.front().filename().string().c_str());
for (size_t i = 1; i < paths.size(); ++ i) {
@@ -4686,7 +4815,12 @@ void Plater::add_model()
}
Plater::TakeSnapshot snapshot(this, snapshot_label);
- load_files(paths, true, false);
+ load_files(paths, true, false, imperial_units);
+}
+
+void Plater::import_sl1_archive()
+{
+ p->m_ui_jobs.import_sla_arch();
}
void Plater::extract_config_from_project()
@@ -4702,23 +4836,256 @@ void Plater::extract_config_from_project()
load_files(input_paths, false, true);
}
-std::vector<size_t> Plater::load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config) { return p->load_files(input_files, load_model, load_config); }
+void Plater::load_gcode()
+{
+ // Ask user for a gcode file name.
+ wxString input_file;
+ wxGetApp().load_gcode(this, input_file);
+ // And finally load the gcode file.
+ load_gcode(input_file);
+}
+
+void Plater::load_gcode(const wxString& filename)
+{
+ if (! is_gcode_file(into_u8(filename)) || m_last_loaded_gcode == filename)
+ return;
+
+ m_last_loaded_gcode = filename;
+
+ // cleanup view before to start loading/processing
+ p->gcode_result.reset();
+ reset_gcode_toolpaths();
+ p->preview->reload_print(false);
+ p->get_current_canvas3D()->render();
+
+ wxBusyCursor wait;
+
+ // process gcode
+ GCodeProcessor processor;
+ processor.enable_producers(true);
+ processor.process_file(filename.ToUTF8().data(), false);
+ p->gcode_result = std::move(processor.extract_result());
+
+ // show results
+ p->preview->reload_print(false);
+ p->preview->get_canvas3d()->zoom_to_gcode();
+
+ if (p->preview->get_canvas3d()->get_gcode_layers_zs().empty()) {
+ wxMessageDialog(this, _L("The selected file") + ":\n" + filename + "\n" + _L("does not contain valid gcode."),
+ wxString(GCODEVIEWER_APP_NAME) + " - " + _L("Error while loading .gcode file"), wxCLOSE | wxICON_WARNING | wxCENTRE).ShowModal();
+ set_project_filename(wxEmptyString);
+ }
+ else
+ set_project_filename(filename);
+}
+
+void Plater::reload_gcode_from_disk()
+{
+ wxString filename(m_last_loaded_gcode);
+ m_last_loaded_gcode.clear();
+ load_gcode(filename);
+}
+
+void Plater::refresh_print()
+{
+ p->preview->refresh_print();
+}
+
+std::vector<size_t> Plater::load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config, bool imperial_units /*= false*/) { return p->load_files(input_files, load_model, load_config, imperial_units); }
// To be called when providing a list of files to the GUI slic3r on command line.
-std::vector<size_t> Plater::load_files(const std::vector<std::string>& input_files, bool load_model, bool load_config)
+std::vector<size_t> Plater::load_files(const std::vector<std::string>& input_files, bool load_model, bool load_config, bool imperial_units /*= false*/)
{
std::vector<fs::path> paths;
paths.reserve(input_files.size());
for (const std::string& path : input_files)
paths.emplace_back(path);
- return p->load_files(paths, load_model, load_config);
+ return p->load_files(paths, load_model, load_config, imperial_units);
+}
+
+#if ENABLE_DRAG_AND_DROP_FIX
+enum class LoadType : unsigned char
+{
+ Unknown,
+ OpenProject,
+ LoadGeometry,
+ LoadConfig
+};
+
+class ProjectDropDialog : public DPIDialog
+{
+ wxRadioBox* m_action{ nullptr };
+public:
+ ProjectDropDialog(const std::string& filename);
+
+ int get_action() const { return m_action->GetSelection() + 1; }
+
+protected:
+ void on_dpi_changed(const wxRect& suggested_rect) override;
+};
+
+ProjectDropDialog::ProjectDropDialog(const std::string& filename)
+ : DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY,
+ from_u8((boost::format(_utf8(L("%s - Drop project file"))) % SLIC3R_APP_NAME).str()), wxDefaultPosition,
+ wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
+{
+ SetFont(wxGetApp().normal_font());
+
+ wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
+
+ const wxString choices[] = { _L("Open as project"),
+ _L("Import geometry only"),
+ _L("Import config only") };
+
+ main_sizer->Add(new wxStaticText(this, wxID_ANY,
+ _L("Select an action to apply to the file") + ": " + from_u8(filename)), 0, wxEXPAND | wxALL, 10);
+ m_action = new wxRadioBox(this, wxID_ANY, _L("Action"), wxDefaultPosition, wxDefaultSize,
+ WXSIZEOF(choices), choices, 0, wxRA_SPECIFY_ROWS);
+ int action = std::clamp(std::stoi(wxGetApp().app_config->get("drop_project_action")),
+ static_cast<int>(LoadType::OpenProject), static_cast<int>(LoadType::LoadConfig)) - 1;
+ m_action->SetSelection(action);
+ main_sizer->Add(m_action, 1, wxEXPAND | wxRIGHT | wxLEFT, 10);
+
+ wxBoxSizer* bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
+ wxCheckBox* check = new wxCheckBox(this, wxID_ANY, _L("Don't show again"));
+ check->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& evt) {
+ wxGetApp().app_config->set("show_drop_project_dialog", evt.IsChecked() ? "0" : "1");
+ });
+
+ bottom_sizer->Add(check, 0, wxEXPAND | wxRIGHT, 5);
+ bottom_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxLEFT, 5);
+ main_sizer->Add(bottom_sizer, 0, wxEXPAND | wxALL, 10);
+
+ SetSizer(main_sizer);
+ main_sizer->SetSizeHints(this);
+}
+
+void ProjectDropDialog::on_dpi_changed(const wxRect& suggested_rect)
+{
+ const int em = em_unit();
+ SetMinSize(wxSize(65 * em, 30 * em));
+ Fit();
+ Refresh();
+}
+
+bool Plater::load_files(const wxArrayString& filenames)
+{
+ const std::regex pattern_drop(".*[.](stl|obj|amf|3mf|prusa)", std::regex::icase);
+ const std::regex pattern_gcode_drop(".*[.](gcode|g)", std::regex::icase);
+
+ std::vector<fs::path> paths;
+
+ // gcode viewer section
+ if (wxGetApp().is_gcode_viewer()) {
+ for (const auto& filename : filenames) {
+ fs::path path(into_path(filename));
+ if (std::regex_match(path.string(), pattern_gcode_drop))
+ paths.push_back(std::move(path));
+ }
+
+ if (paths.size() > 1) {
+ wxMessageDialog(static_cast<wxWindow*>(this), _L("You can open only one .gcode file at a time."),
+ wxString(SLIC3R_APP_NAME) + " - " + _L("Drag and drop G-code file"), wxCLOSE | wxICON_WARNING | wxCENTRE).ShowModal();
+ return false;
+ }
+ else if (paths.size() == 1) {
+ load_gcode(from_path(paths.front()));
+ return true;
+ }
+ return false;
+ }
+
+ // editor section
+ for (const auto& filename : filenames) {
+ fs::path path(into_path(filename));
+ if (std::regex_match(path.string(), pattern_drop))
+ paths.push_back(std::move(path));
+ else if (std::regex_match(path.string(), pattern_gcode_drop))
+ start_new_gcodeviewer(&filename);
+ else
+ continue;
+ }
+ if (paths.empty())
+ // Likely all paths processed were gcodes, for which a G-code viewer instance has hopefully been started.
+ return false;
+
+ // searches for project files
+ for (std::vector<fs::path>::const_reverse_iterator it = paths.rbegin(); it != paths.rend(); ++it) {
+ std::string filename = (*it).filename().string();
+ if (boost::algorithm::iends_with(filename, ".3mf") || boost::algorithm::iends_with(filename, ".amf")) {
+ LoadType load_type = LoadType::Unknown;
+ if (!model().objects.empty()) {
+ if (wxGetApp().app_config->get("show_drop_project_dialog") == "1") {
+ ProjectDropDialog dlg(filename);
+ if (dlg.ShowModal() == wxID_OK) {
+ int choice = dlg.get_action();
+ load_type = static_cast<LoadType>(choice);
+ wxGetApp().app_config->set("drop_project_action", std::to_string(choice));
+ }
+ }
+ else
+ load_type = static_cast<LoadType>(std::clamp(std::stoi(wxGetApp().app_config->get("drop_project_action")),
+ static_cast<int>(LoadType::OpenProject), static_cast<int>(LoadType::LoadConfig)));
+ }
+ else
+ load_type = LoadType::OpenProject;
+
+ if (load_type == LoadType::Unknown)
+ return false;
+
+ switch (load_type) {
+ case LoadType::OpenProject: {
+ load_project(from_path(*it));
+ break;
+ }
+ case LoadType::LoadGeometry: {
+ Plater::TakeSnapshot snapshot(this, _L("Import Object"));
+ std::vector<fs::path> in_paths;
+ in_paths.emplace_back(*it);
+ load_files(in_paths, true, false);
+ break;
+ }
+ case LoadType::LoadConfig: {
+ std::vector<fs::path> in_paths;
+ in_paths.emplace_back(*it);
+ load_files(in_paths, false, true);
+ break;
+ }
+ }
+
+ return true;
+ }
+ }
+
+ // other files
+ wxString snapshot_label;
+ assert(!paths.empty());
+ if (paths.size() == 1) {
+ snapshot_label = _L("Load File");
+ snapshot_label += ": ";
+ snapshot_label += wxString::FromUTF8(paths.front().filename().string().c_str());
+ }
+ else {
+ snapshot_label = _L("Load Files");
+ snapshot_label += ": ";
+ snapshot_label += wxString::FromUTF8(paths.front().filename().string().c_str());
+ for (size_t i = 1; i < paths.size(); ++i) {
+ snapshot_label += ", ";
+ snapshot_label += wxString::FromUTF8(paths[i].filename().string().c_str());
+ }
+ }
+ Plater::TakeSnapshot snapshot(this, snapshot_label);
+ load_files(paths);
+
+ return true;
}
+#endif // ENABLE_DRAG_AND_DROP_FIX
void Plater::update() { p->update(); }
void Plater::stop_jobs() { p->m_ui_jobs.stop_all(); }
-void Plater::update_ui_from_settings() { p->update_ui_from_settings(); }
+void Plater::update_ui_from_settings(bool apply_free_camera_correction) { p->update_ui_from_settings(apply_free_camera_correction); }
void Plater::select_view(const std::string& direction) { p->select_view(direction); }
@@ -4731,6 +5098,11 @@ bool Plater::is_view3D_shown() const { return p->is_view3D_shown(); }
bool Plater::are_view3D_labels_shown() const { return p->are_view3D_labels_shown(); }
void Plater::show_view3D_labels(bool show) { p->show_view3D_labels(show); }
+bool Plater::is_sidebar_collapsed() const { return p->is_sidebar_collapsed(); }
+void Plater::collapse_sidebar(bool show) { p->collapse_sidebar(show); }
+
+bool Plater::is_view3D_layers_editing_enabled() const { return p->is_view3D_layers_editing_enabled(); }
+
void Plater::select_all() { p->select_all(); }
void Plater::deselect_all() { p->deselect_all(); }
@@ -4738,7 +5110,7 @@ void Plater::remove(size_t obj_idx) { p->remove(obj_idx); }
void Plater::reset() { p->reset(); }
void Plater::reset_with_confirm()
{
- if (wxMessageDialog((wxWindow*)this, _(L("All objects will be removed, continue?")), wxString(SLIC3R_APP_NAME) + " - " + _(L("Delete all")), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES)
+ if (wxMessageDialog(static_cast<wxWindow*>(this), _L("All objects will be removed, continue?"), wxString(SLIC3R_APP_NAME) + " - " + _L("Delete all"), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES)
reset();
}
@@ -4746,7 +5118,7 @@ void Plater::delete_object_from_model(size_t obj_idx) { p->delete_object_from_mo
void Plater::remove_selected()
{
- Plater::TakeSnapshot snapshot(this, _(L("Delete Selected Objects")));
+ Plater::TakeSnapshot snapshot(this, _L("Delete Selected Objects"));
this->p->view3D->delete_selected();
}
@@ -4754,7 +5126,7 @@ void Plater::increase_instances(size_t num)
{
if (! can_increase_instances()) { return; }
- Plater::TakeSnapshot snapshot(this, _(L("Increase Instances")));
+ Plater::TakeSnapshot snapshot(this, _L("Increase Instances"));
int obj_idx = p->get_selected_object_idx();
@@ -4771,17 +5143,16 @@ void Plater::increase_instances(size_t num)
// p->print.get_object(obj_idx)->add_copy(Slic3r::to_2d(offset_vec));
}
- sidebar().obj_list()->increase_object_instances(obj_idx, was_one_instance ? num + 1 : num);
-
if (p->get_config("autocenter") == "1")
- p->arrange();
+ arrange();
p->update();
p->get_selection().add_instance(obj_idx, (int)model_object->instances.size() - 1);
- p->selection_changed();
+ sidebar().obj_list()->increase_object_instances(obj_idx, was_one_instance ? num + 1 : num);
+ p->selection_changed();
this->p->schedule_background_process();
}
@@ -4789,7 +5160,7 @@ void Plater::decrease_instances(size_t num)
{
if (! can_decrease_instances()) { return; }
- Plater::TakeSnapshot snapshot(this, _(L("Decrease Instances")));
+ Plater::TakeSnapshot snapshot(this, _L("Decrease Instances"));
int obj_idx = p->get_selected_object_idx();
@@ -4820,12 +5191,12 @@ void Plater::set_number_of_copies(/*size_t num*/)
ModelObject* model_object = p->model.objects[obj_idx];
- const int num = wxGetNumberFromUser( " ", _(L("Enter the number of copies:")),
- _(L("Copies of the selected object")), model_object->instances.size(), 0, 1000, this );
+ const int num = wxGetNumberFromUser( " ", _L("Enter the number of copies:"),
+ _L("Copies of the selected object"), model_object->instances.size(), 0, 1000, this );
if (num < 0)
return;
- Plater::TakeSnapshot snapshot(this, wxString::Format(_(L("Set numbers of copies to %d")), num));
+ Plater::TakeSnapshot snapshot(this, wxString::Format(_L("Set numbers of copies to %d"), num));
int diff = num - (int)model_object->instances.size();
if (diff > 0)
@@ -4834,6 +5205,11 @@ void Plater::set_number_of_copies(/*size_t num*/)
decrease_instances(-diff);
}
+void Plater::fill_bed_with_instances()
+{
+ p->m_ui_jobs.fill_bed();
+}
+
bool Plater::is_selection_empty() const
{
return p->get_selection().is_empty() || p->get_selection().is_wipe_tower();
@@ -4844,6 +5220,37 @@ void Plater::scale_selection_to_fit_print_volume()
p->scale_selection_to_fit_print_volume();
}
+void Plater::convert_unit(bool from_imperial_unit)
+{
+ std::vector<int> obj_idxs, volume_idxs;
+ wxGetApp().obj_list()->get_selection_indexes(obj_idxs, volume_idxs);
+ if (obj_idxs.empty() && volume_idxs.empty())
+ return;
+
+ TakeSnapshot snapshot(this, from_imperial_unit ? _L("Convert from imperial units") : _L("Revert conversion from imperial units"));
+ wxBusyCursor wait;
+
+ ModelObjectPtrs objects;
+ for (int obj_idx : obj_idxs) {
+ ModelObject *object = p->model.objects[obj_idx];
+ object->convert_units(objects, from_imperial_unit, volume_idxs);
+ remove(obj_idx);
+ }
+ p->load_model_objects(objects);
+
+ Selection& selection = p->view3D->get_canvas3d()->get_selection();
+ size_t last_obj_idx = p->model.objects.size() - 1;
+
+ if (volume_idxs.empty()) {
+ for (size_t i = 0; i < objects.size(); ++i)
+ selection.add_object((unsigned int)(last_obj_idx - i), i == 0);
+ }
+ else {
+ for (int vol_idx : volume_idxs)
+ selection.add_volume(last_obj_idx, vol_idx, 0, false);
+ }
+}
+
void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper, bool keep_lower, bool rotate_lower)
{
wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds");
@@ -4855,7 +5262,7 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_uppe
return;
}
- Plater::TakeSnapshot snapshot(this, _(L("Cut by Plane")));
+ Plater::TakeSnapshot snapshot(this, _L("Cut by Plane"));
wxBusyCursor wait;
const auto new_objects = object->cut(instance_idx, z, keep_upper, keep_lower, rotate_lower);
@@ -4876,6 +5283,9 @@ void Plater::export_gcode(bool prefer_removable)
if (p->model.objects.empty())
return;
+ if (p->process_completed_with_error)
+ return;
+
// If possible, remove accents from accented latin characters.
// This function is useful for generating file names to be processed by legacy firmwares.
fs::path default_output_file;
@@ -4886,9 +5296,12 @@ void Plater::export_gcode(bool prefer_removable)
if (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID)
return;
default_output_file = this->p->background_process.output_filepath_for_project(into_path(get_project_filename(".3mf")));
- }
- catch (const std::exception &ex) {
- show_error(this, ex.what());
+ } catch (const Slic3r::PlaceholderParserError &ex) {
+ // Show the error with monospaced font.
+ show_error(this, ex.what(), true);
+ return;
+ } catch (const std::exception &ex) {
+ show_error(this, ex.what(), false);
return;
}
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
@@ -4906,7 +5319,7 @@ void Plater::export_gcode(bool prefer_removable)
fs::path output_path;
{
- wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save SL1 file as:")),
+ wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _L("Save G-code file as:") : _L("Save SL1 file as:"),
start_dir,
from_path(default_output_file.filename()),
GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : FT_PNGZIP, default_output_file.extension().string()),
@@ -4918,12 +5331,16 @@ void Plater::export_gcode(bool prefer_removable)
if (! output_path.empty()) {
bool path_on_removable_media = removable_drive_manager.set_and_verify_last_save_path(output_path.string());
+ p->notification_manager->new_export_began(path_on_removable_media);
+ p->exporting_status = path_on_removable_media ? ExportingStatus::EXPORTING_TO_REMOVABLE : ExportingStatus::EXPORTING_TO_LOCAL;
+ p->last_output_path = output_path.string();
+ p->last_output_dir_path = output_path.parent_path().string();
p->export_gcode(output_path, path_on_removable_media, PrintHostJob());
// Storing a path to AppConfig either as path to removable media or a path to internal media.
// is_path_on_removable_drive() is called with the "true" parameter to update its internal database as the user may have shuffled the external drives
// while the dialog was open.
appconfig.update_last_output_dir(output_path.parent_path().string(), path_on_removable_media);
- p->writing_to_removable_device = path_on_removable_media;
+
}
}
@@ -5013,25 +5430,33 @@ void Plater::export_stl(bool extended, bool selection_only)
? Transform3d::Identity()
: object->model_object()->instances[instance_idx]->get_transformation().get_matrix();
+ TriangleMesh inst_mesh;
+
if (has_pad_mesh)
{
TriangleMesh inst_pad_mesh = pad_mesh;
inst_pad_mesh.transform(inst_transform, is_left_handed);
- mesh.merge(inst_pad_mesh);
+ inst_mesh.merge(inst_pad_mesh);
}
if (has_supports_mesh)
{
TriangleMesh inst_supports_mesh = supports_mesh;
inst_supports_mesh.transform(inst_transform, is_left_handed);
- mesh.merge(inst_supports_mesh);
+ inst_mesh.merge(inst_supports_mesh);
}
TriangleMesh inst_object_mesh = object->get_mesh_to_print();
inst_object_mesh.transform(mesh_trafo_inv);
inst_object_mesh.transform(inst_transform, is_left_handed);
- mesh.merge(inst_object_mesh);
+ inst_mesh.merge(inst_object_mesh);
+
+ // ensure that the instance lays on the bed
+ inst_mesh.translate(0.0f, 0.0f, -inst_mesh.bounding_box().min[2]);
+
+ // merge instance with global mesh
+ mesh.merge(inst_mesh);
if (one_inst_only)
break;
@@ -5041,7 +5466,7 @@ void Plater::export_stl(bool extended, bool selection_only)
}
Slic3r::store_stl(path_u8.c_str(), &mesh, true);
- p->statusbar()->set_status_text(from_u8((boost::format(_utf8(L("STL file exported to %s"))) % path).str()));
+ p->statusbar()->set_status_text(format_wxstr(_L("STL file exported to %s"), path));
}
void Plater::export_amf()
@@ -5058,10 +5483,10 @@ void Plater::export_amf()
bool full_pathnames = wxGetApp().app_config->get("export_sources_full_pathnames") == "1";
if (Slic3r::store_amf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames)) {
// Success
- p->statusbar()->set_status_text(from_u8((boost::format(_utf8(L("AMF file exported to %s"))) % path).str()));
+ p->statusbar()->set_status_text(format_wxstr(_L("AMF file exported to %s"), path));
} else {
// Failure
- p->statusbar()->set_status_text(from_u8((boost::format(_utf8(L("Error exporting AMF file %s"))) % path).str()));
+ p->statusbar()->set_status_text(format_wxstr(_L("Error exporting AMF file %s"), path));
}
}
@@ -5086,20 +5511,16 @@ void Plater::export_3mf(const boost::filesystem::path& output_path)
const std::string path_u8 = into_u8(path);
wxBusyCursor wait;
bool full_pathnames = wxGetApp().app_config->get("export_sources_full_pathnames") == "1";
-#if ENABLE_THUMBNAIL_GENERATOR
ThumbnailData thumbnail_data;
p->generate_thumbnail(thumbnail_data, THUMBNAIL_SIZE_3MF.first, THUMBNAIL_SIZE_3MF.second, false, true, true, true);
if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames, &thumbnail_data)) {
-#else
- if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames)) {
-#endif // ENABLE_THUMBNAIL_GENERATOR
// Success
- p->statusbar()->set_status_text(from_u8((boost::format(_utf8(L("3MF file exported to %s"))) % path).str()));
+ p->statusbar()->set_status_text(format_wxstr(_L("3MF file exported to %s"), path));
p->set_project_filename(path);
}
else {
// Failure
- p->statusbar()->set_status_text(from_u8((boost::format(_utf8(L("Error exporting 3MF file %s"))) % path).str()));
+ p->statusbar()->set_status_text(format_wxstr(_L("Error exporting 3MF file %s"), path));
}
}
@@ -5131,13 +5552,12 @@ void Plater::export_toolpaths_to_obj() const
p->preview->get_canvas3d()->export_toolpaths_to_obj(into_u8(path).c_str());
}
-void Plater::hollow()
-{
- p->hollow();
-}
-
void Plater::reslice()
{
+ // There is "invalid data" button instead "slice now"
+ if (p->process_completed_with_error)
+ return;
+
// Stop arrange and (or) optimize rotation tasks.
this->stop_jobs();
@@ -5160,21 +5580,31 @@ void Plater::reslice()
if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0)
return;
+ bool clean_gcode_toolpaths = true;
if (p->background_process.running())
{
if (wxGetApp().get_mode() == comSimple)
- p->sidebar->set_btn_label(ActionButtonType::abReslice, _(L("Slicing")) + dots);
+ p->sidebar->set_btn_label(ActionButtonType::abReslice, _L("Slicing") + dots);
else
{
- p->sidebar->set_btn_label(ActionButtonType::abReslice, _(L("Slice now")));
+ p->sidebar->set_btn_label(ActionButtonType::abReslice, _L("Slice now"));
p->show_action_buttons(false);
}
}
else if (!p->background_process.empty() && !p->background_process.idle())
p->show_action_buttons(true);
+ else
+ clean_gcode_toolpaths = false;
+
+ if (clean_gcode_toolpaths)
+ reset_gcode_toolpaths();
+#if ENABLE_PREVIEW_TYPE_CHANGE
+ p->preview->reload_print(!clean_gcode_toolpaths);
+#else
// update type of preview
- p->preview->update_view_type(true);
+ p->preview->update_view_type(!clean_gcode_toolpaths);
+#endif // ENABLE_PREVIEW_TYPE_CHANGE
}
void Plater::reslice_SLA_supports(const ModelObject &object, bool postpone_error_messages)
@@ -5215,10 +5645,14 @@ void Plater::reslice_SLA_until_step(SLAPrintObjectStep step, const ModelObject &
void Plater::send_gcode()
{
- if (p->model.objects.empty()) { return; }
+ // if physical_printer is selected, send gcode for this printer
+ DynamicPrintConfig* physical_printer_config = wxGetApp().preset_bundle->physical_printers.get_selected_printer_config();
+ if (! physical_printer_config || p->model.objects.empty())
+ return;
- PrintHostJob upload_job(p->config);
- if (upload_job.empty()) { return; }
+ PrintHostJob upload_job(physical_printer_config);
+ if (upload_job.empty())
+ return;
// Obtain default output path
fs::path default_output_file;
@@ -5229,18 +5663,28 @@ void Plater::send_gcode()
if (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID)
return;
default_output_file = this->p->background_process.output_filepath_for_project(into_path(get_project_filename(".3mf")));
- }
- catch (const std::exception &ex) {
- show_error(this, ex.what());
+ } catch (const Slic3r::PlaceholderParserError& ex) {
+ // Show the error with monospaced font.
+ show_error(this, ex.what(), true);
+ return;
+ } catch (const std::exception& ex) {
+ show_error(this, ex.what(), false);
return;
}
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
- PrintHostSendDialog dlg(default_output_file, upload_job.printhost->can_start_print());
+ // Repetier specific: Query the server for the list of file groups.
+ wxArrayString groups;
+ {
+ wxBusyCursor wait;
+ upload_job.printhost->get_groups(groups);
+ }
+
+ PrintHostSendDialog dlg(default_output_file, upload_job.printhost->can_start_print(), groups);
if (dlg.ShowModal() == wxID_OK) {
upload_job.upload_data.upload_path = dlg.filename();
upload_job.upload_data.start_print = dlg.start_print();
-
+ upload_job.upload_data.group = dlg.group();
p->export_gcode(fs::path(), false, std::move(upload_job));
}
}
@@ -5304,6 +5748,18 @@ void Plater::undo_redo_topmost_string_getter(const bool is_undo, std::string& ou
out_text = "";
}
+bool Plater::search_string_getter(int idx, const char** label, const char** tooltip)
+{
+ const Search::OptionsSearcher& search_list = p->sidebar->get_searcher();
+
+ if (0 <= idx && (size_t)idx < search_list.size()) {
+ search_list[idx].get_marked_label_and_tooltip(label, tooltip);
+ return true;
+ }
+
+ return false;
+}
+
void Plater::on_extruders_change(size_t num_extruders)
{
auto& choices = sidebar().combos_filament();
@@ -5316,12 +5772,12 @@ void Plater::on_extruders_change(size_t num_extruders)
size_t i = choices.size();
while ( i < num_extruders )
{
- PresetComboBox* choice/*{ nullptr }*/;
+ PlaterPresetComboBox* choice/*{ nullptr }*/;
sidebar().init_filament_combo(&choice, i);
choices.push_back(choice);
// initialize selection
- wxGetApp().preset_bundle->update_plater_filament_ui(i, choice);
+ choice->update();
++i;
}
@@ -5363,9 +5819,14 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
}
p->config->set_key_value(opt_key, config.option(opt_key)->clone());
- if (opt_key == "printer_technology")
+ if (opt_key == "printer_technology") {
this->set_printer_technology(config.opt_enum<PrinterTechnology>(opt_key));
- else if ((opt_key == "bed_shape") || (opt_key == "bed_custom_texture") || (opt_key == "bed_custom_model")) {
+ // print technology is changed, so we should to update a search list
+ p->sidebar->update_searcher();
+ p->sidebar->show_sliced_info_sizer(false);
+ p->reset_gcode_toolpaths();
+ }
+ else if (opt_key == "bed_shape" || opt_key == "bed_custom_texture" || opt_key == "bed_custom_model") {
bed_shape_changed = true;
update_scheduled = true;
}
@@ -5382,7 +5843,9 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
}
else if(opt_key == "extruder_colour") {
update_scheduled = true;
+#if !ENABLE_PREVIEW_TYPE_CHANGE
p->preview->set_number_extruders(p->config->option<ConfigOptionStrings>(opt_key)->values.size());
+#endif // !ENABLE_PREVIEW_TYPE_CHANGE
p->sidebar->obj_list()->update_extruder_colors();
} else if(opt_key == "max_print_height") {
update_scheduled = true;
@@ -5395,9 +5858,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
}
if (bed_shape_changed)
- p->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values,
- p->config->option<ConfigOptionString>("bed_custom_texture")->value,
- p->config->option<ConfigOptionString>("bed_custom_model")->value);
+ set_bed_shape();
if (update_scheduled)
update();
@@ -5408,9 +5869,14 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
void Plater::set_bed_shape() const
{
- p->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values,
- p->config->option<ConfigOptionString>("bed_custom_texture")->value,
- p->config->option<ConfigOptionString>("bed_custom_model")->value);
+ set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values,
+ p->config->option<ConfigOptionString>("bed_custom_texture")->value,
+ p->config->option<ConfigOptionString>("bed_custom_model")->value);
+}
+
+void Plater::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom) const
+{
+ p->set_bed_shape(shape, custom_texture, custom_model, force_as_custom);
}
void Plater::force_filament_colors_update()
@@ -5452,50 +5918,53 @@ void Plater::force_print_bed_update()
void Plater::on_activate()
{
-#ifdef __linux__
+#if defined(__linux__) || defined(_WIN32)
wxWindow *focus_window = wxWindow::FindFocus();
// Activating the main frame, and no window has keyboard focus.
// Set the keyboard focus to the visible Canvas3D.
- if (this->p->view3D->IsShown() && (!focus_window || focus_window == this->p->view3D->get_wxglcanvas()))
- this->p->view3D->get_wxglcanvas()->SetFocus();
-
- else if (this->p->preview->IsShown() && (!focus_window || focus_window == this->p->view3D->get_wxglcanvas()))
- this->p->preview->get_wxglcanvas()->SetFocus();
+ if (this->p->view3D->IsShown() && wxWindow::FindFocus() != this->p->view3D->get_wxglcanvas())
+ CallAfter([this]() { this->p->view3D->get_wxglcanvas()->SetFocus(); });
+ else if (this->p->preview->IsShown() && wxWindow::FindFocus() != this->p->view3D->get_wxglcanvas())
+ CallAfter([this]() { this->p->preview->get_wxglcanvas()->SetFocus(); });
#endif
this->p->show_delayed_error_message();
}
// Get vector of extruder colors considering filament color, if extruder color is undefined.
-std::vector<std::string> Plater::get_extruder_colors_from_plater_config() const
+std::vector<std::string> Plater::get_extruder_colors_from_plater_config(const GCodeProcessor::Result* const result) const
{
- const Slic3r::DynamicPrintConfig* config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
- std::vector<std::string> extruder_colors;
- if (!config->has("extruder_colour")) // in case of a SLA print
- return extruder_colors;
+ if (wxGetApp().is_gcode_viewer() && result != nullptr)
+ return result->extruder_colors;
+ else {
+ const Slic3r::DynamicPrintConfig* config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
+ std::vector<std::string> extruder_colors;
+ if (!config->has("extruder_colour")) // in case of a SLA print
+ return extruder_colors;
- extruder_colors = (config->option<ConfigOptionStrings>("extruder_colour"))->values;
- if (!wxGetApp().plater())
- return extruder_colors;
+ extruder_colors = (config->option<ConfigOptionStrings>("extruder_colour"))->values;
+ if (!wxGetApp().plater())
+ return extruder_colors;
- const std::vector<std::string>& filament_colours = (p->config->option<ConfigOptionStrings>("filament_colour"))->values;
- for (size_t i = 0; i < extruder_colors.size(); ++i)
- if (extruder_colors[i] == "" && i < filament_colours.size())
- extruder_colors[i] = filament_colours[i];
+ const std::vector<std::string>& filament_colours = (p->config->option<ConfigOptionStrings>("filament_colour"))->values;
+ for (size_t i = 0; i < extruder_colors.size(); ++i)
+ if (extruder_colors[i] == "" && i < filament_colours.size())
+ extruder_colors[i] = filament_colours[i];
- return extruder_colors;
+ return extruder_colors;
+ }
}
/* Get vector of colors used for rendering of a Preview scene in "Color print" mode
* It consists of extruder colors and colors, saved in model.custom_gcode_per_print_z
*/
-std::vector<std::string> Plater::get_colors_for_color_print() const
+std::vector<std::string> Plater::get_colors_for_color_print(const GCodeProcessor::Result* const result) const
{
- std::vector<std::string> colors = get_extruder_colors_from_plater_config();
+ std::vector<std::string> colors = get_extruder_colors_from_plater_config(result);
colors.reserve(colors.size() + p->model.custom_gcode_per_print_z.gcodes.size());
for (const CustomGCode::Item& code : p->model.custom_gcode_per_print_z.gcodes)
- if (code.gcode == ColorChangeCode)
+ if (code.type == CustomGCode::ColorChange)
colors.emplace_back(code.color);
return colors;
@@ -5516,6 +5985,11 @@ bool Plater::is_export_gcode_scheduled() const
return p->background_process.is_export_scheduled();
}
+const Selection &Plater::get_selection() const
+{
+ return p->get_selection();
+}
+
int Plater::get_selected_object_idx()
{
return p->get_selected_object_idx();
@@ -5531,6 +6005,11 @@ GLCanvas3D* Plater::canvas3D()
return p->view3D->get_canvas3d();
}
+const GLCanvas3D* Plater::canvas3D() const
+{
+ return p->view3D->get_canvas3d();
+}
+
GLCanvas3D* Plater::get_current_canvas3D()
{
return p->get_current_canvas3D();
@@ -5541,20 +6020,38 @@ BoundingBoxf Plater::bed_shape_bb() const
return p->bed_shape_bb();
}
+void Plater::arrange()
+{
+ p->m_ui_jobs.arrange();
+}
+
void Plater::set_current_canvas_as_dirty()
{
p->set_current_canvas_as_dirty();
}
+void Plater::unbind_canvas_event_handlers()
+{
+ p->unbind_canvas_event_handlers();
+}
+
+void Plater::reset_canvas_volumes()
+{
+ p->reset_canvas_volumes();
+}
+
PrinterTechnology Plater::printer_technology() const
{
return p->printer_technology;
}
-void Plater::set_printer_technology(PrinterTechnology printer_technology)
+const DynamicPrintConfig * Plater::config() const { return p->config; }
+
+bool Plater::set_printer_technology(PrinterTechnology printer_technology)
{
p->printer_technology = printer_technology;
- if (p->background_process.select_technology(printer_technology)) {
+ bool ret = p->background_process.select_technology(printer_technology);
+ if (ret) {
// Update the active presets.
}
//FIXME for SLA synchronize
@@ -5563,8 +6060,14 @@ void Plater::set_printer_technology(PrinterTechnology printer_technology)
p->label_btn_export = printer_technology == ptFFF ? L("Export G-code") : L("Export");
p->label_btn_send = printer_technology == ptFFF ? L("Send G-code") : L("Send to printer");
- if (wxGetApp().mainframe)
+ if (wxGetApp().mainframe != nullptr)
wxGetApp().mainframe->update_menubar();
+
+ p->update_main_toolbar_tooltips();
+
+ p->sidebar->get_searcher().set_printer_technology(printer_technology);
+
+ return ret;
}
void Plater::changed_object(int obj_idx)
@@ -5637,7 +6140,10 @@ void Plater::show_action_buttons(const bool ready_to_slice) const { p->show_acti
void Plater::copy_selection_to_clipboard()
{
- if (can_copy_to_clipboard())
+ // At first try to copy selected values to the ObjectList's clipboard
+ // to check if Settings or Layers are selected in the list
+ // and then copy to 3DCanvas's clipboard if not
+ if (can_copy_to_clipboard() && !p->sidebar->obj_list()->copy_to_clipboard())
p->view3D->get_canvas3d()->get_selection().copy_to_clipboard();
}
@@ -5646,8 +6152,37 @@ void Plater::paste_from_clipboard()
if (!can_paste_from_clipboard())
return;
- Plater::TakeSnapshot snapshot(this, _(L("Paste From Clipboard")));
- p->view3D->get_canvas3d()->get_selection().paste_from_clipboard();
+ Plater::TakeSnapshot snapshot(this, _L("Paste From Clipboard"));
+
+ // At first try to paste values from the ObjectList's clipboard
+ // to check if Settings or Layers were copied
+ // and then paste from the 3DCanvas's clipboard if not
+ if (!p->sidebar->obj_list()->paste_from_clipboard())
+ p->view3D->get_canvas3d()->get_selection().paste_from_clipboard();
+}
+
+void Plater::search(bool plater_is_active)
+{
+ if (plater_is_active) {
+ // plater should be focused for correct navigation inside search window
+ this->SetFocus();
+
+ wxKeyEvent evt;
+#ifdef __APPLE__
+ evt.m_keyCode = 'f';
+#else /* __APPLE__ */
+ evt.m_keyCode = WXK_CONTROL_F;
+#endif /* __APPLE__ */
+ evt.SetControlDown(true);
+ canvas3D()->on_char(evt);
+ }
+ else
+ {
+ wxPoint pos = this->ClientToScreen(wxPoint(0, 0));
+ pos.x += em_unit(this) * 40;
+ pos.y += em_unit(this) * 4;
+ p->sidebar->get_searcher().search_dialog->Popup(pos);
+ }
}
void Plater::msw_rescale()
@@ -5664,11 +6199,37 @@ void Plater::msw_rescale()
GetParent()->Layout();
}
+void Plater::sys_color_changed()
+{
+ p->sidebar->sys_color_changed();
+
+ // msw_rescale_menu updates just icons, so use it
+ p->msw_rescale_object_menu();
+
+ Layout();
+ GetParent()->Layout();
+}
+
bool Plater::init_view_toolbar()
{
return p->init_view_toolbar();
}
+void Plater::enable_view_toolbar(bool enable)
+{
+ p->view_toolbar.set_enabled(enable);
+}
+
+bool Plater::init_collapse_toolbar()
+{
+ return p->init_collapse_toolbar();
+}
+
+void Plater::enable_collapse_toolbar(bool enable)
+{
+ p->collapse_toolbar.set_enabled(enable);
+}
+
const Camera& Plater::get_camera() const
{
return p->camera;
@@ -5679,6 +6240,69 @@ Camera& Plater::get_camera()
return p->camera;
}
+#if ENABLE_ENVIRONMENT_MAP
+void Plater::init_environment_texture()
+{
+ if (p->environment_texture.get_id() == 0)
+ p->environment_texture.load_from_file(resources_dir() + "/icons/Pmetal_001.png", false, GLTexture::SingleThreaded, false);
+}
+
+unsigned int Plater::get_environment_texture_id() const
+{
+ return p->environment_texture.get_id();
+}
+#endif // ENABLE_ENVIRONMENT_MAP
+
+const Bed3D& Plater::get_bed() const
+{
+ return p->bed;
+}
+
+Bed3D& Plater::get_bed()
+{
+ return p->bed;
+}
+
+const GLToolbar& Plater::get_view_toolbar() const
+{
+ return p->view_toolbar;
+}
+
+GLToolbar& Plater::get_view_toolbar()
+{
+ return p->view_toolbar;
+}
+
+const GLToolbar& Plater::get_collapse_toolbar() const
+{
+ return p->collapse_toolbar;
+}
+
+GLToolbar& Plater::get_collapse_toolbar()
+{
+ return p->collapse_toolbar;
+}
+
+void Plater::update_preview_bottom_toolbar()
+{
+ p->update_preview_bottom_toolbar();
+}
+
+void Plater::update_preview_moves_slider()
+{
+ p->update_preview_moves_slider();
+}
+
+void Plater::enable_preview_moves_slider(bool enable)
+{
+ p->enable_preview_moves_slider(enable);
+}
+
+void Plater::reset_gcode_toolpaths()
+{
+ p->reset_gcode_toolpaths();
+}
+
const Mouse3DController& Plater::get_mouse3d_controller() const
{
return p->mouse3d_controller;
@@ -5689,6 +6313,16 @@ Mouse3DController& Plater::get_mouse3d_controller()
return p->mouse3d_controller;
}
+const NotificationManager* Plater::get_notification_manager() const
+{
+ return p->notification_manager;
+}
+
+NotificationManager* Plater::get_notification_manager()
+{
+ return p->notification_manager;
+}
+
bool Plater::can_delete() const { return p->can_delete(); }
bool Plater::can_delete_all() const { return p->can_delete_all(); }
bool Plater::can_increase_instances() const { return p->can_increase_instances(); }
@@ -5704,7 +6338,7 @@ bool Plater::can_paste_from_clipboard() const
const Selection& selection = p->view3D->get_canvas3d()->get_selection();
const Selection::Clipboard& clipboard = selection.get_clipboard();
- if (clipboard.is_empty())
+ if (clipboard.is_empty() && p->sidebar->obj_list()->clipboard_is_empty())
return false;
if ((wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) && !clipboard.is_sla_compliant())
@@ -5736,10 +6370,23 @@ bool Plater::can_undo() const { return p->undo_redo_stack().has_undo_snapshot();
bool Plater::can_redo() const { return p->undo_redo_stack().has_redo_snapshot(); }
bool Plater::can_reload_from_disk() const { return p->can_reload_from_disk(); }
const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_redo_stack_main(); }
+void Plater::clear_undo_redo_stack_main() { p->undo_redo_stack_main().clear(); }
void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); }
void Plater::leave_gizmos_stack() { p->leave_gizmos_stack(); }
bool Plater::inside_snapshot_capture() { return p->inside_snapshot_capture(); }
+#if ENABLE_RENDER_STATISTICS
+void Plater::toggle_render_statistic_dialog()
+{
+ p->show_render_statistic_dialog = !p->show_render_statistic_dialog;
+}
+
+bool Plater::is_render_statistic_dialog_visible() const
+{
+ return p->show_render_statistic_dialog;
+}
+#endif // ENABLE_RENDER_STATISTICS
+
// Wrapper around wxWindow::PopupMenu to suppress error messages popping out while tracking the popup menu.
bool Plater::PopupMenu(wxMenu *menu, const wxPoint& pos)
{
@@ -5758,6 +6405,10 @@ bool Plater::PopupMenu(wxMenu *menu, const wxPoint& pos)
}
return out;
}
+void Plater::bring_instance_forward()
+{
+ p->bring_instance_forward();
+}
SuppressBackgroundProcessingUpdate::SuppressBackgroundProcessingUpdate() :
m_was_scheduled(wxGetApp().plater()->is_background_process_update_scheduled())