mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	Split Preset.cpp,hpp to Preset.cpp,hpp and PresetBundle.cpp,hpp
This commit is contained in:
		
							parent
							
								
									1fee3633a0
								
							
						
					
					
						commit
						d564fc95df
					
				
					 8 changed files with 975 additions and 658 deletions
				
			
		
							
								
								
									
										154
									
								
								xs/src/slic3r/GUI/AppConfig.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								xs/src/slic3r/GUI/AppConfig.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,154 @@ | |||
| #include <GL/glew.h> | ||||
| 
 | ||||
| #include "../../libslic3r/libslic3r.h" | ||||
| #include "../../libslic3r/Utils.hpp" | ||||
| #include "AppConfig.hpp" | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <utility> | ||||
| #include <assert.h> | ||||
| 
 | ||||
| #include <boost/filesystem.hpp> | ||||
| #include <boost/nowide/cenv.hpp> | ||||
| #include <boost/nowide/fstream.hpp> | ||||
| #include <boost/property_tree/ini_parser.hpp> | ||||
| #include <boost/property_tree/ptree.hpp> | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| void AppConfig::reset() | ||||
| { | ||||
|     m_storage.clear(); | ||||
|     set_defaults(); | ||||
| }; | ||||
| 
 | ||||
| // Override missing or keys with their defaults.
 | ||||
| void AppConfig::set_defaults() | ||||
| { | ||||
|     // 2) Reset to defaults.
 | ||||
|     if (get("version_check").empty()) | ||||
|         set("version_check", "1"); | ||||
|     if (get("autocenter").empty()) | ||||
|         set("autocenter", "1"); | ||||
|     // Disable background processing by default as it is not stable.
 | ||||
|     if (get("background_processing").empty()) | ||||
|         set("background_processing", "0"); | ||||
|     // If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
 | ||||
|     // By default, Prusa has the controller hidden.
 | ||||
|     if (get("no_controller").empty()) | ||||
|         set("no_controller", "0"); | ||||
|     // If set, the "- default -" selections of print/filament/printer are suppressed, if there is a valid preset available.
 | ||||
|     if (get("no_defaults").empty()) | ||||
|         set("no_defaults", "1"); | ||||
| } | ||||
| 
 | ||||
