diff options
Diffstat (limited to 'src/slic3r/GUI/Plater.cpp')
-rw-r--r-- | src/slic3r/GUI/Plater.cpp | 286 |
1 files changed, 195 insertions, 91 deletions
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 941a181d3..2a80844e3 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -114,7 +114,7 @@ ObjectInfo::ObjectInfo(wxWindow *parent) : grid_sizer->AddGrowableCol(3, 1); auto init_info_label = [parent, grid_sizer](wxStaticText **info_label, wxString text_label) { - auto *text = new wxStaticText(parent, wxID_ANY, text_label); + auto *text = new wxStaticText(parent, wxID_ANY, text_label+":"); text->SetFont(wxGetApp().small_font()); *info_label = new wxStaticText(parent, wxID_ANY, ""); (*info_label)->SetFont(wxGetApp().small_font()); @@ -122,13 +122,13 @@ ObjectInfo::ObjectInfo(wxWindow *parent) : grid_sizer->Add(*info_label, 0); }; - init_info_label(&info_size, _(L("Size:"))); - init_info_label(&info_volume, _(L("Volume:"))); - init_info_label(&info_facets, _(L("Facets:"))); - init_info_label(&info_materials, _(L("Materials:"))); + init_info_label(&info_size, _(L("Size"))); + init_info_label(&info_volume, _(L("Volume"))); + init_info_label(&info_facets, _(L("Facets"))); + 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()); @@ -286,12 +286,17 @@ class FreqChangedParams : public OG_Settings { double m_brim_width = 0.0; wxButton* m_wiping_dialog_button{ nullptr }; + wxSizer* m_sizer {nullptr}; + + std::shared_ptr<ConfigOptionsGroup> m_og_sla; public: FreqChangedParams(wxWindow* parent, const int label_width); ~FreqChangedParams() {} wxButton* get_wiping_dialog_button() { return m_wiping_dialog_button; } - void Show(const bool show); + wxSizer* get_sizer() override; + ConfigOptionsGroup* get_og(const bool is_fff); + void Show(const bool is_fff); }; FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) : @@ -299,22 +304,13 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) : { DynamicPrintConfig* config = &wxGetApp().preset_bundle->prints.get_edited_preset().config; + // Frequently changed parameters for FFF_technology m_og->set_config(config); m_og->label_width = label_width; m_og->m_on_change = [config, this](t_config_option_key opt_key, boost::any value) { - TabPrint* tab_print = nullptr; - for (size_t i = 0; i < wxGetApp().tab_panel()->GetPageCount(); ++i) { - Tab *tab = dynamic_cast<Tab*>(wxGetApp().tab_panel()->GetPage(i)); - if (!tab) - continue; - if (tab->name() == "print") { - tab_print = static_cast<TabPrint*>(tab); - break; - } - } - if (tab_print == nullptr) - return; + Tab* tab_print = wxGetApp().get_tab(Preset::TYPE_PRINT); + if (!tab_print) return; if (opt_key == "fill_density") { value = m_og->get_config_value(*config, opt_key); @@ -413,19 +409,56 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) : return sizer; }; m_og->append_line(line); + + + // Frequently changed parameters for SLA_technology + m_og_sla = std::make_shared<ConfigOptionsGroup>(parent, ""); + DynamicPrintConfig* config_sla = &wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; + m_og_sla->set_config(config_sla); + m_og_sla->label_width = label_width*2; + + m_og_sla->m_on_change = [config_sla, this](t_config_option_key opt_key, boost::any value) { + Tab* tab = wxGetApp().get_tab(Preset::TYPE_SLA_PRINT); + if (!tab) return; + + tab->set_value(opt_key, value); + + DynamicPrintConfig new_conf = *config_sla; + new_conf.set_key_value(opt_key, new ConfigOptionBool(boost::any_cast<bool>(value))); + tab->load_config(new_conf); + tab->update_dirty(); + }; + + m_og_sla->append_single_option_line("supports_enable"); + m_og_sla->append_single_option_line("pad_enable"); + + m_sizer = new wxBoxSizer(wxVERTICAL); + m_sizer->Add(m_og->sizer, 0, wxEXPAND); + m_sizer->Add(m_og_sla->sizer, 0, wxEXPAND | wxTOP, 5); } -void FreqChangedParams::Show(const bool show) +wxSizer* FreqChangedParams::get_sizer() +{ + return m_sizer; +} + +void FreqChangedParams::Show(const bool is_fff) { - bool is_wdb_shown = m_wiping_dialog_button->IsShown(); - m_og->Show(show); + const bool is_wdb_shown = m_wiping_dialog_button->IsShown(); + m_og->Show(is_fff); + m_og_sla->Show(!is_fff); // correct showing of the FreqChangedParams sizer when m_wiping_dialog_button is hidden - if (show && !is_wdb_shown) + if (is_fff && !is_wdb_shown) m_wiping_dialog_button->Hide(); } +ConfigOptionsGroup* FreqChangedParams::get_og(const bool is_fff) +{ + return is_fff ? m_og.get() : m_og_sla.get(); +} + // Sidebar / private struct Sidebar::priv @@ -434,6 +467,7 @@ struct Sidebar::priv wxScrolledWindow *scrolled; + PrusaModeSizer *mode_sizer; wxFlexGridSizer *sizer_presets; PresetComboBox *combo_print; std::vector<PresetComboBox*> combos_filament; @@ -492,6 +526,9 @@ Sidebar::Sidebar(Plater *parent) auto *scrolled_sizer = new wxBoxSizer(wxVERTICAL); p->scrolled->SetSizer(scrolled_sizer); + // Sizer with buttons for mode changing + p->mode_sizer = new PrusaModeSizer(p->scrolled); + // The preset chooser p->sizer_presets = new wxFlexGridSizer(5, 2, 1, 2); p->sizer_presets->AddGrowableCol(1, 1); @@ -558,13 +595,14 @@ Sidebar::Sidebar(Plater *parent) p->sliced_info = new SlicedInfo(p->scrolled); // Sizer in the scrolled area + scrolled_sizer->Add(p->mode_sizer, 0, wxALIGN_RIGHT/*CENTER_HORIZONTAL*/ | wxBOTTOM | wxRIGHT, 5); scrolled_sizer->Add(p->sizer_presets, 0, wxEXPAND | wxLEFT, 2); scrolled_sizer->Add(p->sizer_params, 1, wxEXPAND); scrolled_sizer->Add(p->object_info, 0, wxEXPAND | wxTOP | wxLEFT, 20); scrolled_sizer->Add(p->sliced_info, 0, wxEXPAND | wxTOP | wxLEFT, 20); // Buttons underneath the scrolled area - p->btn_export_gcode = new wxButton(this, wxID_ANY, _(L("Export G-code…"))); + p->btn_export_gcode = new wxButton(this, wxID_ANY, _(L("Export G-code")) + dots); p->btn_export_gcode->SetFont(wxGetApp().bold_font()); p->btn_reslice = new wxButton(this, wxID_ANY, _(L("Slice now"))); p->btn_reslice->SetFont(wxGetApp().bold_font()); @@ -617,18 +655,24 @@ void Sidebar::update_presets(Preset::Type preset_type) PresetBundle &preset_bundle = *wxGetApp().preset_bundle; switch (preset_type) { - case Preset::TYPE_FILAMENT: - if (p->combos_filament.size() == 1) { + case Preset::TYPE_FILAMENT: + { + const int extruder_cnt = p->plater->printer_technology() != ptFFF ? 1 : + dynamic_cast<ConfigOptionFloats*>(preset_bundle.printers.get_edited_preset().config.option("nozzle_diameter"))->values.size(); + const int filament_cnt = p->combos_filament.size() > extruder_cnt ? extruder_cnt : p->combos_filament.size(); + + if (filament_cnt == 1) { // Single filament printer, synchronize the filament presets. - const std::string &name = preset_bundle.filaments.get_selected_preset().name; - preset_bundle.set_filament_preset(0, name); + const std::string &name = preset_bundle.filaments.get_selected_preset().name; + preset_bundle.set_filament_preset(0, name); } - for (size_t i = 0; i < p->combos_filament.size(); i++) { - preset_bundle.update_platter_filament_ui(i, p->combos_filament[i]); + for (size_t i = 0; i < filament_cnt; i++) { + preset_bundle.update_platter_filament_ui(i, p->combos_filament[i]); } break; + } case Preset::TYPE_PRINT: preset_bundle.prints.update_platter_ui(p->combo_print); @@ -673,6 +717,11 @@ void Sidebar::update_presets(Preset::Type preset_type) wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config); } +void Sidebar::update_mode_sizer(const Slic3r::ConfigOptionMode& mode) +{ + p->mode_sizer->SetMode(mode); +} + ObjectManipulation* Sidebar::obj_manipul() { return p->object_manipulation; @@ -693,9 +742,9 @@ wxScrolledWindow* Sidebar::scrolled_panel() return p->scrolled; } -ConfigOptionsGroup* Sidebar::og_freq_chng_params() +ConfigOptionsGroup* Sidebar::og_freq_chng_params(const bool is_fff) { - return p->frequently_changed_parameters->get_og(); + return p->frequently_changed_parameters->get_og(is_fff); } wxButton* Sidebar::get_wiping_dialog_button() @@ -711,7 +760,7 @@ void Sidebar::update_objects_list_extruder_column(int extruders_count) void Sidebar::show_info_sizer() { if (!p->plater->is_single_full_object_selection() || - m_mode < ConfigMenuModeExpert || + m_mode < comExpert || p->plater->model().objects.empty()) { p->object_info->Show(false); return; @@ -743,7 +792,7 @@ void Sidebar::show_info_sizer() wxString tooltip = wxString::Format(_(L("Auto-repaired (%d errors)")), errors); p->object_info->info_manifold->SetLabel(tooltip); - tooltip += wxString::Format(_(L(":\n%d degenerate facets, %d edges fixed, %d facets removed, " + 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); @@ -800,7 +849,7 @@ void Sidebar::show_sliced_info_sizer(const bool show) 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 = "Estimated printing time :"; + new_label = _(L("Estimated printing time")) +" :"; info_text = ""; if (ps.estimated_normal_print_time != "N/A") { new_label += wxString::Format("\n - %s", _(L("normal mode"))); @@ -885,11 +934,11 @@ struct Plater::priv MainFrame *main_frame; // Object popup menu - wxMenu object_menu; + PrusaMenu object_menu; // Part popup menu - wxMenu part_menu; + PrusaMenu part_menu; // SLA-Object popup menu - wxMenu sla_object_menu; + PrusaMenu sla_object_menu; // Data Slic3r::DynamicPrintConfig *config; // FIXME: leak? @@ -921,6 +970,9 @@ struct Plater::priv static const std::regex pattern_3mf; static const std::regex pattern_zip_amf; static const std::regex pattern_any_amf; +#if ENABLE_VOLUMES_CENTERING_FIXES + static const std::regex pattern_prusa; +#endif // ENABLE_VOLUMES_CENTERING_FIXES priv(Plater *q, MainFrame *main_frame); @@ -973,7 +1025,7 @@ struct Plater::priv UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT = 16, }; // returns bit mask of UpdateBackgroundProcessReturnState - unsigned int update_background_process(); + unsigned int update_background_process(bool force_validation = false); // Restart background processing thread based on a bitmask of UpdateBackgroundProcessReturnState. bool restart_background_process(unsigned int state); void update_restart_background_process(bool force_scene_update, bool force_preview_update); @@ -1014,6 +1066,7 @@ private: bool can_delete_object() const; bool can_increase_instances() const; bool can_decrease_instances() const; + bool can_set_instance_to_object() const; bool can_split_to_objects() const; bool can_split_to_volumes() const; bool can_split() const; @@ -1030,6 +1083,9 @@ const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf const std::regex Plater::priv::pattern_3mf(".*3mf", std::regex::icase); const std::regex Plater::priv::pattern_zip_amf(".*[.]zip[.]amf", std::regex::icase); const std::regex Plater::priv::pattern_any_amf(".*[.](amf|amf[.]xml|zip[.]amf)", std::regex::icase); +#if ENABLE_VOLUMES_CENTERING_FIXES +const std::regex Plater::priv::pattern_prusa(".*prusa", std::regex::icase); +#endif // ENABLE_VOLUMES_CENTERING_FIXES Plater::priv::priv(Plater *q, MainFrame *main_frame) : q(q) @@ -1039,7 +1095,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) "brim_width", "variable_layer_height", "serial_port", "serial_speed", "host_type", "print_host", "printhost_apikey", "printhost_cafile", "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" + "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. + "layer_height", "first_layer_height", "min_layer_height", "max_layer_height", + "brim_width", "perimeters", "perimeter_extruder", "fill_density", "infill_extruder", "top_solid_layers", "bottom_solid_layers", "solid_infill_extruder", + "support_material", "support_material_extruder", "support_material_interface_extruder", "support_material_contact_distance", "raft_layers" })) , sidebar(new Sidebar(q)) , delayed_scene_refresh(false) @@ -1076,9 +1136,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) this->background_process_timer.SetOwner(this->q, 0); this->q->Bind(wxEVT_TIMER, [this](wxTimerEvent &evt) { this->update_restart_background_process(false, false); }); +#if !ENABLE_REWORKED_BED_SHAPE_CHANGE auto *bed_shape = config->opt<ConfigOptionPoints>("bed_shape"); view3D->set_bed_shape(bed_shape->values); preview->set_bed_shape(bed_shape->values); +#endif // !ENABLE_REWORKED_BED_SHAPE_CHANGE update(); @@ -1106,7 +1168,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_OBJECT_SELECT, &priv::on_object_select, this); view3D_canvas->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this); view3D_canvas->Bind(EVT_GLCANVAS_RIGHT_CLICK, &priv::on_right_click, this); - view3D_canvas->Bind(EVT_GLCANVAS_MODEL_UPDATE, [this](SimpleEvent&) { this->schedule_background_process(); }); 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_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); }); @@ -1168,7 +1229,7 @@ void Plater::priv::update(bool force_full_scene_refresh) if (this->printer_technology == ptSLA) // Update the SLAPrint from the current Model, so that the reload_scene() // pulls the correct data. - update_status = this->update_background_process(); + update_status = this->update_background_process(false); this->view3D->reload_scene(false, force_full_scene_refresh); this->preview->reload_print(); if (this->printer_technology == ptSLA) @@ -1252,7 +1313,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_ } } - const auto loading = _(L("Loading…")); + const auto loading = _(L("Loading")) + dots; wxProgressDialog dlg(loading, loading); dlg.Pulse(); @@ -1268,6 +1329,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_ 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); const bool type_any_amf = !type_3mf && std::regex_match(path.string(), pattern_any_amf); +#if ENABLE_VOLUMES_CENTERING_FIXES + const bool type_prusa = std::regex_match(path.string(), pattern_prusa); +#endif // ENABLE_VOLUMES_CENTERING_FIXES Slic3r::Model model; try { @@ -1312,23 +1376,33 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_ { // The model should now be initialized - if (model.looks_like_multipart_object()) { - wxMessageDialog dlg(q, _(L( +#if ENABLE_VOLUMES_CENTERING_FIXES + if (!type_3mf && !type_any_amf && !type_prusa) { +#endif // ENABLE_VOLUMES_CENTERING_FIXES + if (model.looks_like_multipart_object()) { + wxMessageDialog dlg(q, _(L( "This file contains several objects positioned at multiple heights. " "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); - if (dlg.ShowModal() == wxID_YES) { - model.convert_multipart_object(nozzle_dmrs->values.size()); + )), _(L("Multi-part object detected")), wxICON_WARNING | wxYES | wxNO); + if (dlg.ShowModal() == wxID_YES) { + model.convert_multipart_object(nozzle_dmrs->values.size()); + } } +#if ENABLE_VOLUMES_CENTERING_FIXES } +#endif // ENABLE_VOLUMES_CENTERING_FIXES +#if !ENABLE_VOLUMES_CENTERING_FIXES if (type_3mf || type_any_amf) { +#endif // !ENABLE_VOLUMES_CENTERING_FIXES for (ModelObject* model_object : model.objects) { model_object->center_around_origin(); model_object->ensure_on_bed(); } +#if !ENABLE_VOLUMES_CENTERING_FIXES } +#endif // !ENABLE_VOLUMES_CENTERING_FIXES // check multi-part object adding for the SLA-printing if (printer_technology == ptSLA) @@ -1426,10 +1500,11 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode object->center_around_origin(); scaled_down = true; } else if (max_ratio > 5) { - const Vec3d inverse = ratio.cwiseInverse(); - for (ModelInstance *instance : model_object->instances) { + const Vec3d inverse = 1.0 / max_ratio * Vec3d::Ones(); + for (ModelInstance *instance : object->instances) { instance->set_scaling_factor(inverse); } + scaled_down = true; } object->ensure_on_bed(); @@ -1538,7 +1613,16 @@ void Plater::priv::selection_changed() view3D->enable_toolbar_item("fewer", can_decrease_instances()); view3D->enable_toolbar_item("splitobjects", can_split/*_to_objects*/()); view3D->enable_toolbar_item("splitvolumes", can_split/*_to_volumes*/()); - view3D->enable_toolbar_item("layersediting", layers_height_allowed()); + + // if the selection is not valid to allow for layer editing, we need to turn off the tool if it is running + bool enable_layer_editing = layers_height_allowed(); + if (!enable_layer_editing && view3D->is_layers_editing_enabled()) { + SimpleEvent evt(EVT_GLTOOLBAR_LAYERSEDITING); + on_action_layersediting(evt); + } + + view3D->enable_toolbar_item("layersediting", enable_layer_editing); + // forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears) view3D->render(); } @@ -1803,6 +1887,8 @@ void Plater::priv::schedule_background_process() { // Trigger the timer event after 0.5s this->background_process_timer.Start(500, wxTIMER_ONE_SHOT); + // Notify the Canvas3D that something has changed, so it may invalidate some of the layer editing stuff. + this->view3D->get_canvas3d()->set_config(this->config); } void Plater::priv::update_print_volume_state() @@ -1816,7 +1902,7 @@ void Plater::priv::update_print_volume_state() // Update background processing thread from the current config and Model. // Returns a bitmask of UpdateBackgroundProcessReturnState. -unsigned int Plater::priv::update_background_process() +unsigned int Plater::priv::update_background_process(bool force_validation) { // bitmap of enum UpdateBackgroundProcessReturnState unsigned int return_state = 0; @@ -1856,19 +1942,21 @@ unsigned int Plater::priv::update_background_process() } } - if (! this->background_process.empty()) { + if ((invalidated != Print::APPLY_STATUS_UNCHANGED || force_validation) && ! this->background_process.empty()) { + // 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()) { if (invalidated != Print::APPLY_STATUS_UNCHANGED && this->background_processing_enabled()) return_state |= UPDATE_BACKGROUND_PROCESS_RESTART; } else { // The print is not valid. + // The error returned from the Print needs to be translated into the local language. GUI::show_error(this->q, _(err)); return_state |= UPDATE_BACKGROUND_PROCESS_INVALID; } } - if (invalidated != Print::APPLY_STATUS_UNCHANGED && was_running && ! this->background_process.running() && + 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. wxCommandEvent evt(EVT_PROCESS_COMPLETED); @@ -1913,7 +2001,7 @@ void Plater::priv::export_gcode(fs::path output_path, PrintHostJob upload_job) } // bitmask of UpdateBackgroundProcessReturnState - unsigned int state = update_background_process(); + unsigned int state = update_background_process(true); if (state & priv::UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) view3D->reload_scene(false); @@ -1932,7 +2020,7 @@ void Plater::priv::export_gcode(fs::path output_path, PrintHostJob upload_job) void Plater::priv::update_restart_background_process(bool force_update_scene, bool force_update_preview) { // bitmask of UpdateBackgroundProcessReturnState - unsigned int state = this->update_background_process(); + unsigned int state = this->update_background_process(false); if (force_update_scene || (state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0) view3D->reload_scene(false); @@ -1982,7 +2070,7 @@ void Plater::priv::reload_from_disk() } } - // XXX: Restore more: layer_height_ranges, layer_height_profile, layer_height_profile_valid (?) + // XXX: Restore more: layer_height_ranges, layer_height_profile (?) } remove(obj_orig_idx); @@ -2013,7 +2101,7 @@ void Plater::priv::fix_through_netfabb(const int obj_idx) o->volumes[i]->config.apply(model_object->volumes[i]->config); } } - // FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile, layer_height_profile_valid, + // FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile } remove(obj_idx); @@ -2244,7 +2332,7 @@ void Plater::priv::on_right_click(Vec2dEvent& evt) return; wxMenu* menu = printer_technology == ptSLA ? &sla_object_menu : - get_selection().is_single_full_instance/*object*/() ? // show "Object menu" for each FullInstance instead of FullObject + get_selection().is_single_full_instance() ? // show "Object menu" for each FullInstance instead of FullObject &object_menu : &part_menu; sidebar->obj_list()->append_menu_item_settings(menu); @@ -2292,27 +2380,32 @@ bool Plater::priv::init_object_menu() bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/) { - wxMenuItem* item_delete = append_menu_item(menu, wxID_ANY, _(L("Delete\tDel")), _(L("Remove the selected object")), + wxMenuItem* item_delete = append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")), [this](wxCommandEvent&) { q->remove_selected(); }, "brick_delete.png"); if (!is_part){ - wxMenuItem* item_increase = append_menu_item(menu, wxID_ANY, _(L("Increase copies\t+")), _(L("Place one more copy of the selected object")), + wxMenuItem* item_increase = append_menu_item(menu, wxID_ANY, _(L("Increase copies")) + "\t+", _(L("Place one more copy of the selected object")), [this](wxCommandEvent&) { q->increase_instances(); }, "add.png"); - wxMenuItem* item_decrease = append_menu_item(menu, wxID_ANY, _(L("Decrease copies\t-")), _(L("Remove one copy of the selected object")), + wxMenuItem* item_decrease = append_menu_item(menu, wxID_ANY, _(L("Decrease copies")) + "\t-", _(L("Remove one copy of the selected object")), [this](wxCommandEvent&) { q->decrease_instances(); }, "delete.png"); - wxMenuItem* item_set_number_of_copies = append_menu_item(menu, wxID_ANY, _(L("Set number of copies…")), _(L("Change the number of copies of the selected object")), + wxMenuItem* item_set_number_of_copies = append_menu_item(menu, wxID_ANY, _(L("Set number of copies")) + dots, _(L("Change the number of copies of the selected object")), [this](wxCommandEvent&) { q->set_number_of_copies(); }, "textfield.png"); + + menu->AppendSeparator(); + wxMenuItem* item_instance_to_object = sidebar->obj_list()->append_menu_item_instance_to_object(menu); + if (q != nullptr) { q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_increase_instances()); }, item_increase->GetId()); q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_decrease_instances()); }, item_decrease->GetId()); q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_increase_instances()); }, item_set_number_of_copies->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_set_instance_to_object()); }, item_instance_to_object->GetId()); } menu->AppendSeparator(); append_menu_item(menu, wxID_ANY, _(L("Reload from Disk")), _(L("Reload the selected file from Disk")), [this](wxCommandEvent&) { reload_from_disk(); }); - append_menu_item(menu, wxID_ANY, _(L("Export object as STL…")), _(L("Export this single object as STL file")), + append_menu_item(menu, wxID_ANY, _(L("Export object as STL")) + dots, _(L("Export this single object as STL file")), [this](wxCommandEvent&) { q->export_stl(true); }); } menu->AppendSeparator(); @@ -2352,25 +2445,16 @@ bool Plater::priv::complit_init_object_menu() [this](wxCommandEvent&) { split_volume(); }, "shape_ungroup_p.png", &object_menu); wxMenuItem* item_split = append_submenu(&object_menu, split_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object")), "shape_ungroup.png"); - -// append_menu_item(&object_menu, wxID_ANY, _(L("Reload from Disk")), _(L("Reload the selected file from Disk")), -// [this](wxCommandEvent&) { reload_from_disk(); }); -// -// append_menu_item(&object_menu, wxID_ANY, _(L("Export object as STL…")), _(L("Export this single object as STL file")), -// [this](wxCommandEvent&) { q->export_stl(true); }); - - // Append "Add..." popupmenu object_menu.AppendSeparator(); - sidebar->obj_list()->append_menu_items_add_volume(&object_menu); -// object_menu.AppendSeparator(); + // "Add (volumes)" popupmenu will be added later in append_menu_items_add_volume() // ui updates needs to be binded to the parent panel if (q != nullptr) { - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects() || can_split_to_volumes*/()); }, item_split->GetId()); - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split_objects->GetId()); - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); }, item_split_volumes->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split_objects->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split_volumes->GetId()); } return true; } @@ -2380,16 +2464,16 @@ bool Plater::priv::complit_init_sla_object_menu() wxMenuItem* item_split = append_menu_item(&sla_object_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object into individual objects")), [this](wxCommandEvent&) { split_object(); }, "shape_ungroup_o.png"); + 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(); }); -// sla_object_menu.AppendSeparator(); - // ui updates needs to be binded to the parent panel if (q != nullptr) { - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split->GetId()); } return true; @@ -2400,15 +2484,15 @@ bool Plater::priv::complit_init_part_menu() wxMenuItem* item_split = append_menu_item(&part_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object into individual sub-parts")), [this](wxCommandEvent&) { split_volume(); }, "shape_ungroup_p.png"); + part_menu.AppendSeparator(); + auto obj_list = sidebar->obj_list(); obj_list->append_menu_item_change_type(&part_menu); -// part_menu.AppendSeparator(); - // ui updates needs to be binded to the parent panel if (q != nullptr) { - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); }, item_split->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split->GetId()); } return true; @@ -2439,7 +2523,7 @@ void Plater::priv::init_view_toolbar() GLToolbarItem::Data item; item.name = "3D"; - item.tooltip = GUI::L_str("3D editor view"); + item.tooltip = GUI::L_str("3D editor view [Ctrl+5]"); item.sprite_id = 0; item.action_event = EVT_GLVIEWTOOLBAR_3D; item.is_toggable = false; @@ -2447,7 +2531,7 @@ void Plater::priv::init_view_toolbar() return; item.name = "Preview"; - item.tooltip = GUI::L_str("Preview"); + item.tooltip = GUI::L_str("Preview [Ctrl+6]"); item.sprite_id = 1; item.action_event = EVT_GLVIEWTOOLBAR_PREVIEW; item.is_toggable = false; @@ -2476,6 +2560,12 @@ bool Plater::priv::can_increase_instances() const return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()); } +bool Plater::priv::can_set_instance_to_object() const +{ + const int obj_idx = get_selected_object_idx(); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1); +} + bool Plater::priv::can_decrease_instances() const { int obj_idx = get_selected_object_idx(); @@ -2499,8 +2589,6 @@ bool Plater::priv::can_split_to_volumes() const bool Plater::priv::can_split() const { - if (printer_technology == ptSLA) - return false; return sidebar->obj_list()->is_splittable(); } @@ -2594,6 +2682,16 @@ void Plater::extract_config_from_project() void Plater::load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config) { p->load_files(input_files, load_model, load_config); } +// To be called when providing a list of files to the GUI slic3r on command line. +void Plater::load_files(const std::vector<std::string>& input_files, bool load_model, bool load_config) +{ + std::vector<fs::path> paths; + paths.reserve(input_files.size()); + for (const std::string &path : input_files) + paths.emplace_back(path); + p->load_files(paths, load_model, load_config); +} + void Plater::update() { p->update(); } void Plater::update_ui_from_settings() { p->update_ui_from_settings(); } @@ -2704,6 +2802,10 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_uppe wxCHECK_RET(instance_idx < object->instances.size(), "instance_idx out of bounds"); + if (!keep_upper && !keep_lower) { + return; + } + const auto new_objects = object->cut(instance_idx, z, keep_upper, keep_lower, rotate_lower); remove(obj_idx); @@ -2838,7 +2940,7 @@ void Plater::reslice() { //FIXME Don't reslice if export of G-code or sending to OctoPrint is running. // bitmask of UpdateBackgroundProcessReturnState - unsigned int state = this->p->update_background_process(); + unsigned int state = this->p->update_background_process(true); if (state & priv::UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) this->p->view3D->reload_scene(false); // Only restarts if the state is valid. @@ -2904,7 +3006,7 @@ 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") this->set_printer_technology(config.opt_enum<PrinterTechnology>(opt_key)); - else if (opt_key == "bed_shape") { + else if (opt_key == "bed_shape") { if (p->view3D) p->view3D->set_bed_shape(p->config->option<ConfigOptionPoints>(opt_key)->values); if (p->preview) p->preview->set_bed_shape(p->config->option<ConfigOptionPoints>(opt_key)->values); update_scheduled = true; @@ -2929,12 +3031,14 @@ void Plater::on_config_change(const DynamicPrintConfig &config) p->preview->set_number_extruders(p->config->option<ConfigOptionStrings>(opt_key)->values.size()); } else if(opt_key == "max_print_height") { update_scheduled = true; - } else if(opt_key == "printer_model") { + } + else if (opt_key == "printer_model") { // update to force bed selection(for texturing) if (p->view3D) p->view3D->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values); if (p->preview) p->preview->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values); update_scheduled = true; - } else if (opt_key == "host_type" && this->p->printer_technology == ptSLA) { + } + else if (opt_key == "host_type" && this->p->printer_technology == ptSLA) { p->config->option<ConfigOptionEnum<PrintHostType>>(opt_key)->value = htSL1; } } |