From c5a67ff931f78adb78ed853756ba46c8d9da0fd4 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Fri, 30 Aug 2019 17:40:25 +0200 Subject: [PATCH] WIP: filament/material filtering per selected printers --- src/slic3r/GUI/ConfigWizard.cpp | 267 +++++++++++++++--------- src/slic3r/GUI/ConfigWizard_private.hpp | 52 +++-- src/slic3r/GUI/PresetBundle.hpp | 13 +- 3 files changed, 206 insertions(+), 126 deletions(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 1c70ad726..691a2f2cd 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -475,7 +475,7 @@ PagePrinters::PagePrinters(ConfigWizard *parent, picker->Bind(EVT_PRINTER_PICK, [this, appconfig](const PrinterPickerEvent &evt) { appconfig->set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable); - wizard_p()->on_printer_pick(this); + wizard_p()->on_printer_pick(this, evt); }); append(new wxStaticLine(this)); @@ -527,6 +527,7 @@ PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxStrin , list_l3(new PresetList(this)) , sel1_prev(wxNOT_FOUND) , sel2_prev(wxNOT_FOUND) + , presets_loaded(false) { append_spacer(VERTICAL_SPACING); @@ -559,12 +560,6 @@ PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxStrin append(grid); - list_l1->append(_(L("(All)")), &EMPTY); - - for (const std::string &type : materials->types) { - list_l1->append(type, &type); - } - list_l1->Bind(wxEVT_LISTBOX, [this](wxCommandEvent &) { update_lists(list_l1->GetSelection(), list_l2->GetSelection()); }); @@ -577,10 +572,27 @@ PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxStrin sel_all->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { select_all(true); }); sel_none->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { select_all(false); }); + reload_presets(); +} + +void PageMaterials::reload_presets() +{ + list_l1->Clear(); + + list_l1->append(_(L("(All)")), &EMPTY); + + for (const std::string &type : materials->types) { + list_l1->append(type, &type); + } + if (list_l1->GetCount() > 0) { list_l1->SetSelection(0); + sel1_prev = wxNOT_FOUND; + sel2_prev = wxNOT_FOUND; update_lists(0, 0); } + + presets_loaded = true; } void PageMaterials::update_lists(int sel1, int sel2) @@ -592,14 +604,14 @@ void PageMaterials::update_lists(int sel1, int sel2) // Refresh the second list // XXX: The vendor list is created with quadratic complexity here, - // but the number of vendors is realistically so small this shouldn't be a problem. + // but the number of vendors is going to be very small this shouldn't be a problem. list_l2->Clear(); list_l2->append(_(L("(All)")), &EMPTY); if (sel1 != wxNOT_FOUND) { const std::string &type = list_l1->get_data(sel1); - materials->filter_presets(type, EMPTY, [this](Preset &p) { + materials->filter_presets(type, EMPTY, [this](const Preset *p) { const std::string &vendor = this->materials->get_vendor(p); if (list_l2->find(vendor) == wxNOT_FOUND) { @@ -623,9 +635,9 @@ void PageMaterials::update_lists(int sel1, int sel2) const std::string &type = list_l1->get_data(sel1); const std::string &vendor = list_l2->get_data(sel2); - materials->filter_presets(type, vendor, [this](Preset &p) { - const int i = list_l3->append(p.name, &p); - const bool checked = wizard_p()->appconfig_new.has(materials->appconfig_section(), p.name); + materials->filter_presets(type, vendor, [this](const Preset *p) { + const int i = list_l3->append(p->name, p); + const bool checked = wizard_p()->appconfig_new.has(materials->appconfig_section(), p->name); list_l3->Check(i, checked); }); } @@ -660,6 +672,24 @@ void PageMaterials::select_all(bool select) } } +void PageMaterials::clear() +{ + list_l1->Clear(); + list_l2->Clear(); + list_l3->Clear(); + sel1_prev = wxNOT_FOUND; + sel2_prev = wxNOT_FOUND; + presets_loaded = false; +} + +void PageMaterials::on_activate() +{ + if (! presets_loaded) { + wizard_p()->update_materials(); + reload_presets(); + } +} + const char *PageCustom::default_profile_name = "My Settings"; @@ -952,8 +982,8 @@ ConfigWizardIndex::ConfigWizardIndex(wxWindow *parent) , bullet_black(ScalableBitmap(parent, "bullet_black.png")) , bullet_blue(ScalableBitmap(parent, "bullet_blue.png")) , bullet_white(ScalableBitmap(parent, "bullet_white.png")) - , item_active(0) - , item_hover(-1) + , item_active(NO_ITEM) + , item_hover(NO_ITEM) , last_page((size_t)-1) { SetMinSize(bg.bmp().GetSize()); @@ -1015,6 +1045,8 @@ void ConfigWizardIndex::go_prev() { // Search for a preceiding item that is a page (not a label, ie. page != nullptr) + if (item_active == NO_ITEM) { return; } + for (size_t i = item_active; i > 0; i--) { if (items[i - 1].page != nullptr) { go_to(i - 1); @@ -1027,6 +1059,8 @@ void ConfigWizardIndex::go_next() { // Search for a next item that is a page (not a label, ie. page != nullptr) + if (item_active == NO_ITEM) { return; } + for (size_t i = item_active + 1; i < items.size(); i++) { if (items[i].page != nullptr) { go_to(i); @@ -1035,19 +1069,27 @@ void ConfigWizardIndex::go_next() } } +// This one actually performs the go-to op void ConfigWizardIndex::go_to(size_t i) { - if (i < items.size() && items[i].page != nullptr) { + if (i != item_active + && i < items.size() + && items[i].page != nullptr) { + auto *new_active = items[i].page; auto *former_active = active_page(); - if (former_active != nullptr) { former_active->Hide(); } + if (former_active != nullptr) { + former_active->Hide(); + } item_active = i; - items[i].page->Show(); + new_active->Show(); wxCommandEvent evt(EVT_INDEX_PAGE, GetId()); AddPendingEvent(evt); Refresh(); + + new_active->on_activate(); } } @@ -1069,7 +1111,7 @@ void ConfigWizardIndex::clear() if (former_active != nullptr) { former_active->Hide(); } items.clear(); - item_active = 0; + item_active = NO_ITEM; } void ConfigWizardIndex::on_paint(wxPaintEvent & evt) @@ -1150,24 +1192,38 @@ void ConfigWizardIndex::msw_rescale() const std::string Materials::UNKNOWN = "(Unknown)"; +void Materials::push(const Preset *preset) +{ + presets.insert(preset); + types.insert(technology & T_FFF + ? Materials::get_filament_type(preset) + : Materials::get_material_type(preset)); +} + +void Materials::clear() +{ + presets.clear(); + types.clear(); +} + const std::string& Materials::appconfig_section() const { return (technology & T_FFF) ? AppConfig::SECTION_FILAMENTS : AppConfig::SECTION_MATERIALS; } -const std::string& Materials::get_type(Preset &preset) const +const std::string& Materials::get_type(const Preset *preset) const { return (technology & T_FFF) ? get_filament_type(preset) : get_material_type(preset); } -const std::string& Materials::get_vendor(Preset &preset) const +const std::string& Materials::get_vendor(const Preset *preset) const { return (technology & T_FFF) ? get_filament_vendor(preset) : get_material_vendor(preset); } -const std::string& Materials::get_filament_type(const Preset &preset) +const std::string& Materials::get_filament_type(const Preset *preset) { - const auto *opt = preset.config.opt("filament_type"); + const auto *opt = preset->config.opt("filament_type"); if (opt != nullptr && opt->values.size() > 0) { return opt->values[0]; } else { @@ -1175,15 +1231,15 @@ const std::string& Materials::get_filament_type(const Preset &preset) } } -const std::string& Materials::get_filament_vendor(const Preset &preset) +const std::string& Materials::get_filament_vendor(const Preset *preset) { - const auto *opt = preset.config.opt("filament_vendor"); + const auto *opt = preset->config.opt("filament_vendor"); return opt != nullptr ? opt->value : UNKNOWN; } -const std::string& Materials::get_material_type(Preset &preset) +const std::string& Materials::get_material_type(const Preset *preset) { - const auto *opt = preset.config.opt("material_type"); + const auto *opt = preset->config.opt("material_type"); if (opt != nullptr) { return opt->value; } else { @@ -1191,9 +1247,9 @@ const std::string& Materials::get_material_type(Preset &preset) } } -const std::string& Materials::get_material_vendor(const Preset &preset) +const std::string& Materials::get_material_vendor(const Preset *preset) { - const auto *opt = preset.config.opt("material_vendor"); + const auto *opt = preset->config.opt("material_vendor"); return opt != nullptr ? opt->value : UNKNOWN; } @@ -1275,73 +1331,8 @@ void ConfigWizard::priv::init_dialog_size() void ConfigWizard::priv::load_vendors() { - // const auto vendor_dir = fs::path(Slic3r::data_dir()) / "vendor"; - // const auto rsrc_vendor_dir = fs::path(resources_dir()) / "profiles"; - - // PresetBundle bundle; - // bundle.load_available_system_presets(); bundles = BundleMap::load(); - // // Load vendors from the "vendors" directory in datadir - // // XXX: The VendorProfile is loaded twice here, ditto below - // for (auto &dir_entry : boost::filesystem::directory_iterator(vendor_dir)) { - // if (Slic3r::is_ini_file(dir_entry)) { - // try { - // bundle.load_configbundle(dir_entry.path().string(), PresetBundle::LOAD_CFGBNDLE_SYSTEM); - - // auto vp = VendorProfile::from_ini(dir_entry.path()); - // vendors[vp.id] = std::move(vp); - // } - // catch (const std::exception& e) { - // BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what(); - // } - // } - // } - - // // Additionally load up vendors from the application resources directory, but only those not seen in the datadir - // for (auto &dir_entry : boost::filesystem::directory_iterator(rsrc_vendor_dir)) { - // if (Slic3r::is_ini_file(dir_entry)) { - // const auto id = dir_entry.path().stem().string(); - - // if (vendors.find(id) == vendors.end()) { - // try { - // bundle.load_configbundle(dir_entry.path().string(), PresetBundle::LOAD_CFGBNDLE_SYSTEM); - - // auto vp = VendorProfile::from_ini(dir_entry.path()); - // vendors_rsrc[vp.id] = dir_entry.path().filename().string(); - // vendors[vp.id] = std::move(vp); - // } - // catch (const std::exception& e) { - // BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what(); - // } - // } - // } - // } - - // // Move materials to our Materials container: - // for (auto &&f : bundle.filaments) { - // f.vendor = nullptr; - // filaments.presets.push_back(std::move(f)); - // filaments.types.insert(Materials::get_filament_type(f)); - // } - // for (auto &&m : bundle.sla_materials) { - // m.vendor = nullptr; - // sla_materials.presets.push_back(std::move(m)); - // sla_materials.types.insert(Materials::get_material_type(m)); - // } - for (auto &pair : bundles) { - for (auto &&f : pair.second.preset_bundle->filaments) { - f.vendor = nullptr; - filaments.presets.push_back(std::move(f)); - filaments.types.insert(Materials::get_filament_type(f)); - } - for (auto &&m : pair.second.preset_bundle->sla_materials) { - m.vendor = nullptr; - sla_materials.presets.push_back(std::move(m)); - sla_materials.types.insert(Materials::get_material_type(m)); - } - } - // Load up the set of vendors / models / variants the user has had enabled up till now AppConfig *app_config = wxGetApp().app_config; if (! app_config->legacy_datadir()) { @@ -1360,15 +1351,13 @@ void ConfigWizard::priv::load_vendors() } } -// TODO: This'll be done differently, cf. the design document - // Load up the materials enabled till now, - // apply defaults from vendor profiles if there are no selections yet. - // bundle.init_materials_selection(*app_config); + // Initialize the is_visible flag in printer Presets + for (auto &pair : bundles) { + pair.second.preset_bundle->load_installed_printers(appconfig_new); + } + + update_materials(); - // TODO: load up sane defaults if no previous data in AppConfig - // as per the design doc: - // - all f/m for installed printers if prev Slicer version - // - default f/m set from bundle + default for each printer from bundle if fresh install if (app_config->has_section(AppConfig::SECTION_FILAMENTS)) { appconfig_new.set_section(AppConfig::SECTION_FILAMENTS, app_config->get_section(AppConfig::SECTION_FILAMENTS)); } @@ -1420,12 +1409,66 @@ void ConfigWizard::priv::set_run_reason(RunReason run_reason) } } +void ConfigWizard::priv::update_materials() +{ + filaments.clear(); + sla_materials.clear(); + + if (any_fff_selected) { + // Iterate filaments in all bundles + for (const auto &pair : bundles) { + for (const auto &filament : pair.second.preset_bundle->filaments) { + // Check if filament is already added + if (filaments.containts(&filament)) { continue; } + + // Iterate printers in all bundles + for (const auto &pair : bundles) { + for (const auto &printer : pair.second.preset_bundle->printers) { + // Filter out inapplicable printers + if (!printer.is_visible || printer.printer_technology() != ptFFF) { + continue; + } + + if (filament.is_compatible_with_printer(printer)) { + filaments.push(&filament); + } + } + } + } + } + } + + if (any_sla_selected) { + // Iterate SLA materials in all bundles + for (const auto &pair : bundles) { + for (const auto &material : pair.second.preset_bundle->sla_materials) { + // Check if material is already added + if (sla_materials.containts(&material)) { continue; } + + // Iterate printers in all bundles + for (const auto &pair : bundles) { + for (const auto &printer : pair.second.preset_bundle->printers) { + // Filter out inapplicable printers + if (!printer.is_visible || printer.printer_technology() != ptSLA) { + continue; + } + + if (material.is_compatible_with_printer(printer)) { + sla_materials.push(&material); + } + } + } + } + } + } +} + void ConfigWizard::priv::on_custom_setup() { load_pages(); } -void ConfigWizard::priv::on_printer_pick(PagePrinters *page) +void ConfigWizard::priv::on_printer_pick(PagePrinters *page, const PrinterPickerEvent &evt) { if (page_msla->any_selected() != any_sla_selected || page_fff->any_selected() != any_fff_selected) { @@ -1434,6 +1477,24 @@ void ConfigWizard::priv::on_printer_pick(PagePrinters *page) load_pages(); } + + // Update the is_visible flag on relevant printer profiles + for (auto &pair : bundles) { + if (pair.first != evt.vendor_id) { continue; } + + for (auto &preset : pair.second.preset_bundle->printers) { + if (preset.config.opt_string("printer_model") == evt.model_id + && preset.config.opt_string("printer_variant") == evt.variant_name) { + preset.is_visible = evt.enable; + } + } + } + + if (page == page_fff) { + page_filaments->clear(); + } else if (page == page_msla) { + page_sla_materials->clear(); + } } void ConfigWizard::priv::on_3rdparty_install(const VendorProfile *vendor, bool install) @@ -1636,7 +1697,9 @@ ConfigWizard::ConfigWizard(wxWindow *parent) p->any_sla_selected = p->page_msla->any_selected(); p->any_fff_selected = p->page_fff->any_selected(); + p->load_pages(); + p->index->go_to(size_t{0}); vsizer->Add(topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN); vsizer->Add(hline, 0, wxEXPAND); diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index 708eb94fc..0d2123695 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -21,7 +21,6 @@ #include "libslic3r/PrintConfig.hpp" #include "slic3r/Utils/PresetUpdater.hpp" #include "AppConfig.hpp" -// #include "Preset.hpp" #include "PresetBundle.hpp" #include "BedShapeDialog.hpp" @@ -58,17 +57,23 @@ enum Technology { struct Materials { Technology technology; - std::vector presets; + std::set presets; std::set types; Materials(Technology technology) : technology(technology) {} + void push(const Preset *preset); + void clear(); + bool containts(const Preset *preset) { + return presets.find(preset) != presets.end(); + } + const std::string& appconfig_section() const; - const std::string& get_type(Preset &preset) const; - const std::string& get_vendor(Preset &preset) const; + const std::string& get_type(const Preset *preset) const; + const std::string& get_vendor(const Preset *preset) const; template void filter_presets(const std::string &type, const std::string &vendor, F cb) { - for (Preset &preset : presets) { + for (const Preset *preset : presets) { if ((type.empty() || get_type(preset) == type) && (vendor.empty() || get_vendor(preset) == vendor)) { cb(preset); } @@ -76,10 +81,10 @@ struct Materials } static const std::string UNKNOWN; - static const std::string& get_filament_type(const Preset &preset); - static const std::string& get_filament_vendor(const Preset &preset); - static const std::string& get_material_type(Preset &preset); - static const std::string& get_material_vendor(const Preset &preset); + static const std::string& get_filament_type(const Preset *preset); + static const std::string& get_filament_vendor(const Preset *preset); + static const std::string& get_material_type(const Preset *preset); + static const std::string& get_material_vendor(const Preset *preset); }; struct Bundle @@ -104,6 +109,7 @@ struct BundleMap: std::unordered_map const Bundle& prusa_bundle() const; }; +struct PrinterPickerEvent; // GUI elements @@ -170,6 +176,7 @@ struct ConfigWizardPage: wxPanel virtual void apply_custom_config(DynamicPrintConfig &config) {} virtual void set_run_reason(ConfigWizard::RunReason run_reason) {} + virtual void on_activate() {} }; struct PageWelcome: ConfigWizardPage @@ -209,6 +216,9 @@ template struct DataList : public T { DataList(wxWindow *parent) : T(parent, wxID_ANY) {} + // Note: We're _not_ using wxLB_SORT here because it doesn't do the right thing, + // eg. "ABS" is sorted before "(All)" + int append(const std::string &label, const D *data) { void *ptr = reinterpret_cast(const_cast(data)); return this->Append(from_u8(label), ptr); @@ -241,14 +251,19 @@ struct PageMaterials: ConfigWizardPage StringList *list_l1, *list_l2; PresetList *list_l3; int sel1_prev, sel2_prev; + bool presets_loaded; + + static const std::string EMPTY; PageMaterials(ConfigWizard *parent, Materials *materials, wxString title, wxString shortname, wxString list1name); + void reload_presets(); void update_lists(int sel1, int sel2); void select_material(int i); void select_all(bool select); + void clear(); - static const std::string EMPTY; + virtual void on_activate() override; }; struct PageCustom: ConfigWizardPage @@ -339,6 +354,8 @@ public: void msw_rescale(); int em() const { return em_w; } + + static const size_t NO_ITEM = size_t(-1); private: struct Item { @@ -379,14 +396,12 @@ struct ConfigWizard::priv ConfigWizard *q; ConfigWizard::RunReason run_reason = RR_USER; AppConfig appconfig_new; // Backing for vendor/model/variant and material selections in the GUI - // std::unordered_map vendors; - // PresetBundle bundle; // XXX: comment - BundleMap bundles; // XXX: comment + BundleMap bundles; // Holds all loaded config bundles, the key is the vendor names. + // Materials refers to Presets in those bundles by pointers. + // Also we update the is_visible flag in printer Presets according to the + // PrinterPickers state. Materials filaments; // Holds available filament presets and their types & vendors Materials sla_materials; // Ditto for SLA materials - // std::set install_3rdparty; - // XXX: rm: (?) - // std::unordered_map vendors_rsrc; // List of bundles to install from resources std::unique_ptr custom_config; // Backing for custom printer definition bool any_fff_selected; // Used to decide whether to display Filaments page bool any_sla_selected; // Used to decide whether to display SLA Materials page @@ -437,10 +452,11 @@ struct ConfigWizard::priv void set_start_page(ConfigWizard::StartPage start_page); void create_3rdparty_pages(); void set_run_reason(RunReason run_reason); + void update_materials(); void on_custom_setup(); - void on_printer_pick(PagePrinters *page); - void on_3rdparty_install(const VendorProfile *vendor, bool install); // XXX: ? + void on_printer_pick(PagePrinters *page, const PrinterPickerEvent &evt); + void on_3rdparty_install(const VendorProfile *vendor, bool install); void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater); diff --git a/src/slic3r/GUI/PresetBundle.hpp b/src/slic3r/GUI/PresetBundle.hpp index 0fea2a0f8..79519071b 100644 --- a/src/slic3r/GUI/PresetBundle.hpp +++ b/src/slic3r/GUI/PresetBundle.hpp @@ -134,20 +134,21 @@ public: void load_default_preset_bitmaps(wxWindow *window); + // FIXME: rm void load_available_system_presets(); // XXX: name XXX: retval (VendorMap stored internally) + // Set the is_visible flag for printer vendors, printer models and printer variants + // based on the user configuration. + // If the "vendor" section is missing, enable all models and variants of the particular vendor. + void load_installed_printers(const AppConfig &config); + static const char *PRUSA_BUNDLE; private: std::string load_system_presets(); // Merge one vendor's presets with the other vendor's presets, report duplicates. std::vector merge_presets(PresetBundle &&other); - // Set the "enabled" flag for printer vendors, printer models and printer variants - // based on the user configuration. - // If the "vendor" section is missing, enable all models and variants of the particular vendor. - void load_installed_printers(const AppConfig &config); - - // Set the enabled flag for filaments and sla materials, + // Set the is_visible flag for filaments and sla materials, // apply defaults based on enabled printers when no filaments/materials are installed. void load_installed_filaments(AppConfig &config); void load_installed_sla_materials(AppConfig &config);