| void AppConfig::load() | ||||
| { | ||||
|     // 1) Read the complete config file into a boost::property_tree.
 | ||||
|     namespace pt = boost::property_tree; | ||||
|     pt::ptree tree; | ||||
|     boost::nowide::ifstream ifs(AppConfig::config_path()); | ||||
|     pt::read_ini(ifs, tree); | ||||
| 
 | ||||
|     // 2) Parse the property_tree, extract the sections and key / value pairs.
 | ||||
|     for (const auto §ion : tree) { | ||||
|     	if (section.second.empty()) { | ||||
|     		// This may be a top level (no section) entry, or an empty section.
 | ||||
|     		std::string data = section.second.data(); | ||||
|     		if (! data.empty()) | ||||
|     			// If there is a non-empty data, then it must be a top-level (without a section) config entry.
 | ||||
|     			m_storage[""][section.first] = data; | ||||
|     	} else { | ||||
|     		// This must be a section name. Read the entries of a section.
 | ||||
|     		std::map<std::string, std::string> &storage = m_storage[section.first]; | ||||
|             for (auto &kvp : section.second) | ||||
|             	storage[kvp.first] = kvp.second.data(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Override missing or keys with their defaults.
 | ||||
|     this->set_defaults(); | ||||
|     m_dirty = false; | ||||
| } | ||||
| 
 | ||||
| void AppConfig::save() | ||||
| { | ||||
|     boost::nowide::ofstream c; | ||||
|     c.open(AppConfig::config_path(), std::ios::out | std::ios::trunc); | ||||
|     c << "# " << Slic3r::header_slic3r_generated() << std::endl; | ||||
|     // Make sure the "no" category is written first.
 | ||||
|     for (const std::pair<std::string, std::string> &kvp : m_storage[""]) | ||||
|         c << kvp.first << " = " << kvp.second << std::endl; | ||||
|     // Write the other categories.
 | ||||
|     for (const auto category : m_storage) { | ||||
|     	if (category.first.empty()) | ||||
|     		continue; | ||||
|     	c << std::endl << "[" << category.first << "]" << std::endl; | ||||
|     	for (const std::pair<std::string, std::string> &kvp : category.second) | ||||
| 	        c << kvp.first << " = " << kvp.second << std::endl; | ||||
| 	} | ||||
|     c.close(); | ||||
|     m_dirty = false; | ||||
| } | ||||
| 
 | ||||
| std::string AppConfig::get_last_dir() const | ||||
| { | ||||
|     const auto it = m_storage.find("recent"); | ||||
|     if (it != m_storage.end()) { | ||||
|         { | ||||
|             const auto it2 = it->second.find("skein_directory"); | ||||
|             if (it2 != it->second.end() && ! it2->second.empty()) | ||||
|                 return it2->second; | ||||
|         } | ||||
|         { | ||||
|             const auto it2 = it->second.find("config_directory"); | ||||
|             if (it2 != it->second.end() && ! it2->second.empty()) | ||||
|                 return it2->second; | ||||
|         } | ||||
|     } | ||||
|     return std::string(); | ||||
| } | ||||
| 
 | ||||
| void AppConfig::update_config_dir(const std::string &dir) | ||||
| { | ||||
|     this->set("recent", "config_directory", dir); | ||||
|     this->save(); | ||||
| } | ||||
| 
 | ||||
| void AppConfig::update_skein_dir(const std::string &dir) | ||||
| { | ||||
|     this->set("recent", "skein_directory", dir); | ||||
|     this->save(); | ||||
| } | ||||
| 
 | ||||
| std::string AppConfig::get_last_output_dir(const std::string &alt) const | ||||
| { | ||||
|     const auto it = m_storage.find(""); | ||||
|     if (it != m_storage.end()) { | ||||
|         const auto it2 = it->second.find("last_output_path"); | ||||
|         const auto it3 = it->second.find("remember_output_path"); | ||||
|         if (it2 != it->second.end() && it3 != it->second.end() && ! it2->second.empty() && it3->second == "1") | ||||
|             return it2->second; | ||||
|     } | ||||
|     return alt; | ||||
| } | ||||
| 
 | ||||
| void AppConfig::update_last_output_dir(const std::string &dir) | ||||
| { | ||||
|     this->set("", "last_output_path", dir); | ||||
|     this->save(); | ||||
| } | ||||
| 
 | ||||
| std::string AppConfig::config_path() | ||||
| { | ||||
| 	return boost::filesystem::canonical(boost::filesystem::path(Slic3r::data_dir()) / "config.ini").make_preferred().string(); | ||||
| } | ||||
| 
 | ||||
| bool AppConfig::exists() | ||||
| { | ||||
|     return boost::filesystem::exists(AppConfig::config_path()); | ||||
| } | ||||
| 
 | ||||
| }; // namespace Slic3r
 | ||||
							
								
								
									
										88
									
								
								xs/src/slic3r/GUI/AppConfig.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								xs/src/slic3r/GUI/AppConfig.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,88 @@ | |||
| #ifndef slic3r_AppConfig_hpp_ | ||||
| #define slic3r_AppConfig_hpp_ | ||||
| 
 | ||||
| #include <map> | ||||
| #include <string> | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| class AppConfig | ||||
| { | ||||
| public: | ||||
| 	AppConfig() : m_dirty(false) { this->reset(); } | ||||
| 
 | ||||
| 	// Clear and reset to defaults.
 | ||||
| 	void 			   	reset(); | ||||
| 	// Override missing or keys with their defaults.
 | ||||
| 	void 			   	set_defaults(); | ||||
| 
 | ||||
| 	// Load the config.ini from a user profile directory (or a datadir, if configured).
 | ||||
| 	void 			   	load(); | ||||
| 	// Store the config.ini into a user profile directory (or a datadir, if configured).
 | ||||
| 	void 			   	save(); | ||||
| 
 | ||||
| 	// Does this config need to be saved?
 | ||||
| 	bool 				dirty() const { return m_dirty; } | ||||
| 
 | ||||
| 	// Const accessor, it will return false if a section or a key does not exist.
 | ||||
| 	bool get(const std::string §ion, const std::string &key, std::string &value) const | ||||
| 	{ | ||||
| 		value.clear(); | ||||
| 		auto it = m_storage.find(section); | ||||
| 		if (it == m_storage.end()) | ||||
| 			return false; | ||||
| 		auto it2 = it->second.find(key); | ||||
| 		if (it2 == it->second.end())  | ||||
| 			return false; | ||||
| 		value = it2->second; | ||||
| 		return true; | ||||
| 	} | ||||
| 	std::string 		get(const std::string §ion, const std::string &key) const | ||||
| 		{ std::string value; this->get(section, key, value); return value; } | ||||
| 	std::string 		get(const std::string &key) const | ||||
| 		{ std::string value; this->get("", key, value); return value; } | ||||
| 	void			    set(const std::string §ion, const std::string &key, const std::string &value) | ||||
| 	{  | ||||
| 		std::string &old = m_storage[section][key]; | ||||
| 		if (old != value) { | ||||
| 			old = value; | ||||
| 			m_dirty = true; | ||||
| 		} | ||||
| 	} | ||||
| 	void			    set(const std::string &key, const std::string &value) | ||||
| 		{ this->set("", key, value);  } | ||||
| 	bool				has(const std::string §ion, const std::string &key) const | ||||
| 	{ | ||||
| 		auto it = m_storage.find(section); | ||||
| 		if (it == m_storage.end()) | ||||
| 			return false; | ||||
| 		auto it2 = it->second.find(key); | ||||
| 		return it2 != it->second.end() && ! it2->second.empty(); | ||||
| 	} | ||||
| 	bool				has(const std::string &key) const | ||||
| 		{ return this->has("", key); } | ||||
| 
 | ||||
| 	// return recent/skein_directory or recent/config_directory or empty string.
 | ||||
| 	std::string 		get_last_dir() const; | ||||
| 	void 				update_config_dir(const std::string &dir); | ||||
| 	void 				update_skein_dir(const std::string &dir); | ||||
| 
 | ||||
| 	std::string 		get_last_output_dir(const std::string &alt) const; | ||||
| 	void                update_last_output_dir(const std::string &dir); | ||||
| 
 | ||||
| 	// Get the default config path from Slic3r::data_dir().
 | ||||
| 	static std::string  config_path(); | ||||
| 
 | ||||
| 	// Does the config file exist?
 | ||||
| 	static bool 		exists(); | ||||
| 
 | ||||
| private: | ||||
| 	// Map of section, name -> value
 | ||||
| 	std::map<std::string, std::map<std::string, std::string>> 	m_storage; | ||||
| 	// Has any value been modified since the config.ini has been last saved or loaded?
 | ||||
| 	bool														m_dirty; | ||||
| }; | ||||
| 
 | ||||
| }; // namespace Slic3r
 | ||||
| 
 | ||||
| #endif /* slic3r_AppConfig_hpp_ */ | ||||
|  | @ -30,9 +30,13 @@ namespace Slic3r { | |||
| 
 | ||||
| // Suffix to be added to a modified preset name in the combo box.
 | ||||
| static std::string g_suffix_modified = " (modified)"; | ||||
| const std::string& Preset::suffix_modified() | ||||
| { | ||||
|     return g_suffix_modified; | ||||
| } | ||||
| // Remove an optional "(modified)" suffix from a name.
 | ||||
| // This converts a UI name to a unique preset identifier.
 | ||||
| std::string remove_suffix_modified(const std::string &name) | ||||
| std::string Preset::remove_suffix_modified(const std::string &name) | ||||
| { | ||||
|     return boost::algorithm::ends_with(name, g_suffix_modified) ? | ||||
|         name.substr(0, name.size() - g_suffix_modified.size()) : | ||||
|  | @ -41,7 +45,7 @@ std::string remove_suffix_modified(const std::string &name) | |||
| 
 | ||||
| // Load keys from a config file or a G-code.
 | ||||
| // Throw exceptions with reasonable messages if something goes wrong.
 | ||||
| static void load_config_file(DynamicPrintConfig &config, const std::string &path) | ||||
| void Preset::load_config_file(DynamicPrintConfig &config, const std::string &path) | ||||
| { | ||||
|     try { | ||||
|         if (boost::algorithm::iends_with(path, ".gcode") || boost::algorithm::iends_with(path, ".g")) | ||||
|  | @ -319,7 +323,7 @@ bool PresetCollection::update_dirty_ui(wxItemContainer *ui) | |||
|     // 2) Update the labels.
 | ||||
|     for (unsigned int ui_id = 0; ui_id < ui->GetCount(); ++ ui_id) { | ||||
|         std::string   old_label    = ui->GetString(ui_id).utf8_str().data(); | ||||
|         std::string   preset_name  = remove_suffix_modified(old_label); | ||||
|         std::string   preset_name  = Preset::remove_suffix_modified(old_label); | ||||
|         const Preset *preset       = this->find_preset(preset_name, false); | ||||
|         assert(preset != nullptr); | ||||
|         std::string new_label = preset->is_dirty ? preset->name + g_suffix_modified : preset->name; | ||||
|  | @ -346,7 +350,7 @@ Preset& PresetCollection::select_preset(size_t idx) | |||
| 
 | ||||
| bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, bool force) | ||||
| {    | ||||
|     std::string name = remove_suffix_modified(name_w_suffix); | ||||
|     std::string name = Preset::remove_suffix_modified(name_w_suffix); | ||||
|     // 1) Try to find the preset by its name.
 | ||||
|     Preset key(m_type, name, false); | ||||
|     auto it = std::lower_bound(m_presets.begin(), m_presets.end(), key); | ||||
|  | @ -373,229 +377,6 @@ bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, b | |||
|     return false; | ||||
| } | ||||
| 
 | ||||
| PresetBundle::PresetBundle() : | ||||
|     prints(Preset::TYPE_PRINT, print_options()),  | ||||
|     filaments(Preset::TYPE_FILAMENT, filament_options()),  | ||||
|     printers(Preset::TYPE_PRINTER, printer_options()), | ||||
|     m_bitmapCompatible(new wxBitmap), | ||||
|     m_bitmapIncompatible(new wxBitmap) | ||||
| { | ||||
|     ::wxInitAllImageHandlers(); | ||||
| 
 | ||||
|     // Create the ID config keys, as they are not part of the Static print config classes.
 | ||||
|     this->prints.preset(0).config.opt_string("print_settings_id", true); | ||||
|     this->filaments.preset(0).config.opt_string("filament_settings_id", true); | ||||
|     this->printers.preset(0).config.opt_string("print_settings_id", true); | ||||
|     // Create the "compatible printers" keys, as they are not part of the Static print config classes.
 | ||||
|     this->filaments.preset(0).config.optptr("compatible_printers", true); | ||||
|     this->prints.preset(0).config.optptr("compatible_printers", true); | ||||
| 
 | ||||
|     this->prints   .load_bitmap_default("cog.png"); | ||||
|     this->filaments.load_bitmap_default("spool.png"); | ||||
|     this->printers .load_bitmap_default("printer_empty.png"); | ||||
| 
 | ||||
|     // FIXME select some icons indicating compatibility.
 | ||||
|     this->load_compatible_bitmaps("cog.png", "cog.png"); | ||||
| } | ||||
| 
 | ||||
| PresetBundle::~PresetBundle() | ||||
| { | ||||
| 	assert(m_bitmapCompatible != nullptr); | ||||
| 	assert(m_bitmapIncompatible != nullptr); | ||||
| 	delete m_bitmapCompatible; | ||||
| 	m_bitmapCompatible = nullptr; | ||||
|     delete m_bitmapIncompatible; | ||||
| 	m_bitmapIncompatible = nullptr; | ||||
|     for (std::pair<const std::string, wxBitmap*> &bitmap : m_mapColorToBitmap) | ||||
|         delete bitmap.second; | ||||
| } | ||||
| 
 | ||||
| void PresetBundle::setup_directories() | ||||
| { | ||||
|     boost::filesystem::path dir = boost::filesystem::canonical(Slic3r::data_dir()); | ||||
|     if (! boost::filesystem::is_directory(dir)) | ||||
|         throw std::runtime_error(std::string("datadir does not exist: ") + Slic3r::data_dir()); | ||||
|     std::initializer_list<const char*> names = { "print", "filament", "printer" }; | ||||
|     for (const char *name : names) { | ||||
|         boost::filesystem::path subdir = (dir / subdir).make_preferred(); | ||||
|         if (! boost::filesystem::is_directory(subdir) &&  | ||||
|             ! boost::filesystem::create_directory(subdir)) | ||||
|             throw std::runtime_error(std::string("Slic3r was unable to create its data directory at ") + subdir.string()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void PresetBundle::load_presets(const std::string &dir_path) | ||||
| { | ||||
|     this->prints   .load_presets(dir_path, "print"); | ||||
|     this->filaments.load_presets(dir_path, "filament"); | ||||
|     this->printers .load_presets(dir_path, "printer"); | ||||
|     this->update_multi_material_filament_presets(); | ||||
| } | ||||
| 
 | ||||
| // Load selections (current print, current filaments, current printer) from config.ini
 | ||||
| // This is done just once on application start up.
 | ||||
| void PresetBundle::load_selections(const AppConfig &config) | ||||
| { | ||||
|     prints.select_preset_by_name(config.get("presets", "print"), true); | ||||
|     filaments.select_preset_by_name(config.get("presets", "filament"), true); | ||||
|     this->set_filament_preset(0, filaments.get_selected_preset().name); | ||||
|     for (int i = 1; i < 1000; ++ i) { | ||||
|         char name[64]; | ||||
|         sprintf(name, "filament_%d", i); | ||||
|         if (! config.has("presets", name)) | ||||
|             break; | ||||
|         this->set_filament_preset(i, name); | ||||
|     } | ||||
|     printers.select_preset_by_name(config.get("presets", "printer"), true); | ||||
| } | ||||
| 
 | ||||
| // Export selections (current print, current filaments, current printer) into config.ini
 | ||||
| void PresetBundle::export_selections(AppConfig &config) | ||||
| { | ||||
|     config.set("presets", "print",    prints   .get_selected_preset().name); | ||||
|     config.set("presets", "filament", filaments.get_selected_preset().name); | ||||
|     for (int i = 1; i < 1000; ++ i) { | ||||
|         char name[64]; | ||||
|         sprintf(name, "filament_%d", i); | ||||
|         config.set("presets", name, filament_presets[i]); | ||||
|     } | ||||
|     config.set("presets", "printer",  printers .get_selected_preset().name); | ||||
| } | ||||
| 
 | ||||
| bool PresetBundle::load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible) | ||||
| { | ||||
|     bool loaded_compatible   = m_bitmapCompatible  ->LoadFile( | ||||
|         wxString::FromUTF8(Slic3r::var(path_bitmap_compatible).c_str()), wxBITMAP_TYPE_PNG); | ||||
|     bool loaded_incompatible = m_bitmapIncompatible->LoadFile( | ||||
|         wxString::FromUTF8(Slic3r::var(path_bitmap_incompatible).c_str()), wxBITMAP_TYPE_PNG); | ||||
|     if (loaded_compatible) { | ||||
|         prints   .set_bitmap_compatible(m_bitmapCompatible); | ||||
|         filaments.set_bitmap_compatible(m_bitmapCompatible); | ||||
|         printers .set_bitmap_compatible(m_bitmapCompatible); | ||||
|     } | ||||
|     if (loaded_incompatible) { | ||||
|         prints   .set_bitmap_compatible(m_bitmapIncompatible); | ||||
|         filaments.set_bitmap_compatible(m_bitmapIncompatible); | ||||
|         printers .set_bitmap_compatible(m_bitmapIncompatible);         | ||||
|     } | ||||
|     return loaded_compatible && loaded_incompatible; | ||||
| } | ||||
| 
 | ||||
| DynamicPrintConfig PresetBundle::full_config() const | ||||
| {     | ||||
|     DynamicPrintConfig out; | ||||
|     out.apply(FullPrintConfig()); | ||||
|     out.apply(this->prints.get_edited_preset().config); | ||||
|     out.apply(this->printers.get_edited_preset().config); | ||||
| 
 | ||||
|     auto   *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(out.option("nozzle_diameter")); | ||||
|     size_t  num_extruders   = nozzle_diameter->values.size(); | ||||
| 
 | ||||
|     if (num_extruders <= 1) { | ||||
|         out.apply(this->filaments.get_edited_preset().config); | ||||
|     } else { | ||||
|         // Retrieve filament presets and build a single config object for them.
 | ||||
|         // First collect the filament configurations based on the user selection of this->filament_presets.
 | ||||
|         std::vector<const DynamicPrintConfig*> filament_configs; | ||||
|         for (const std::string &filament_preset_name : this->filament_presets) | ||||
|             filament_configs.emplace_back(&this->filaments.find_preset(filament_preset_name, true)->config); | ||||
| 		while (filament_configs.size() < num_extruders) | ||||
|             filament_configs.emplace_back(&this->filaments.first_visible().config); | ||||
|         // Option values to set a ConfigOptionVector from.
 | ||||
|         std::vector<const ConfigOption*> filament_opts(num_extruders, nullptr); | ||||
|         // loop through options and apply them to the resulting config.
 | ||||
|         for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) { | ||||
|             // Get a destination option.
 | ||||
|             ConfigOption *opt_dst = out.option(key, false); | ||||
|             if (opt_dst->is_scalar()) { | ||||
|                 // Get an option, do not create if it does not exist.
 | ||||
|                 const ConfigOption *opt_src = filament_configs.front()->option(key); | ||||
|                 if (opt_src != nullptr) | ||||
|                     opt_dst->set(opt_src); | ||||
|             } else { | ||||
|                 // Setting a vector value from all filament_configs.
 | ||||
|                 for (size_t i = 0; i < filament_opts.size(); ++ i) | ||||
|                     filament_opts[i] = filament_configs[i]->option(key); | ||||
|                 static_cast<ConfigOptionVectorBase*>(opt_dst)->set(filament_opts); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     static const char *keys[] = { "perimeter", "infill", "solid_infill", "support_material", "support_material_interface" }; | ||||
|     for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++ i) { | ||||
|         std::string key = std::string(keys[i]) + "_extruder"; | ||||
|         auto *opt = dynamic_cast<ConfigOptionInt*>(out.option(key, false)); | ||||
|         assert(opt != nullptr); | ||||
|         opt->value = std::min<int>(opt->value, std::min<int>(0, int(num_extruders) - 1)); | ||||
|     } | ||||
| 
 | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| // Load an external config file containing the print, filament and printer presets.
 | ||||
| // Instead of a config file, a G-code may be loaded containing the full set of parameters.
 | ||||
| // In the future the configuration will likely be read from an AMF file as well.
 | ||||
| // If the file is loaded successfully, its print / filament / printer profiles will be activated.
 | ||||
| void PresetBundle::load_config_file(const std::string &path) | ||||
| { | ||||
|     // 1) Initialize a config from full defaults.
 | ||||
|     DynamicPrintConfig config; | ||||
|     config.apply(FullPrintConfig()); | ||||
| 
 | ||||
|     // 2) Try to load the config file.
 | ||||
|     // Throw exceptions with reasonable messages if something goes wrong.
 | ||||
|     Slic3r::load_config_file(config, path); | ||||
| 
 | ||||
|     // 3) Create a name from the file name.
 | ||||
|     // Keep the suffix (.ini, .gcode, .amf, .3mf etc) to differentiate it from the normal profiles.
 | ||||
|     std::string name = boost::filesystem::path(path).filename().string(); | ||||
| 
 | ||||
|     // 3) If the loading succeeded, split and load the config into print / filament / printer settings.
 | ||||
|     // First load the print and printer presets.
 | ||||
|     for (size_t i_group = 0; i_group < 2; ++ i_group) { | ||||
|         PresetCollection &presets = (i_group == 0) ? this->prints : this->printers; | ||||
|         presets.load_preset(path, name, config).is_external = true; | ||||
|     } | ||||
| 
 | ||||
|     // Now load the filaments. If there are multiple filament presets, split them and load them.
 | ||||
|     auto   *nozzle_diameter   = dynamic_cast<const ConfigOptionFloats*>(config.option("nozzle_diameter")); | ||||
|     auto   *filament_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("filament_diameter")); | ||||
|     size_t  num_extruders     = std::min(nozzle_diameter->values.size(), filament_diameter->values.size()); | ||||
|     if (num_extruders <= 1) { | ||||
|         this->filaments.load_preset(path, name, config).is_external = true; | ||||
|         this->filament_presets.clear(); | ||||
|         this->filament_presets.emplace_back(name); | ||||
|     } else { | ||||
|         // Split the filament presets, load each of them separately.
 | ||||
|         std::vector<DynamicPrintConfig> configs(num_extruders, this->filaments.default_preset().config); | ||||
|         // loop through options and scatter them into configs.
 | ||||
|         for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) { | ||||
|             const ConfigOption *other_opt = config.option(key, false); | ||||
|             if (other_opt == nullptr) | ||||
|                 continue; | ||||
|             if (other_opt->is_scalar()) { | ||||
|                 for (size_t i = 0; i < configs.size(); ++ i) | ||||
|                     configs[i].option(key, false)->set(other_opt); | ||||
|             } else { | ||||
|                 for (size_t i = 0; i < configs.size(); ++ i) | ||||
|                     static_cast<ConfigOptionVectorBase*>(configs[i].option(key, false))->set_at(other_opt, 0, i); | ||||
|             } | ||||
|         } | ||||
|         // Load the configs into this->filaments and make them active.
 | ||||
|         filament_presets.clear(); | ||||
|         for (size_t i = 0; i < configs.size(); ++ i) { | ||||
|             char suffix[64]; | ||||
|             if (i == 0) | ||||
|                 suffix[0] = 0; | ||||
|             else | ||||
|                 sprintf(suffix, " (%d)", i); | ||||
|             // Load all filament presets, but only select the first one in the preset dialog.
 | ||||
|             this->filaments.load_preset(path, name + suffix, configs[i], i == 0).is_external = true; | ||||
|             filament_presets.emplace_back(name + suffix); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::string PresetCollection::name() const | ||||
| { | ||||
|     switch (this->type()) { | ||||
|  | @ -606,359 +387,4 @@ std::string PresetCollection::name() const | |||
|     } | ||||
| } | ||||
| 
 | ||||
| // Load a config bundle file, into presets and store the loaded presets into separate files
 | ||||
| // of the local configuration directory.
 | ||||
| size_t PresetBundle::load_configbundle(const std::string &path) | ||||
| { | ||||
|     // 1) Read the complete config file into a boost::property_tree.
 | ||||
|     namespace pt = boost::property_tree; | ||||
|     pt::ptree tree; | ||||
|     boost::nowide::ifstream ifs(path); | ||||
|     pt::read_ini(ifs, tree); | ||||
| 
 | ||||
|     // 2) Parse the property_tree, extract the active preset names and the profiles, save them into local config files.
 | ||||
|     std::vector<std::string> loaded_prints; | ||||
|     std::vector<std::string> loaded_filaments; | ||||
|     std::vector<std::string> loaded_printers; | ||||
|     std::string              active_print; | ||||
|     std::vector<std::string> active_filaments; | ||||
|     std::string              active_printer; | ||||
|     size_t                   presets_loaded = 0; | ||||
|     for (const auto §ion : tree) { | ||||
|         PresetCollection         *presets = nullptr; | ||||
|         std::vector<std::string> *loaded  = nullptr; | ||||
|         std::string               preset_name; | ||||
|         if (boost::starts_with(section.first, "print:")) { | ||||
|             presets = &prints; | ||||
|             loaded  = &loaded_prints; | ||||
|             preset_name = section.first.substr(6); | ||||
|         } else if (boost::starts_with(section.first, "filament:")) { | ||||
|             presets = &filaments; | ||||
|             loaded  = &loaded_filaments; | ||||
|             preset_name = section.first.substr(9); | ||||
|         } else if (boost::starts_with(section.first, "printer:")) { | ||||
|             presets = &printers; | ||||
|             loaded  = &loaded_printers; | ||||
|             preset_name = section.first.substr(8); | ||||
|         } else if (section.first == "presets") { | ||||
|             // Load the names of the active presets.
 | ||||
|             for (auto &kvp : section.second) { | ||||
|                 if (kvp.first == "print") { | ||||
|                     active_print = kvp.second.data(); | ||||
|                 } else if (boost::starts_with(kvp.first, "filament")) { | ||||
|                     int idx = 0; | ||||
|                     if (kvp.first == "filament" || sscanf(kvp.first.c_str(), "filament_%d", &idx) == 1) { | ||||
|                         if (int(active_filaments.size()) <= idx) | ||||
|                             active_filaments.resize(idx + 1, std::string()); | ||||
|                         active_filaments[idx] = kvp.second.data(); | ||||
|                     } | ||||
|                 } else if (kvp.first == "printer") { | ||||
|                     active_printer = kvp.second.data(); | ||||
|                 } | ||||
|             } | ||||
|         } else if (section.first == "settings") { | ||||
|             // Load the settings.
 | ||||
|             for (auto &kvp : section.second) { | ||||
|                 if (kvp.first == "autocenter") { | ||||
|                 } | ||||
|             } | ||||
|         } else | ||||
|             // Ignore an unknown section.
 | ||||
|             continue; | ||||
|         if (presets != nullptr) { | ||||
|             // Load the print, filament or printer preset.
 | ||||
|             DynamicPrintConfig config(presets->default_preset().config); | ||||
|             for (auto &kvp : section.second) | ||||
|                 config.set_deserialize(kvp.first, kvp.second.data()); | ||||
|             // Load the preset into the list of presets, save it to disk.
 | ||||
|             presets->load_preset(Slic3r::config_path(presets->name(), preset_name), preset_name, config, false).save(); | ||||
|             ++ presets_loaded; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // 3) Activate the presets.
 | ||||
|     if (! active_print.empty())  | ||||
|         prints.select_preset_by_name(active_print, true); | ||||
|     if (! active_printer.empty()) | ||||
|         printers.select_preset_by_name(active_printer, true); | ||||
|     // Activate the first filament preset.
 | ||||
|     if (! active_filaments.empty() && ! active_filaments.front().empty()) | ||||
|         filaments.select_preset_by_name(active_filaments.front(), true); | ||||
| 
 | ||||
|     this->update_multi_material_filament_presets(); | ||||
|     for (size_t i = 0; i < std::min(this->filament_presets.size(), active_filaments.size()); ++ i) | ||||
|         this->filament_presets[i] = filaments.first_visible().name; | ||||
|     return presets_loaded; | ||||
| } | ||||
| 
 | ||||
| void PresetBundle::update_multi_material_filament_presets() | ||||
| { | ||||
|     // Verify and select the filament presets.
 | ||||
|     auto   *nozzle_diameter = static_cast<const ConfigOptionFloats*>(printers.get_selected_preset().config.option("nozzle_diameter")); | ||||
|     size_t  num_extruders   = nozzle_diameter->values.size(); | ||||
|     // Verify validity of the current filament presets.
 | ||||
|     for (size_t i = 0; i < std::min(this->filament_presets.size(), num_extruders); ++ i) | ||||
|         this->filament_presets[i] = this->filaments.find_preset(this->filament_presets[i], true)->name; | ||||
|     // Append the rest of filament presets.
 | ||||
|     if (this->filament_presets.size() < num_extruders) | ||||
|         this->filament_presets.resize(num_extruders, this->filaments.first_visible().name); | ||||
| } | ||||
| 
 | ||||
| void PresetBundle::export_configbundle(const std::string &path, const DynamicPrintConfig &settings) | ||||
| { | ||||
|     boost::nowide::ofstream c; | ||||
|     c.open(path, std::ios::out | std::ios::trunc); | ||||
| 
 | ||||
|     // Put a comment at the first line including the time stamp and Slic3r version.
 | ||||
|     c << "# " << Slic3r::header_slic3r_generated() << std::endl; | ||||
| 
 | ||||
|     // Export the print, filament and printer profiles.
 | ||||
|     for (size_t i_group = 0; i_group < 3; ++ i_group) { | ||||
|         const PresetCollection &presets = (i_group == 0) ? this->prints : (i_group == 1) ? this->filaments : this->printers; | ||||
|         for (const Preset &preset : presets()) { | ||||
|             if (preset.is_default || preset.is_external) | ||||
|                 // Only export the common presets, not external files or the default preset.
 | ||||
|                 continue; | ||||
|             c << "[" << presets.name() << ":" << preset.name << "]" << std::endl; | ||||
|             for (const std::string &opt_key : preset.config.keys()) | ||||
|                 c << opt_key << " = " << preset.config.serialize(opt_key) << std::endl; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Export the names of the active presets.
 | ||||
|     c << "[presets]" << std::endl; | ||||
|     c << "print = " << this->prints.get_selected_preset().name << std::endl; | ||||
|     c << "printer = " << this->printers.get_selected_preset().name << std::endl; | ||||
|     for (size_t i = 0; i < this->filament_presets.size(); ++ i) { | ||||
|         char suffix[64]; | ||||
|         if (i > 0) | ||||
|             sprintf(suffix, "_%d", i); | ||||
|         else | ||||
|             suffix[0] = 0; | ||||
|         c << "filament" << suffix << " = " << this->filament_presets[i] << std::endl; | ||||
|     } | ||||
| 
 | ||||
|     // Export the following setting values from the provided setting repository.
 | ||||
|     static const char *settings_keys[] = { "autocenter" }; | ||||
|     c << "[presets]" << std::endl; | ||||
|     c << "print = " << this->prints.get_selected_preset().name << std::endl; | ||||
|     for (size_t i = 0; i < sizeof(settings_keys) / sizeof(settings_keys[0]); ++ i) | ||||
|         c << settings_keys[i] << " = " << settings.serialize(settings_keys[i]) << std::endl; | ||||
| 
 | ||||
|     c.close(); | ||||
| } | ||||
| 
 | ||||
| // Set the filament preset name. As the name could come from the UI selection box, 
 | ||||
| // an optional "(modified)" suffix will be removed from the filament name.
 | ||||
| void PresetBundle::set_filament_preset(size_t idx, const std::string &name) | ||||
| { | ||||
|     if (idx >= filament_presets.size()) | ||||
|         filament_presets.resize(idx + 1, filaments.default_preset().name); | ||||
|     filament_presets[idx] = remove_suffix_modified(name); | ||||
| } | ||||
| 
 | ||||
| static inline int hex_digit_to_int(const char c) | ||||
| { | ||||
|     return  | ||||
|         (c >= '0' && c <= '9') ? int(c - '0') :  | ||||
|         (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : | ||||
|         (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; | ||||
| } | ||||
| 
 | ||||
| static inline bool parse_color(const std::string &scolor, unsigned char *rgb_out) | ||||
| { | ||||
|     rgb_out[0] = rgb_out[1] = rgb_out[2] = 0; | ||||
|     const char        *c      = scolor.data() + 1; | ||||
|     if (scolor.size() != 7 || scolor.front() != '#') | ||||
|         return false; | ||||
|     for (size_t i = 0; i < 3; ++ i) { | ||||
|         int digit1 = hex_digit_to_int(*c ++); | ||||
|         int digit2 = hex_digit_to_int(*c ++); | ||||
|         if (digit1 == -1 || digit2 == -1) | ||||
|             return false; | ||||
|         rgb_out[i] = (unsigned char)(digit1 * 16 + digit2); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui) | ||||
| { | ||||
|     if (ui == nullptr) | ||||
|         return; | ||||
| 
 | ||||
|     unsigned char rgb[3]; | ||||
|     std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder); | ||||
|     if (! parse_color(extruder_color, rgb)) | ||||
|         // Extruder color is not defined.
 | ||||
|         extruder_color.clear(); | ||||
| 
 | ||||
|     // Fill in the list from scratch.
 | ||||
|     ui->Freeze(); | ||||
|     ui->Clear(); | ||||
|     for (size_t i = this->filaments().front().is_visible ? 0 : 1; i < this->filaments().size(); ++ i) { | ||||
|         const Preset &preset = this->filaments.preset(i); | ||||
| 		if (! preset.is_visible) | ||||
| 			continue; | ||||
|         bool selected = this->filament_presets[idx_extruder] == preset.name; | ||||
| 		// Assign an extruder color to the selected item if the extruder color is defined.
 | ||||
| 		std::string   filament_rgb = preset.config.opt_string("filament_colour", 0); | ||||
| 		std::string   extruder_rgb = (selected && !extruder_color.empty()) ? extruder_color : filament_rgb; | ||||
| 		wxBitmap     *bitmap	   = nullptr; | ||||
| 		if (filament_rgb == extruder_rgb) { | ||||
| 			auto it = m_mapColorToBitmap.find(filament_rgb); | ||||
| 			if (it == m_mapColorToBitmap.end()) { | ||||
| 				// Create the bitmap.
 | ||||
| 				parse_color(filament_rgb, rgb); | ||||
| 				wxImage image(24, 16); | ||||
| 				image.SetRGB(wxRect(0, 0, 24, 16), rgb[0], rgb[1], rgb[2]); | ||||
| 				m_mapColorToBitmap[filament_rgb] = bitmap = new wxBitmap(image); | ||||
| 			} else { | ||||
| 				bitmap = it->second; | ||||
| 			} | ||||
| 		} else { | ||||
| 			std::string bitmap_key = filament_rgb + extruder_rgb; | ||||
| 			auto it = m_mapColorToBitmap.find(bitmap_key); | ||||
| 			if (it == m_mapColorToBitmap.end()) { | ||||
| 				// Create the bitmap.
 | ||||
| 				wxImage image(24, 16); | ||||
| 				parse_color(extruder_rgb, rgb); | ||||
| 				image.SetRGB(wxRect(0, 0, 16, 16), rgb[0], rgb[1], rgb[2]); | ||||
| 				parse_color(filament_rgb, rgb); | ||||
| 				image.SetRGB(wxRect(16, 0, 8, 16), rgb[0], rgb[1], rgb[2]); | ||||
| 				m_mapColorToBitmap[filament_rgb] = bitmap = new wxBitmap(image); | ||||
| 			} else { | ||||
| 				bitmap = it->second; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), (bitmap == 0) ? wxNullBitmap : *bitmap); | ||||
|         if (selected) | ||||
|             ui->SetSelection(ui->GetCount() - 1); | ||||
|     } | ||||
|     ui->Thaw(); | ||||
| } | ||||
| 
 | ||||
| // Update the colors preview at the platter extruder combo box.
 | ||||
| void PresetBundle::update_platter_filament_ui_colors(unsigned int idx_extruder, wxBitmapComboBox *ui) | ||||
| { | ||||
| 	this->update_platter_filament_ui(idx_extruder, ui); | ||||
| 	return; | ||||
| 
 | ||||
|     unsigned char rgb[3]; | ||||
|     std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder); | ||||
|     if (! parse_color(extruder_color, rgb)) | ||||
|         // Extruder color is not defined.
 | ||||
|         extruder_color.clear(); | ||||
| 
 | ||||
|     ui->Freeze(); | ||||
|     for (unsigned int ui_id = 0; ui_id < ui->GetCount(); ++ ui_id) { | ||||
|         std::string   preset_name        = ui->GetString(ui_id).utf8_str().data(); | ||||
|         size_t        filament_preset_id = size_t(ui->GetClientData(ui_id)); | ||||
|         const Preset *filament_preset    = filaments.find_preset(preset_name, false); | ||||
|         assert(filament_preset != nullptr); | ||||
|         // Assign an extruder color to the selected item if the extruder color is defined.
 | ||||
|         std::string   filament_rgb       = filament_preset->config.opt_string("filament_colour", 0); | ||||
|         std::string   extruder_rgb       = (int(ui_id) == ui->GetSelection() && ! extruder_color.empty()) ? extruder_color : filament_rgb; | ||||
|         wxBitmap     *bitmap             = nullptr; | ||||
|         if (filament_rgb == extruder_rgb) { | ||||
|             auto it = m_mapColorToBitmap.find(filament_rgb); | ||||
|             if (it == m_mapColorToBitmap.end()) { | ||||
|                 // Create the bitmap.
 | ||||
|                 parse_color(filament_rgb, rgb); | ||||
|                 wxImage image(24, 16); | ||||
|                 image.SetRGB(wxRect(0, 0, 24, 16), rgb[0], rgb[1], rgb[2]); | ||||
|                 m_mapColorToBitmap[filament_rgb] = new wxBitmap(image); | ||||
|             } else { | ||||
|                 bitmap = it->second; | ||||
|             } | ||||
|         } else { | ||||
|             std::string bitmap_key = filament_rgb + extruder_rgb; | ||||
|             auto it = m_mapColorToBitmap.find(bitmap_key); | ||||
|             if (it == m_mapColorToBitmap.end()) { | ||||
|                 // Create the bitmap.
 | ||||
|                 wxImage image(24, 16); | ||||
|                 parse_color(extruder_rgb, rgb); | ||||
|                 image.SetRGB(wxRect(0, 0, 16, 16), rgb[0], rgb[1], rgb[2]); | ||||
|                 parse_color(filament_rgb, rgb); | ||||
|                 image.SetRGB(wxRect(16, 0, 8, 16), rgb[0], rgb[1], rgb[2]); | ||||
|                 m_mapColorToBitmap[filament_rgb] = new wxBitmap(image); | ||||
|             } else { | ||||
|                 bitmap = it->second; | ||||
|             } | ||||
|         } | ||||
|         ui->SetItemBitmap(ui_id, *bitmap); | ||||
|     } | ||||
|     ui->Thaw(); | ||||
| } | ||||
| 
 | ||||
| const std::vector<std::string>& PresetBundle::print_options() | ||||
| {     | ||||
|     const char *opts[] = {  | ||||
|         "layer_height", "first_layer_height", "perimeters", "spiral_vase", "top_solid_layers", "bottom_solid_layers",  | ||||
|         "extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs",  | ||||
|         "seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "external_fill_pattern",  | ||||
|         "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle",  | ||||
|         "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", "max_print_speed",  | ||||
|         "max_volumetric_speed", "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative",  | ||||
|         "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed",  | ||||
|         "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed", | ||||
|         "bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration",  | ||||
|         "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", | ||||
|         "min_skirt_length", "brim_width", "support_material", "support_material_threshold", "support_material_enforce_layers",  | ||||
|         "raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing",  | ||||
|         "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers",  | ||||
|         "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance",  | ||||
|         "support_material_buildplate_only", "dont_support_bridges", "notes", "complete_objects", "extruder_clearance_radius",  | ||||
|         "extruder_clearance_height", "gcode_comments", "output_filename_format", "post_process", "perimeter_extruder",  | ||||
|         "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",  | ||||
|         "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width",  | ||||
|         "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",  | ||||
|         "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects",  | ||||
|         "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", | ||||
|         "wipe_tower_width", "wipe_tower_per_color_wipe" | ||||
|     }; | ||||
|     static std::vector<std::string> s_opts; | ||||
|     if (s_opts.empty()) | ||||
|         s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); | ||||
|     return s_opts; | ||||
| } | ||||
| 
 | ||||
| const std::vector<std::string>& PresetBundle::filament_options() | ||||
| {     | ||||
|     const char *opts[] = { | ||||
|         "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed",  | ||||
|         "extrusion_multiplier", "filament_density", "filament_cost", "temperature", "first_layer_temperature", "bed_temperature",  | ||||
|         "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed", "bridge_fan_speed",  | ||||
|         "disable_fan_first_layers", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", "start_filament_gcode",  | ||||
|         "end_filament_gcode" | ||||
|     }; | ||||
|     static std::vector<std::string> s_opts; | ||||
|     if (s_opts.empty()) | ||||
|         s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); | ||||
|     return s_opts; | ||||
| } | ||||
| 
 | ||||
| const std::vector<std::string>& PresetBundle::printer_options() | ||||
| {     | ||||
|     const char *opts[] = { | ||||
|         "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed",  | ||||
|         "octoprint_host", "octoprint_apikey", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",  | ||||
|         "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",  | ||||
|         "nozzle_diameter", "extruder_offset", "retract_length", "retract_lift", "retract_speed", "deretract_speed",  | ||||
|         "retract_before_wipe", "retract_restart_extra", "retract_before_travel", "retract_layer_change", "wipe",  | ||||
|         "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour", "printer_notes" | ||||
|     }; | ||||
|     static std::vector<std::string> s_opts; | ||||
|     if (s_opts.empty()) | ||||
|         s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); | ||||
|     return s_opts; | ||||
| } | ||||
| 
 | ||||
| void PresetBundle::set_default_suppressed(bool default_suppressed) | ||||
| { | ||||
|     prints.set_default_suppressed(default_suppressed); | ||||
|     filaments.set_default_suppressed(default_suppressed); | ||||
|     printers.set_default_suppressed(default_suppressed); | ||||
| } | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
|  |  | |||
|  | @ -6,8 +6,6 @@ | |||
| #include "../../libslic3r/libslic3r.h" | ||||
| #include "../../libslic3r/PrintConfig.hpp" | ||||
| 
 | ||||
| #include "AppConfig.hpp" | ||||
| 
 | ||||
| class wxBitmap; | ||||
| class wxChoice; | ||||
| class wxBitmapComboBox; | ||||
|  | @ -74,6 +72,13 @@ public: | |||
| 
 | ||||
|     // Sort lexicographically by a preset name. The preset name shall be unique across a single PresetCollection.
 | ||||
|     bool                operator<(const Preset &other) const { return this->name < other.name; } | ||||
| 
 | ||||
| protected: | ||||
|     friend class        PresetCollection; | ||||
|     friend class        PresetBundle; | ||||
|     static void         load_config_file(DynamicPrintConfig &config, const std::string &path); | ||||
|     static const std::string& suffix_modified(); | ||||
|     static std::string  remove_suffix_modified(const std::string &name); | ||||
| }; | ||||
| 
 | ||||
| // Collections of presets of the same type (one of the Print, Filament or Printer type).
 | ||||
|  | @ -195,80 +200,6 @@ private: | |||
|     std::string             m_dir_path; | ||||
| }; | ||||
| 
 | ||||
| // Bundle of Print + Filament + Printer presets.
 | ||||
| class PresetBundle | ||||
| { | ||||
| public: | ||||
|     PresetBundle(); | ||||
|     ~PresetBundle(); | ||||
| 
 | ||||
|     void            setup_directories(); | ||||
| 
 | ||||
|     // Load ini files of all types (print, filament, printer) from the provided directory path.
 | ||||
|     void            load_presets(const std::string &dir_path); | ||||
| 
 | ||||
|     // Load selections (current print, current filaments, current printer) from config.ini
 | ||||
|     // This is done just once on application start up.
 | ||||
|     void            load_selections(const AppConfig &config); | ||||
|     // Export selections (current print, current filaments, current printer) into config.ini
 | ||||
|     void            export_selections(AppConfig &config); | ||||
| 
 | ||||
|     PresetCollection            prints; | ||||
|     PresetCollection            filaments; | ||||
|     PresetCollection            printers; | ||||
|     // Filament preset names for a multi-extruder or multi-material print.
 | ||||
|     // extruders.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size()
 | ||||
|     std::vector<std::string>    filament_presets; | ||||
| 
 | ||||
|     DynamicPrintConfig          full_config() const; | ||||
| 
 | ||||
|     // Load an external config file containing the print, filament and printer presets.
 | ||||
|     // Instead of a config file, a G-code may be loaded containing the full set of parameters.
 | ||||
|     // In the future the configuration will likely be read from an AMF file as well.
 | ||||
|     // If the file is loaded successfully, its print / filament / printer profiles will be activated.
 | ||||
|     void                        load_config_file(const std::string &path); | ||||
| 
 | ||||
|     // Load a config bundle file, into presets and store the loaded presets into separate files
 | ||||
|     // of the local configuration directory.
 | ||||
|     // Load settings into the provided settings instance.
 | ||||
|     // Activate the presets stored in the config bundle.
 | ||||
|     // Returns the number of presets loaded successfully.
 | ||||
|     size_t                      load_configbundle(const std::string &path); | ||||
| 
 | ||||
|     // Export a config bundle file containing all the presets and the names of the active presets.
 | ||||
|     void                        export_configbundle(const std::string &path, const DynamicPrintConfig &settings); | ||||
| 
 | ||||
|     // Update a filament selection combo box on the platter for an idx_extruder.
 | ||||
|     void                        update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui); | ||||
|     // Update the colors preview at the platter extruder combo box.
 | ||||
|     void                        update_platter_filament_ui_colors(unsigned int idx_extruder, wxBitmapComboBox *ui); | ||||
| 
 | ||||
|     static const std::vector<std::string>&  print_options(); | ||||
|     static const std::vector<std::string>&  filament_options(); | ||||
|     static const std::vector<std::string>&  printer_options(); | ||||
| 
 | ||||
|     // Enable / disable the "- default -" preset.
 | ||||
|     void                        set_default_suppressed(bool default_suppressed); | ||||
| 
 | ||||
|     // Set the filament preset name. As the name could come from the UI selection box, 
 | ||||
|     // an optional "(modified)" suffix will be removed from the filament name.
 | ||||
|     void                        set_filament_preset(size_t idx, const std::string &name); | ||||
| 
 | ||||
|     // Read out the number of extruders from an active printer preset,
 | ||||
|     // update size and content of filament_presets.
 | ||||
|     void                        update_multi_material_filament_presets(); | ||||
| 
 | ||||
| private: | ||||
|     bool                        load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible); | ||||
| 
 | ||||
|     // Indicator, that the preset is compatible with the selected printer.
 | ||||
|     wxBitmap                            *m_bitmapCompatible; | ||||
|     // Indicator, that the preset is NOT compatible with the selected printer.
 | ||||
|     wxBitmap                            *m_bitmapIncompatible; | ||||
|     // Caching color bitmaps for the 
 | ||||
|     std::map<std::string, wxBitmap*>     m_mapColorToBitmap; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
| 
 | ||||
| #endif /* slic3r_Preset_hpp_ */ | ||||
|  |  | |||
							
								
								
									
										603
									
								
								xs/src/slic3r/GUI/PresetBundle.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										603
									
								
								xs/src/slic3r/GUI/PresetBundle.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,603 @@ | |||
| #include "PresetBundle.hpp" | ||||
| 
 | ||||
| #include <fstream> | ||||
| #include <boost/filesystem.hpp> | ||||
| #include <boost/algorithm/string/predicate.hpp> | ||||
| 
 | ||||
| #include <boost/nowide/cenv.hpp> | ||||
| #include <boost/nowide/cstdio.hpp> | ||||
| #include <boost/nowide/fstream.hpp> | ||||
| #include <boost/property_tree/ini_parser.hpp> | ||||
| #include <boost/property_tree/ptree.hpp> | ||||
| #include <boost/locale.hpp> | ||||
| 
 | ||||
| #include <wx/image.h> | ||||
| #include <wx/choice.h> | ||||
| #include <wx/bmpcbox.h> | ||||
| #include <wx/wupdlock.h> | ||||
| 
 | ||||
| #include "../../libslic3r/Utils.hpp" | ||||
| 
 | ||||
| #include <assert.h> | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| PresetBundle::PresetBundle() : | ||||
|     prints(Preset::TYPE_PRINT, print_options()),  | ||||
|     filaments(Preset::TYPE_FILAMENT, filament_options()),  | ||||
|     printers(Preset::TYPE_PRINTER, printer_options()), | ||||
|     m_bitmapCompatible(new wxBitmap), | ||||
|     m_bitmapIncompatible(new wxBitmap) | ||||
| { | ||||
|     ::wxInitAllImageHandlers(); | ||||
| 
 | ||||
|     // Create the ID config keys, as they are not part of the Static print config classes.
 | ||||
|     this->prints.preset(0).config.opt_string("print_settings_id", true); | ||||
|     this->filaments.preset(0).config.opt_string("filament_settings_id", true); | ||||
|     this->printers.preset(0).config.opt_string("print_settings_id", true); | ||||
|     // Create the "compatible printers" keys, as they are not part of the Static print config classes.
 | ||||
|     this->filaments.preset(0).config.optptr("compatible_printers", true); | ||||
|     this->prints.preset(0).config.optptr("compatible_printers", true); | ||||
| 
 | ||||
|     this->prints   .load_bitmap_default("cog.png"); | ||||
|     this->filaments.load_bitmap_default("spool.png"); | ||||
|     this->printers .load_bitmap_default("printer_empty.png"); | ||||
| 
 | ||||
|     // FIXME select some icons indicating compatibility.
 | ||||
|     this->load_compatible_bitmaps("cog.png", "cog.png"); | ||||
| } | ||||
| 
 | ||||
| PresetBundle::~PresetBundle() | ||||
| { | ||||
| 	assert(m_bitmapCompatible != nullptr); | ||||
| 	assert(m_bitmapIncompatible != nullptr); | ||||
| 	delete m_bitmapCompatible; | ||||
| 	m_bitmapCompatible = nullptr; | ||||
|     delete m_bitmapIncompatible; | ||||
| 	m_bitmapIncompatible = nullptr; | ||||
|     for (std::pair<const std::string, wxBitmap*> &bitmap : m_mapColorToBitmap) | ||||
|         delete bitmap.second; | ||||
| } | ||||
| 
 | ||||
| void PresetBundle::setup_directories() | ||||
| { | ||||
|     boost::filesystem::path dir = boost::filesystem::canonical(Slic3r::data_dir()); | ||||
|     if (! boost::filesystem::is_directory(dir)) | ||||
|         throw std::runtime_error(std::string("datadir does not exist: ") + Slic3r::data_dir()); | ||||
|     std::initializer_list<const char*> names = { "print", "filament", "printer" }; | ||||
|     for (const char *name : names) { | ||||
|         boost::filesystem::path subdir = (dir / subdir).make_preferred(); | ||||
|         if (! boost::filesystem::is_directory(subdir) &&  | ||||
|             ! boost::filesystem::create_directory(subdir)) | ||||
|             throw std::runtime_error(std::string("Slic3r was unable to create its data directory at ") + subdir.string()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void PresetBundle::load_presets(const std::string &dir_path) | ||||
| { | ||||
|     this->prints   .load_presets(dir_path, "print"); | ||||
|     this->filaments.load_presets(dir_path, "filament"); | ||||
|     this->printers .load_presets(dir_path, "printer"); | ||||
|     this->update_multi_material_filament_presets(); | ||||
| } | ||||
| 
 | ||||
| // Load selections (current print, current filaments, current printer) from config.ini
 | ||||
| // This is done just once on application start up.
 | ||||
| void PresetBundle::load_selections(const AppConfig &config) | ||||
| { | ||||
|     prints.select_preset_by_name(config.get("presets", "print"), true); | ||||
|     filaments.select_preset_by_name(config.get("presets", "filament"), true); | ||||
|     this->set_filament_preset(0, filaments.get_selected_preset().name); | ||||
|     for (int i = 1; i < 1000; ++ i) { | ||||
|         char name[64]; | ||||
|         sprintf(name, "filament_%d", i); | ||||
|         if (! config.has("presets", name)) | ||||
|             break; | ||||
|         this->set_filament_preset(i, name); | ||||
|     } | ||||
|     printers.select_preset_by_name(config.get("presets", "printer"), true); | ||||
| } | ||||
| 
 | ||||
| // Export selections (current print, current filaments, current printer) into config.ini
 | ||||
| void PresetBundle::export_selections(AppConfig &config) | ||||
| { | ||||
|     config.set("presets", "print",    prints   .get_selected_preset().name); | ||||
|     config.set("presets", "filament", filaments.get_selected_preset().name); | ||||
|     for (int i = 1; i < 1000; ++ i) { | ||||
|         char name[64]; | ||||
|         sprintf(name, "filament_%d", i); | ||||
|         config.set("presets", name, filament_presets[i]); | ||||
|     } | ||||
|     config.set("presets", "printer",  printers .get_selected_preset().name); | ||||
| } | ||||
| 
 | ||||
| bool PresetBundle::load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible) | ||||
| { | ||||
|     bool loaded_compatible   = m_bitmapCompatible  ->LoadFile( | ||||
|         wxString::FromUTF8(Slic3r::var(path_bitmap_compatible).c_str()), wxBITMAP_TYPE_PNG); | ||||
|     bool loaded_incompatible = m_bitmapIncompatible->LoadFile( | ||||
|         wxString::FromUTF8(Slic3r::var(path_bitmap_incompatible).c_str()), wxBITMAP_TYPE_PNG); | ||||
|     if (loaded_compatible) { | ||||
|         prints   .set_bitmap_compatible(m_bitmapCompatible); | ||||
|         filaments.set_bitmap_compatible(m_bitmapCompatible); | ||||
|         printers .set_bitmap_compatible(m_bitmapCompatible); | ||||
|     } | ||||
|     if (loaded_incompatible) { | ||||
|         prints   .set_bitmap_compatible(m_bitmapIncompatible); | ||||
|         filaments.set_bitmap_compatible(m_bitmapIncompatible); | ||||
|         printers .set_bitmap_compatible(m_bitmapIncompatible);         | ||||
|     } | ||||
|     return loaded_compatible && loaded_incompatible; | ||||
| } | ||||
| 
 | ||||
| DynamicPrintConfig PresetBundle::full_config() const | ||||
| {     | ||||
|     DynamicPrintConfig out; | ||||
|     out.apply(FullPrintConfig()); | ||||
|     out.apply(this->prints.get_edited_preset().config); | ||||
|     out.apply(this->printers.get_edited_preset().config); | ||||
| 
 | ||||
|     auto   *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(out.option("nozzle_diameter")); | ||||
|     size_t  num_extruders   = nozzle_diameter->values.size(); | ||||
| 
 | ||||
|     if (num_extruders <= 1) { | ||||
|         out.apply(this->filaments.get_edited_preset().config); | ||||
|     } else { | ||||
|         // Retrieve filament presets and build a single config object for them.
 | ||||
|         // First collect the filament configurations based on the user selection of this->filament_presets.
 | ||||
|         std::vector<const DynamicPrintConfig*> filament_configs; | ||||
|         for (const std::string &filament_preset_name : this->filament_presets) | ||||
|             filament_configs.emplace_back(&this->filaments.find_preset(filament_preset_name, true)->config); | ||||
| 		while (filament_configs.size() < num_extruders) | ||||
|             filament_configs.emplace_back(&this->filaments.first_visible().config); | ||||
|         // Option values to set a ConfigOptionVector from.
 | ||||
|         std::vector<const ConfigOption*> filament_opts(num_extruders, nullptr); | ||||
|         // loop through options and apply them to the resulting config.
 | ||||
|         for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) { | ||||
|             // Get a destination option.
 | ||||
|             ConfigOption *opt_dst = out.option(key, false); | ||||
|             if (opt_dst->is_scalar()) { | ||||
|                 // Get an option, do not create if it does not exist.
 | ||||
|                 const ConfigOption *opt_src = filament_configs.front()->option(key); | ||||
|                 if (opt_src != nullptr) | ||||
|                     opt_dst->set(opt_src); | ||||
|             } else { | ||||
|                 // Setting a vector value from all filament_configs.
 | ||||
|                 for (size_t i = 0; i < filament_opts.size(); ++ i) | ||||
|                     filament_opts[i] = filament_configs[i]->option(key); | ||||
|                 static_cast<ConfigOptionVectorBase*>(opt_dst)->set(filament_opts); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     static const char *keys[] = { "perimeter", "infill", "solid_infill", "support_material", "support_material_interface" }; | ||||
|     for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++ i) { | ||||
|         std::string key = std::string(keys[i]) + "_extruder"; | ||||
|         auto *opt = dynamic_cast<ConfigOptionInt*>(out.option(key, false)); | ||||
|         assert(opt != nullptr); | ||||
|         opt->value = std::min<int>(opt->value, std::min<int>(0, int(num_extruders) - 1)); | ||||
|     } | ||||
| 
 | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| // Load an external config file containing the print, filament and printer presets.
 | ||||
| // Instead of a config file, a G-code may be loaded containing the full set of parameters.
 | ||||
| // In the future the configuration will likely be read from an AMF file as well.
 | ||||
| // If the file is loaded successfully, its print / filament / printer profiles will be activated.
 | ||||
| void PresetBundle::load_config_file(const std::string &path) | ||||
| { | ||||
|     // 1) Initialize a config from full defaults.
 | ||||
|     DynamicPrintConfig config; | ||||
|     config.apply(FullPrintConfig()); | ||||
| 
 | ||||
|     // 2) Try to load the config file.
 | ||||
|     // Throw exceptions with reasonable messages if something goes wrong.
 | ||||
|     Preset::load_config_file(config, path); | ||||
| 
 | ||||
|     // 3) Create a name from the file name.
 | ||||
|     // Keep the suffix (.ini, .gcode, .amf, .3mf etc) to differentiate it from the normal profiles.
 | ||||
|     std::string name = boost::filesystem::path(path).filename().string(); | ||||
| 
 | ||||
|     // 3) If the loading succeeded, split and load the config into print / filament / printer settings.
 | ||||
|     // First load the print and printer presets.
 | ||||
|     for (size_t i_group = 0; i_group < 2; ++ i_group) { | ||||
|         PresetCollection &presets = (i_group == 0) ? this->prints : this->printers; | ||||
|         presets.load_preset(path, name, config).is_external = true; | ||||
|     } | ||||
| 
 | ||||
|     // Now load the filaments. If there are multiple filament presets, split them and load them.
 | ||||
|     auto   *nozzle_diameter   = dynamic_cast<const ConfigOptionFloats*>(config.option("nozzle_diameter")); | ||||
|     auto   *filament_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("filament_diameter")); | ||||
|     size_t  num_extruders     = std::min(nozzle_diameter->values.size(), filament_diameter->values.size()); | ||||
|     if (num_extruders <= 1) { | ||||
|         this->filaments.load_preset(path, name, config).is_external = true; | ||||
|         this->filament_presets.clear(); | ||||
|         this->filament_presets.emplace_back(name); | ||||
|     } else { | ||||
|         // Split the filament presets, load each of them separately.
 | ||||
|         std::vector<DynamicPrintConfig> configs(num_extruders, this->filaments.default_preset().config); | ||||
|         // loop through options and scatter them into configs.
 | ||||
|         for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) { | ||||
|             const ConfigOption *other_opt = config.option(key, false); | ||||
|             if (other_opt == nullptr) | ||||
|                 continue; | ||||
|             if (other_opt->is_scalar()) { | ||||
|                 for (size_t i = 0; i < configs.size(); ++ i) | ||||
|                     configs[i].option(key, false)->set(other_opt); | ||||
|             } else { | ||||
|                 for (size_t i = 0; i < configs.size(); ++ i) | ||||
|                     static_cast<ConfigOptionVectorBase*>(configs[i].option(key, false))->set_at(other_opt, 0, i); | ||||
|             } | ||||
|         } | ||||
|         // Load the configs into this->filaments and make them active.
 | ||||
|         filament_presets.clear(); | ||||
|         for (size_t i = 0; i < configs.size(); ++ i) { | ||||
|             char suffix[64]; | ||||
|             if (i == 0) | ||||
|                 suffix[0] = 0; | ||||
|             else | ||||
|                 sprintf(suffix, " (%d)", i); | ||||
|             // Load all filament presets, but only select the first one in the preset dialog.
 | ||||
|             this->filaments.load_preset(path, name + suffix, configs[i], i == 0).is_external = true; | ||||
|             filament_presets.emplace_back(name + suffix); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Load a config bundle file, into presets and store the loaded presets into separate files
 | ||||
| // of the local configuration directory.
 | ||||
| size_t PresetBundle::load_configbundle(const std::string &path) | ||||
| { | ||||
|     // 1) Read the complete config file into a boost::property_tree.
 | ||||
|     namespace pt = boost::property_tree; | ||||
|     pt::ptree tree; | ||||
|     boost::nowide::ifstream ifs(path); | ||||
|     pt::read_ini(ifs, tree); | ||||
| 
 | ||||
|     // 2) Parse the property_tree, extract the active preset names and the profiles, save them into local config files.
 | ||||
|     std::vector<std::string> loaded_prints; | ||||
|     std::vector<std::string> loaded_filaments; | ||||
|     std::vector<std::string> loaded_printers; | ||||
|     std::string              active_print; | ||||
|     std::vector<std::string> active_filaments; | ||||
|     std::string              active_printer; | ||||
|     size_t                   presets_loaded = 0; | ||||
|     for (const auto §ion : tree) { | ||||
|         PresetCollection         *presets = nullptr; | ||||
|         std::vector<std::string> *loaded  = nullptr; | ||||
|         std::string               preset_name; | ||||
|         if (boost::starts_with(section.first, "print:")) { | ||||
|             presets = &prints; | ||||
|             loaded  = &loaded_prints; | ||||
|             preset_name = section.first.substr(6); | ||||
|         } else if (boost::starts_with(section.first, "filament:")) { | ||||
|             presets = &filaments; | ||||
|             loaded  = &loaded_filaments; | ||||
|             preset_name = section.first.substr(9); | ||||
|         } else if (boost::starts_with(section.first, "printer:")) { | ||||
|             presets = &printers; | ||||
|             loaded  = &loaded_printers; | ||||
|             preset_name = section.first.substr(8); | ||||
|         } else if (section.first == "presets") { | ||||
|             // Load the names of the active presets.
 | ||||
|             for (auto &kvp : section.second) { | ||||
|                 if (kvp.first == "print") { | ||||
|                     active_print = kvp.second.data(); | ||||
|                 } else if (boost::starts_with(kvp.first, "filament")) { | ||||
|                     int idx = 0; | ||||
|                     if (kvp.first == "filament" || sscanf(kvp.first.c_str(), "filament_%d", &idx) == 1) { | ||||
|                         if (int(active_filaments.size()) <= idx) | ||||
|                             active_filaments.resize(idx + 1, std::string()); | ||||
|                         active_filaments[idx] = kvp.second.data(); | ||||
|                     } | ||||
|                 } else if (kvp.first == "printer") { | ||||
|                     active_printer = kvp.second.data(); | ||||
|                 } | ||||
|             } | ||||
|         } else if (section.first == "settings") { | ||||
|             // Load the settings.
 | ||||
|             for (auto &kvp : section.second) { | ||||
|                 if (kvp.first == "autocenter") { | ||||
|                 } | ||||
|             } | ||||
|         } else | ||||
|             // Ignore an unknown section.
 | ||||
|             continue; | ||||
|         if (presets != nullptr) { | ||||
|             // Load the print, filament or printer preset.
 | ||||
|             DynamicPrintConfig config(presets->default_preset().config); | ||||
|             for (auto &kvp : section.second) | ||||
|                 config.set_deserialize(kvp.first, kvp.second.data()); | ||||
|             // Load the preset into the list of presets, save it to disk.
 | ||||
|             presets->load_preset(Slic3r::config_path(presets->name(), preset_name), preset_name, config, false).save(); | ||||
|             ++ presets_loaded; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // 3) Activate the presets.
 | ||||
|     if (! active_print.empty())  | ||||
|         prints.select_preset_by_name(active_print, true); | ||||
|     if (! active_printer.empty()) | ||||
|         printers.select_preset_by_name(active_printer, true); | ||||
|     // Activate the first filament preset.
 | ||||
|     if (! active_filaments.empty() && ! active_filaments.front().empty()) | ||||
|         filaments.select_preset_by_name(active_filaments.front(), true); | ||||
| 
 | ||||
|     this->update_multi_material_filament_presets(); | ||||
|     for (size_t i = 0; i < std::min(this->filament_presets.size(), active_filaments.size()); ++ i) | ||||
|         this->filament_presets[i] = filaments.first_visible().name; | ||||
|     return presets_loaded; | ||||
| } | ||||
| 
 | ||||
| void PresetBundle::update_multi_material_filament_presets() | ||||
| { | ||||
|     // Verify and select the filament presets.
 | ||||
|     auto   *nozzle_diameter = static_cast<const ConfigOptionFloats*>(printers.get_selected_preset().config.option("nozzle_diameter")); | ||||
|     size_t  num_extruders   = nozzle_diameter->values.size(); | ||||
|     // Verify validity of the current filament presets.
 | ||||
|     for (size_t i = 0; i < std::min(this->filament_presets.size(), num_extruders); ++ i) | ||||
|         this->filament_presets[i] = this->filaments.find_preset(this->filament_presets[i], true)->name; | ||||
|     // Append the rest of filament presets.
 | ||||
|     if (this->filament_presets.size() < num_extruders) | ||||
|         this->filament_presets.resize(num_extruders, this->filaments.first_visible().name); | ||||
| } | ||||
| 
 | ||||
| void PresetBundle::export_configbundle(const std::string &path, const DynamicPrintConfig &settings) | ||||
| { | ||||
|     boost::nowide::ofstream c; | ||||
|     c.open(path, std::ios::out | std::ios::trunc); | ||||
| 
 | ||||
|     // Put a comment at the first line including the time stamp and Slic3r version.
 | ||||
|     c << "# " << Slic3r::header_slic3r_generated() << std::endl; | ||||
| 
 | ||||
|     // Export the print, filament and printer profiles.
 | ||||
|     for (size_t i_group = 0; i_group < 3; ++ i_group) { | ||||
|         const PresetCollection &presets = (i_group == 0) ? this->prints : (i_group == 1) ? this->filaments : this->printers; | ||||
|         for (const Preset &preset : presets()) { | ||||
|             if (preset.is_default || preset.is_external) | ||||
|                 // Only export the common presets, not external files or the default preset.
 | ||||
|                 continue; | ||||
|             c << "[" << presets.name() << ":" << preset.name << "]" << std::endl; | ||||
|             for (const std::string &opt_key : preset.config.keys()) | ||||
|                 c << opt_key << " = " << preset.config.serialize(opt_key) << std::endl; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Export the names of the active presets.
 | ||||
|     c << "[presets]" << std::endl; | ||||
|     c << "print = " << this->prints.get_selected_preset().name << std::endl; | ||||
|     c << "printer = " << this->printers.get_selected_preset().name << std::endl; | ||||
|     for (size_t i = 0; i < this->filament_presets.size(); ++ i) { | ||||
|         char suffix[64]; | ||||
|         if (i > 0) | ||||
|             sprintf(suffix, "_%d", i); | ||||
|         else | ||||
|             suffix[0] = 0; | ||||
|         c << "filament" << suffix << " = " << this->filament_presets[i] << std::endl; | ||||
|     } | ||||
| 
 | ||||
|     // Export the following setting values from the provided setting repository.
 | ||||
|     static const char *settings_keys[] = { "autocenter" }; | ||||
|     c << "[presets]" << std::endl; | ||||
|     c << "print = " << this->prints.get_selected_preset().name << std::endl; | ||||
|     for (size_t i = 0; i < sizeof(settings_keys) / sizeof(settings_keys[0]); ++ i) | ||||
|         c << settings_keys[i] << " = " << settings.serialize(settings_keys[i]) << std::endl; | ||||
| 
 | ||||
|     c.close(); | ||||
| } | ||||
| 
 | ||||
| // Set the filament preset name. As the name could come from the UI selection box, 
 | ||||
| // an optional "(modified)" suffix will be removed from the filament name.
 | ||||
| void PresetBundle::set_filament_preset(size_t idx, const std::string &name) | ||||
| { | ||||
|     if (idx >= filament_presets.size()) | ||||
|         filament_presets.resize(idx + 1, filaments.default_preset().name); | ||||
|     filament_presets[idx] = Preset::remove_suffix_modified(name); | ||||
| } | ||||
| 
 | ||||
| static inline int hex_digit_to_int(const char c) | ||||
| { | ||||
|     return  | ||||
|         (c >= '0' && c <= '9') ? int(c - '0') :  | ||||
|         (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : | ||||
|         (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; | ||||
| } | ||||
| 
 | ||||
| static inline bool parse_color(const std::string &scolor, unsigned char *rgb_out) | ||||
| { | ||||
|     rgb_out[0] = rgb_out[1] = rgb_out[2] = 0; | ||||
|     const char        *c      = scolor.data() + 1; | ||||
|     if (scolor.size() != 7 || scolor.front() != '#') | ||||
|         return false; | ||||
|     for (size_t i = 0; i < 3; ++ i) { | ||||
|         int digit1 = hex_digit_to_int(*c ++); | ||||
|         int digit2 = hex_digit_to_int(*c ++); | ||||
|         if (digit1 == -1 || digit2 == -1) | ||||
|             return false; | ||||
|         rgb_out[i] = (unsigned char)(digit1 * 16 + digit2); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui) | ||||
| { | ||||
|     if (ui == nullptr) | ||||
|         return; | ||||
| 
 | ||||
|     unsigned char rgb[3]; | ||||
|     std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder); | ||||
|     if (! parse_color(extruder_color, rgb)) | ||||
|         // Extruder color is not defined.
 | ||||
|         extruder_color.clear(); | ||||
| 
 | ||||
|     // Fill in the list from scratch.
 | ||||
|     ui->Freeze(); | ||||
|     ui->Clear(); | ||||
|     for (size_t i = this->filaments().front().is_visible ? 0 : 1; i < this->filaments().size(); ++ i) { | ||||
|         const Preset &preset = this->filaments.preset(i); | ||||
| 		if (! preset.is_visible) | ||||
| 			continue; | ||||
|         bool selected = this->filament_presets[idx_extruder] == preset.name; | ||||
| 		// Assign an extruder color to the selected item if the extruder color is defined.
 | ||||
| 		std::string   filament_rgb = preset.config.opt_string("filament_colour", 0); | ||||
| 		std::string   extruder_rgb = (selected && !extruder_color.empty()) ? extruder_color : filament_rgb; | ||||
| 		wxBitmap     *bitmap	   = nullptr; | ||||
| 		if (filament_rgb == extruder_rgb) { | ||||
| 			auto it = m_mapColorToBitmap.find(filament_rgb); | ||||
| 			if (it == m_mapColorToBitmap.end()) { | ||||
| 				// Create the bitmap.
 | ||||
| 				parse_color(filament_rgb, rgb); | ||||
| 				wxImage image(24, 16); | ||||
| 				image.SetRGB(wxRect(0, 0, 24, 16), rgb[0], rgb[1], rgb[2]); | ||||
| 				m_mapColorToBitmap[filament_rgb] = bitmap = new wxBitmap(image); | ||||
| 			} else { | ||||
| 				bitmap = it->second; | ||||
| 			} | ||||
| 		} else { | ||||
| 			std::string bitmap_key = filament_rgb + extruder_rgb; | ||||
| 			auto it = m_mapColorToBitmap.find(bitmap_key); | ||||
| 			if (it == m_mapColorToBitmap.end()) { | ||||
| 				// Create the bitmap.
 | ||||
| 				wxImage image(24, 16); | ||||
| 				parse_color(extruder_rgb, rgb); | ||||
| 				image.SetRGB(wxRect(0, 0, 16, 16), rgb[0], rgb[1], rgb[2]); | ||||
| 				parse_color(filament_rgb, rgb); | ||||
| 				image.SetRGB(wxRect(16, 0, 8, 16), rgb[0], rgb[1], rgb[2]); | ||||
| 				m_mapColorToBitmap[filament_rgb] = bitmap = new wxBitmap(image); | ||||
| 			} else { | ||||
| 				bitmap = it->second; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), (bitmap == 0) ? wxNullBitmap : *bitmap); | ||||
|         if (selected) | ||||
|             ui->SetSelection(ui->GetCount() - 1); | ||||
|     } | ||||
|     ui->Thaw(); | ||||
| } | ||||
| 
 | ||||
| // Update the colors preview at the platter extruder combo box.
 | ||||
| void PresetBundle::update_platter_filament_ui_colors(unsigned int idx_extruder, wxBitmapComboBox *ui) | ||||
| { | ||||
| 	this->update_platter_filament_ui(idx_extruder, ui); | ||||
| 	return; | ||||
| 
 | ||||
|     unsigned char rgb[3]; | ||||
|     std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder); | ||||
|     if (! parse_color(extruder_color, rgb)) | ||||
|         // Extruder color is not defined.
 | ||||
|         extruder_color.clear(); | ||||
| 
 | ||||
|     ui->Freeze(); | ||||
|     for (unsigned int ui_id = 0; ui_id < ui->GetCount(); ++ ui_id) { | ||||
|         std::string   preset_name        = ui->GetString(ui_id).utf8_str().data(); | ||||
|         size_t        filament_preset_id = size_t(ui->GetClientData(ui_id)); | ||||
|         const Preset *filament_preset    = filaments.find_preset(preset_name, false); | ||||
|         assert(filament_preset != nullptr); | ||||
|         // Assign an extruder color to the selected item if the extruder color is defined.
 | ||||
|         std::string   filament_rgb       = filament_preset->config.opt_string("filament_colour", 0); | ||||
|         std::string   extruder_rgb       = (int(ui_id) == ui->GetSelection() && ! extruder_color.empty()) ? extruder_color : filament_rgb; | ||||
|         wxBitmap     *bitmap             = nullptr; | ||||
|         if (filament_rgb == extruder_rgb) { | ||||
|             auto it = m_mapColorToBitmap.find(filament_rgb); | ||||
|             if (it == m_mapColorToBitmap.end()) { | ||||
|                 // Create the bitmap.
 | ||||
|                 parse_color(filament_rgb, rgb); | ||||
|                 wxImage image(24, 16); | ||||
|                 image.SetRGB(wxRect(0, 0, 24, 16), rgb[0], rgb[1], rgb[2]); | ||||
|                 m_mapColorToBitmap[filament_rgb] = new wxBitmap(image); | ||||
|             } else { | ||||
|                 bitmap = it->second; | ||||
|             } | ||||
|         } else { | ||||
|             std::string bitmap_key = filament_rgb + extruder_rgb; | ||||
|             auto it = m_mapColorToBitmap.find(bitmap_key); | ||||
|             if (it == m_mapColorToBitmap.end()) { | ||||
|                 // Create the bitmap.
 | ||||
|                 wxImage image(24, 16); | ||||
|                 parse_color(extruder_rgb, rgb); | ||||
|                 image.SetRGB(wxRect(0, 0, 16, 16), rgb[0], rgb[1], rgb[2]); | ||||
|                 parse_color(filament_rgb, rgb); | ||||
|                 image.SetRGB(wxRect(16, 0, 8, 16), rgb[0], rgb[1], rgb[2]); | ||||
|                 m_mapColorToBitmap[filament_rgb] = new wxBitmap(image); | ||||
|             } else { | ||||
|                 bitmap = it->second; | ||||
|             } | ||||
|         } | ||||
|         ui->SetItemBitmap(ui_id, *bitmap); | ||||
|     } | ||||
|     ui->Thaw(); | ||||
| } | ||||
| 
 | ||||
| const std::vector<std::string>& PresetBundle::print_options() | ||||
| {     | ||||
|     const char *opts[] = {  | ||||
|         "layer_height", "first_layer_height", "perimeters", "spiral_vase", "top_solid_layers", "bottom_solid_layers",  | ||||
|         "extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs",  | ||||
|         "seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "external_fill_pattern",  | ||||
|         "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle",  | ||||
|         "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", "max_print_speed",  | ||||
|         "max_volumetric_speed", "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative",  | ||||
|         "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed",  | ||||
|         "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed", | ||||
|         "bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration",  | ||||
|         "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", | ||||
|         "min_skirt_length", "brim_width", "support_material", "support_material_threshold", "support_material_enforce_layers",  | ||||
|         "raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing",  | ||||
|         "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers",  | ||||
|         "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance",  | ||||
|         "support_material_buildplate_only", "dont_support_bridges", "notes", "complete_objects", "extruder_clearance_radius",  | ||||
|         "extruder_clearance_height", "gcode_comments", "output_filename_format", "post_process", "perimeter_extruder",  | ||||
|         "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",  | ||||
|         "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width",  | ||||
|         "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",  | ||||
|         "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects",  | ||||
|         "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", | ||||
|         "wipe_tower_width", "wipe_tower_per_color_wipe" | ||||
|     }; | ||||
|     static std::vector<std::string> s_opts; | ||||
|     if (s_opts.empty()) | ||||
|         s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); | ||||
|     return s_opts; | ||||
| } | ||||
| 
 | ||||
| const std::vector<std::string>& PresetBundle::filament_options() | ||||
| {     | ||||
|     const char *opts[] = { | ||||
|         "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed",  | ||||
|         "extrusion_multiplier", "filament_density", "filament_cost", "temperature", "first_layer_temperature", "bed_temperature",  | ||||
|         "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed", "bridge_fan_speed",  | ||||
|         "disable_fan_first_layers", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", "start_filament_gcode",  | ||||
|         "end_filament_gcode" | ||||
|     }; | ||||
|     static std::vector<std::string> s_opts; | ||||
|     if (s_opts.empty()) | ||||
|         s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); | ||||
|     return s_opts; | ||||
| } | ||||
| 
 | ||||
| const std::vector<std::string>& PresetBundle::printer_options() | ||||
| {     | ||||
|     const char *opts[] = { | ||||
|         "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed",  | ||||
|         "octoprint_host", "octoprint_apikey", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",  | ||||
|         "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",  | ||||
|         "nozzle_diameter", "extruder_offset", "retract_length", "retract_lift", "retract_speed", "deretract_speed",  | ||||
|         "retract_before_wipe", "retract_restart_extra", "retract_before_travel", "retract_layer_change", "wipe",  | ||||
|         "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour", "printer_notes" | ||||
|     }; | ||||
|     static std::vector<std::string> s_opts; | ||||
|     if (s_opts.empty()) | ||||
|         s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); | ||||
|     return s_opts; | ||||
| } | ||||
| 
 | ||||
| void PresetBundle::set_default_suppressed(bool default_suppressed) | ||||
| { | ||||
|     prints.set_default_suppressed(default_suppressed); | ||||
|     filaments.set_default_suppressed(default_suppressed); | ||||
|     printers.set_default_suppressed(default_suppressed); | ||||
| } | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
							
								
								
									
										85
									
								
								xs/src/slic3r/GUI/PresetBundle.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								xs/src/slic3r/GUI/PresetBundle.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,85 @@ | |||
| #ifndef slic3r_PresetBundle_hpp_ | ||||
| #define slic3r_PresetBundle_hpp_ | ||||
| 
 | ||||
| #include "AppConfig.hpp" | ||||
| #include "Preset.hpp" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| // Bundle of Print + Filament + Printer presets.
 | ||||
| class PresetBundle | ||||
| { | ||||
| public: | ||||
|     PresetBundle(); | ||||
|     ~PresetBundle(); | ||||
| 
 | ||||
|     void            setup_directories(); | ||||
| 
 | ||||
|     // Load ini files of all types (print, filament, printer) from the provided directory path.
 | ||||
|     void            load_presets(const std::string &dir_path); | ||||
| 
 | ||||
|     // Load selections (current print, current filaments, current printer) from config.ini
 | ||||
|     // This is done just once on application start up.
 | ||||
|     void            load_selections(const AppConfig &config); | ||||
|     // Export selections (current print, current filaments, current printer) into config.ini
 | ||||
|     void            export_selections(AppConfig &config); | ||||
| 
 | ||||
|     PresetCollection            prints; | ||||
|     PresetCollection            filaments; | ||||
|     PresetCollection            printers; | ||||
|     // Filament preset names for a multi-extruder or multi-material print.
 | ||||
|     // extruders.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size()
 | ||||
|     std::vector<std::string>    filament_presets; | ||||
| 
 | ||||
|     DynamicPrintConfig          full_config() const; | ||||
| 
 | ||||
|     // Load an external config file containing the print, filament and printer presets.
 | ||||
|     // Instead of a config file, a G-code may be loaded containing the full set of parameters.
 | ||||
|     // In the future the configuration will likely be read from an AMF file as well.
 | ||||
|     // If the file is loaded successfully, its print / filament / printer profiles will be activated.
 | ||||
|     void                        load_config_file(const std::string &path); | ||||
| 
 | ||||
|     // Load a config bundle file, into presets and store the loaded presets into separate files
 | ||||
|     // of the local configuration directory.
 | ||||
|     // Load settings into the provided settings instance.
 | ||||
|     // Activate the presets stored in the config bundle.
 | ||||
|     // Returns the number of presets loaded successfully.
 | ||||
|     size_t                      load_configbundle(const std::string &path); | ||||
| 
 | ||||
|     // Export a config bundle file containing all the presets and the names of the active presets.
 | ||||
|     void                        export_configbundle(const std::string &path, const DynamicPrintConfig &settings); | ||||
| 
 | ||||
|     // Update a filament selection combo box on the platter for an idx_extruder.
 | ||||
|     void                        update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui); | ||||
|     // Update the colors preview at the platter extruder combo box.
 | ||||
|     void                        update_platter_filament_ui_colors(unsigned int idx_extruder, wxBitmapComboBox *ui); | ||||
| 
 | ||||
|     static const std::vector<std::string>&  print_options(); | ||||
|     static const std::vector<std::string>&  filament_options(); | ||||
|     static const std::vector<std::string>&  printer_options(); | ||||
| 
 | ||||
|     // Enable / disable the "- default -" preset.
 | ||||
|     void                        set_default_suppressed(bool default_suppressed); | ||||
| 
 | ||||
|     // Set the filament preset name. As the name could come from the UI selection box, 
 | ||||
|     // an optional "(modified)" suffix will be removed from the filament name.
 | ||||
|     void                        set_filament_preset(size_t idx, const std::string &name); | ||||
| 
 | ||||
|     // Read out the number of extruders from an active printer preset,
 | ||||
|     // update size and content of filament_presets.
 | ||||
|     void                        update_multi_material_filament_presets(); | ||||
| 
 | ||||
| private: | ||||
|     bool                        load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible); | ||||
| 
 | ||||
|     // Indicator, that the preset is compatible with the selected printer.
 | ||||
|     wxBitmap                            *m_bitmapCompatible; | ||||
|     // Indicator, that the preset is NOT compatible with the selected printer.
 | ||||
|     wxBitmap                            *m_bitmapIncompatible; | ||||
|     // Caching color bitmaps for the 
 | ||||
|     std::map<std::string, wxBitmap*>     m_mapColorToBitmap; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
| 
 | ||||
| #endif /* slic3r_PresetBundle_hpp_ */ | ||||
							
								
								
									
										29
									
								
								xs/xsp/GUI_AppConfig.xsp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								xs/xsp/GUI_AppConfig.xsp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | |||
| 
 | ||||
| %module{Slic3r::XS}; | ||||
| 
 | ||||
| %{ | ||||
| #include <xsinit.h> | ||||
| #include "slic3r/GUI/AppConfig.hpp" | ||||
| %} | ||||
| 
 | ||||
| %name{Slic3r::GUI::AppConfig} class AppConfig { | ||||
|     AppConfig(); | ||||
|     ~AppConfig(); | ||||
| 
 | ||||
|     void 					    reset(); | ||||
|     void 						set_defaults(); | ||||
| 
 | ||||
|     void                        load(); | ||||
|     void                        save(); | ||||
|     bool						exists(); | ||||
| 
 | ||||
|     std::string                 get(char *name); | ||||
|     void                        set(char *name, char *value); | ||||
|     bool						has(char *section); | ||||
| 
 | ||||
|     std::string                 get_last_dir(); | ||||
|     void                        update_config_dir(char *dir); | ||||
|     void                        update_skein_dir(char *dir); | ||||
|     std::string                 get_last_output_dir(const char *alt = ""); | ||||
|     void                        update_last_output_dir(char *dir); | ||||
| }; | ||||
|  | @ -3,6 +3,7 @@ | |||
| %{ | ||||
| #include <xsinit.h> | ||||
| #include "slic3r/GUI/Preset.hpp" | ||||
| #include "slic3r/GUI/PresetBundle.hpp" | ||||
| %} | ||||
| 
 | ||||
| %name{Slic3r::GUI::Preset} class Preset { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 bubnikv
						bubnikv