orcaslicer/src/libslic3r/Preset.cpp
Ocraftyone 4d14ee15ce
Thumbnail Formats Option Ported from PrusaSlicer and add BIQU/BTT format (#2405)
* Add needed src files and update CMake files

* Implementation of GCodeThumbnailsFormat for PNG, JPG, and QOI complete

* Implement BIQU (Big Tree Tech) Thumbnail Format

* have GCodeProcessor.post_process pass through original line end characters

* fix biqu thumbnail output
use \r\n for new lines in the biqu thumbnail portion. the firmware requires these end characters to function properly.
update names of variables and add comments to be more descriptive
replace modified Qt pixel algorithm with much simpler algorithm from BTT TFT firmware

* rename BiQU to BTT_TFT for better clarity

* remove underscore from GUI option

---------

Co-authored-by: SoftFever <103989404+SoftFever@users.noreply.github.com>
2023-10-17 20:00:38 +08:00

3219 lines
142 KiB
C++

#include <cassert>
#include "Config.hpp"
#include "Exception.hpp"
#include "Preset.hpp"
#include "PresetBundle.hpp"
#include "AppConfig.hpp"
#ifdef _MSC_VER
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#endif /* _MSC_VER */
// instead of #include "slic3r/GUI/I18N.hpp" :
#ifndef L
// !!! If you needed to translate some string,
// !!! please use _L(string)
// !!! _() - is a standard wxWidgets macro to translate
// !!! L() is used only for marking localizable string
// !!! It will be used in "xgettext" to create a Locating Message Catalog.
#define L(s) s
#endif /* L */
#include <algorithm>
#include <fstream>
#include <stdexcept>
#include <unordered_map>
#include <boost/format.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp>
//BBS: add regex
#include <boost/algorithm/string/regex.hpp>
#include <boost/nowide/cenv.hpp>
#include <boost/nowide/convert.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 <boost/log/trivial.hpp>
#include "libslic3r.h"
#include "Utils.hpp"
#include "Time.hpp"
#include "PlaceholderParser.hpp"
using boost::property_tree::ptree;
namespace Slic3r {
//BBS: add a function to load the version from xxx.json
Semver get_version_from_json(std::string file_path)
{
try {
boost::nowide::ifstream ifs(file_path);
json j;
ifs >> j;
std::string version_str = j.at(BBL_JSON_KEY_VERSION);
auto config_version = Semver::parse(version_str);
if (! config_version) {
return Semver();
} else {
return *config_version;
}
}
catch(nlohmann::detail::parse_error &err) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse "<<file_path<<" got a nlohmann::detail::parse_error, reason = " << err.what();
return Semver();
//throw ConfigurationError(format("Failed loading configuration file \"%1%\": %2%", file_path, err.what()));
}
}
//BBS: add a function to load the key-values from xxx.json
int get_values_from_json(std::string file_path, std::vector<std::string>& keys, std::map<std::string, std::string>& key_values)
{
try {
boost::nowide::ifstream ifs(file_path);
json j;
ifs >> j;
for (int i=0; i < keys.size(); i++)
{
if (j.contains(keys[i])) {
std::string value = j.at(keys[i]);
key_values.emplace(keys[i], value);
}
}
}
catch(nlohmann::detail::parse_error &err) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse "<<file_path<<" got a nlohmann::detail::parse_error, reason = " << err.what();
//throw ConfigurationError(format("Failed loading json file \"%1%\": %2%", file_path, err.what()));
return 0;
}
return key_values.size();
}
ConfigFileType guess_config_file_type(const ptree &tree)
{
size_t app_config = 0;
size_t bundle = 0;
size_t config = 0;
for (const ptree::value_type &v : tree) {
if (v.second.empty()) {
if (
#ifdef SUPPORT_BACKGROUND_PROCESSING
v.first == "background_processing" ||
#endif
v.first == "last_export_path")
++ app_config;
else if (v.first == "nozzle_diameter" ||
v.first == "filament_diameter")
++ config;
} else if (boost::algorithm::starts_with(v.first, "print:") ||
boost::algorithm::starts_with(v.first, "filament:") ||
boost::algorithm::starts_with(v.first, "printer:") ||
v.first == "settings")
++ bundle;
else if (v.first == "presets") {
++ app_config;
++ bundle;
} else if (v.first == "recent") {
for (auto &kvp : v.second)
if (kvp.first == "settings_folder" || kvp.first == "last_opened_folder")
++ app_config;
}
}
return (app_config > bundle && app_config > config) ? CONFIG_FILE_TYPE_APP_CONFIG :
(bundle > config) ? CONFIG_FILE_TYPE_CONFIG_BUNDLE : CONFIG_FILE_TYPE_CONFIG;
}
VendorProfile VendorProfile::from_ini(const boost::filesystem::path &path, bool load_all)
{
ptree tree;
boost::filesystem::ifstream ifs(path);
boost::property_tree::read_ini(ifs, tree);
return VendorProfile::from_ini(tree, path, load_all);
}
static const std::unordered_map<std::string, std::string> pre_family_model_map {{
{ "MK3", "MK3" },
{ "MK3MMU2", "MK3" },
{ "MK2.5", "MK2.5" },
{ "MK2.5MMU2", "MK2.5" },
{ "MK2S", "MK2" },
{ "MK2SMM", "MK2" },
{ "SL1", "SL1" },
}};
VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem::path &path, bool load_all)
{
static const std::string printer_model_key = "printer_model:";
static const std::string filaments_section = "default_filaments";
static const std::string materials_section = "default_sla_materials";
const std::string id = path.stem().string();
if (! boost::filesystem::exists(path)) {
throw Slic3r::RuntimeError((boost::format("Cannot load Vendor Config Bundle `%1%`: File not found: `%2%`.") % id % path).str());
}
VendorProfile res(id);
// Helper to get compulsory fields
auto get_or_throw = [&](const ptree &tree, const std::string &key) -> ptree::const_assoc_iterator
{
auto res = tree.find(key);
if (res == tree.not_found()) {
throw Slic3r::RuntimeError((boost::format("Vendor Config Bundle `%1%` is not valid: Missing secion or key: `%2%`.") % id % key).str());
}
return res;
};
// Load the header
const auto &vendor_section = get_or_throw(tree, "vendor")->second;
res.name = get_or_throw(vendor_section, "name")->second.data();
auto config_version_str = get_or_throw(vendor_section, "config_version")->second.data();
auto config_version = Semver::parse(config_version_str);
if (! config_version) {
throw Slic3r::RuntimeError((boost::format("Vendor Config Bundle `%1%` is not valid: Cannot parse config_version: `%2%`.") % id % config_version_str).str());
} else {
res.config_version = std::move(*config_version);
}
// Load URLs
const auto config_update_url = vendor_section.find("config_update_url");
if (config_update_url != vendor_section.not_found()) {
res.config_update_url = config_update_url->second.data();
}
const auto changelog_url = vendor_section.find("changelog_url");
if (changelog_url != vendor_section.not_found()) {
res.changelog_url = changelog_url->second.data();
}
if (! load_all) {
return res;
}
// Load printer models
for (auto &section : tree) {
if (boost::starts_with(section.first, printer_model_key)) {
VendorProfile::PrinterModel model;
model.id = section.first.substr(printer_model_key.size());
model.name = section.second.get<std::string>("name", model.id);
const char *technology_fallback = boost::algorithm::starts_with(model.id, "SL") ? "SLA" : "FFF";
auto technology_field = section.second.get<std::string>("technology", technology_fallback);
if (! ConfigOptionEnum<PrinterTechnology>::from_string(technology_field, model.technology)) {
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Invalid printer technology field: `%2%`") % id % technology_field;
model.technology = ptFFF;
}
model.family = section.second.get<std::string>("family", std::string());
if (model.family.empty() && res.name == "BBL") {
// If no family is specified, it can be inferred for known printers
const auto from_pre_map = pre_family_model_map.find(model.id);
if (from_pre_map != pre_family_model_map.end()) { model.family = from_pre_map->second; }
}
#if 0
// Remove SLA printers from the initial alpha.
if (model.technology == ptSLA)
continue;
#endif
section.second.get<std::string>("variants", "");
const auto variants_field = section.second.get<std::string>("variants", "");
std::vector<std::string> variants;
if (Slic3r::unescape_strings_cstyle(variants_field, variants)) {
for (const std::string &variant_name : variants) {
if (model.variant(variant_name) == nullptr)
model.variants.emplace_back(VendorProfile::PrinterVariant(variant_name));
}
} else {
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Malformed variants field: `%2%`") % id % variants_field;
}
auto default_materials_field = section.second.get<std::string>("default_materials", "");
if (default_materials_field.empty())
default_materials_field = section.second.get<std::string>("default_filaments", "");
if (Slic3r::unescape_strings_cstyle(default_materials_field, model.default_materials)) {
Slic3r::sort_remove_duplicates(model.default_materials);
if (! model.default_materials.empty() && model.default_materials.front().empty())
// An empty material was inserted into the list of default materials. Remove it.
model.default_materials.erase(model.default_materials.begin());
} else {
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Malformed default_materials field: `%2%`") % id % default_materials_field;
}
model.bed_model = section.second.get<std::string>("bed_model", "");
model.bed_texture = section.second.get<std::string>("bed_texture", "");
if (! model.id.empty() && ! model.variants.empty())
res.models.push_back(std::move(model));
}
}
// Load filaments and sla materials to be installed by default
const auto filaments = tree.find(filaments_section);
if (filaments != tree.not_found()) {
for (auto &pair : filaments->second) {
if (pair.second.data() == "1") {
res.default_filaments.insert(pair.first);
}
}
}
const auto materials = tree.find(materials_section);
if (materials != tree.not_found()) {
for (auto &pair : materials->second) {
if (pair.second.data() == "1") {
res.default_sla_materials.insert(pair.first);
}
}
}
return res;
}
std::vector<std::string> VendorProfile::families() const
{
std::vector<std::string> res;
unsigned num_familiies = 0;
for (auto &model : models) {
if (std::find(res.begin(), res.end(), model.family) == res.end()) {
res.push_back(model.family);
num_familiies++;
}
}
return res;
}
// 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;
}
void Preset::update_suffix_modified(const std::string& new_suffix_modified)
{
g_suffix_modified = new_suffix_modified;
}
// Remove an optional "(modified)" suffix from a name.
// This converts a UI name to a unique preset identifier.
std::string Preset::remove_suffix_modified(const std::string &name)
{
return boost::algorithm::starts_with(name, g_suffix_modified) ?
name.substr(g_suffix_modified.size()) :
name;
}
// Update new extruder fields at the printer profile.
void Preset::normalize(DynamicPrintConfig &config)
{
// BBS
auto* filament_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("filament_diameter"));
if (filament_diameter != nullptr)
// Loaded the FFF Printer settings. Verify, that all extruder dependent values have enough values.
config.set_num_filaments((unsigned int)filament_diameter->values.size());
if (config.option("filament_diameter") != nullptr) {
// This config contains single or multiple filament presets.
// Ensure that the filament preset vector options contain the correct number of values.
// BBS
size_t n = (filament_diameter == nullptr) ? 1 : filament_diameter->values.size();
const auto &defaults = FullPrintConfig::defaults();
for (const std::string &key : Preset::filament_options()) {
if (key == "compatible_prints" || key == "compatible_printers")
continue;
auto *opt = config.option(key, false);
/*assert(opt != nullptr);
assert(opt->is_vector());*/
if (opt != nullptr && opt->is_vector())
static_cast<ConfigOptionVectorBase*>(opt)->resize(n, defaults.option(key));
}
// The following keys are mandatory for the UI, but they are not part of FullPrintConfig, therefore they are handled separately.
for (const std::string &key : { "filament_settings_id" }) {
auto *opt = config.option(key, false);
assert(opt == nullptr || opt->type() == coStrings);
if (opt != nullptr && opt->type() == coStrings)
static_cast<ConfigOptionStrings*>(opt)->values.resize(n, std::string());
}
}
handle_legacy_sla(config);
}
std::string Preset::remove_invalid_keys(DynamicPrintConfig &config, const DynamicPrintConfig &default_config)
{
std::string incorrect_keys;
for (const std::string &key : config.keys())
if (! default_config.has(key)) {
if (incorrect_keys.empty())
incorrect_keys = key;
else {
incorrect_keys += ", ";
incorrect_keys += key;
}
config.erase(key);
}
return incorrect_keys;
}
std::string Preset::get_type_string(Preset::Type type)
{
switch (type) {
case Preset::Type::TYPE_FILAMENT:
return PRESET_FILAMENT_NAME;
case Preset::Type::TYPE_PRINT:
return PRESET_PRINT_NAME;
case Preset::Type::TYPE_PRINTER:
return PRESET_PRINTER_NAME;
case Preset::Type::TYPE_PHYSICAL_PRINTER:
return "physical_printer";
case Preset::Type::TYPE_INVALID:
return "invalid";
default:
return "invalid";
}
}
std::string Preset::get_iot_type_string(Preset::Type type)
{
switch (type) {
case Preset::Type::TYPE_FILAMENT:
return PRESET_IOT_FILAMENT_TYPE;
case Preset::Type::TYPE_PRINT:
return PRESET_IOT_PRINT_TYPE;
case Preset::Type::TYPE_PRINTER:
return PRESET_IOT_PRINTER_TYPE;
default:
return "invalid";
}
}
//make the type string compatibility with local and iot type string
Preset::Type Preset::get_type_from_string(std::string type_str)
{
if (type_str.compare(PRESET_PRINT_NAME) == 0 || type_str.compare(PRESET_IOT_PRINT_TYPE) == 0)
return Preset::Type::TYPE_PRINT;
else if (type_str.compare(PRESET_FILAMENT_NAME) == 0 || type_str.compare(PRESET_IOT_FILAMENT_TYPE) == 0)
return Preset::Type::TYPE_FILAMENT;
else if (type_str.compare(PRESET_PRINTER_NAME) == 0 || type_str.compare(PRESET_IOT_PRINTER_TYPE) == 0)
return Preset::Type::TYPE_PRINTER;
else
return Preset::Type::TYPE_INVALID;
}
void Preset::load_info(const std::string& file)
{
try {
boost::property_tree::ptree tree;
boost::nowide::ifstream ifs(file);
boost::property_tree::read_ini(ifs, tree);
if (tree.empty()) return;
for (const boost::property_tree::ptree::value_type &v : tree) {
if (v.first.compare("sync_info") == 0)
this->sync_info = v.second.get_value<std::string>();
else if (v.first.compare("user_id") == 0)
this->user_id = v.second.get_value<std::string>();
else if (v.first.compare("setting_id") == 0) {
this->setting_id = v.second.get_value<std::string>();
if (this->setting_id.compare("null") == 0)
this->setting_id.clear();
}
else if (v.first.compare("base_id") == 0) {
this->base_id = v.second.get_value<std::string>();
if (this->base_id.compare("null") == 0)
this->base_id.clear();
}
else if (v.first.compare("updated_time") == 0) {
std::string time = v.second.get_value<std::string>();
this->updated_time = std::atoll(time.c_str());
}
}
}
catch (...) {
return;
}
//TODO: workaround for current info file convert, will remove it later
if (this->updated_time == 0) {
this->updated_time = (long long)Slic3r::Utils::get_current_time_utc();
//this->sync_info = "update";
BOOST_LOG_TRIVIAL(info) << boost::format("old info file, updated time to %1%") % this->updated_time;
save_info();
}
}
void Preset::save_info(std::string file)
{
//BBS: add project embedded preset logic
if (this->is_project_embedded)
return;
if (file.empty()) {
fs::path idx_file(this->file);
idx_file.replace_extension(".info");
file = idx_file.string();
}
boost::nowide::ofstream c;
c.open(file, std::ios::out | std::ios::trunc);
std::string sync_info_to_save;
//BBS: hold is used for stop requesting to server this time
if (this->sync_info.compare("hold") != 0)
sync_info_to_save = this->sync_info;
c << "sync_info" << " = " << sync_info_to_save << std::endl;
c << "user_id" << " = " << this->user_id << std::endl;
c << "setting_id" << " = " << this->setting_id << std::endl;
c << "base_id" << " = " << this->base_id << std::endl;
c << "updated_time" << " = " << std::to_string(this->updated_time) << std::endl;
c.close();
}
void Preset::remove_files()
{
//BBS: add project embedded preset logic
if (this->is_project_embedded)
return;
// Erase the preset file.
boost::nowide::remove(this->file.c_str());
fs::path idx_path(this->file);
idx_path.replace_extension(".info");
if (fs::exists(idx_path))
boost::nowide::remove(idx_path.string().c_str());
}
//BBS: add logic for only difference save
void Preset::save(DynamicPrintConfig* parent_config)
{
//BBS: add project embedded preset logic
if (this->is_project_embedded)
return;
//BBS: change to json format
//this->config.save(this->file);
std::string from_str;
if (this->is_user())
from_str = std::string("User");
else if (this->is_project_embedded)
from_str = std::string("Project");
else if (this->is_system)
from_str = std::string("System");
else
from_str = std::string("Default");
//BBS: only save difference if it has parent
if (parent_config) {
DynamicPrintConfig temp_config;
std::vector<std::string> dirty_options = config.diff(*parent_config);
for (auto option: dirty_options)
{
ConfigOption *opt_src = config.option(option);
ConfigOption *opt_dst = temp_config.option(option, true);
opt_dst->set(opt_src);
}
temp_config.save_to_json(this->file, this->name, from_str, this->version.to_string(), this->custom_defined);
}
else
this->config.save_to_json(this->file, this->name, from_str, this->version.to_string(), this->custom_defined);
fs::path idx_file(this->file);
idx_file.replace_extension(".info");
this->save_info(idx_file.string());
}
// Return a label of this preset, consisting of a name and a "(modified)" suffix, if this preset is dirty.
std::string Preset::label(bool no_alias) const
{
return (this->is_dirty ? g_suffix_modified : "")
+ ((no_alias || this->alias.empty()) ? this->name : this->alias);
}
bool is_compatible_with_print(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_print, const PresetWithVendorProfile &active_printer)
{
if (preset.vendor != nullptr && preset.vendor != active_printer.vendor)
// The current profile has a vendor assigned and it is different from the active print's vendor.
return false;
auto &condition = preset.preset.compatible_prints_condition();
auto *compatible_prints = dynamic_cast<const ConfigOptionStrings*>(preset.preset.config.option("compatible_prints"));
bool has_compatible_prints = compatible_prints != nullptr && ! compatible_prints->values.empty();
if (! has_compatible_prints && ! condition.empty()) {
try {
return PlaceholderParser::evaluate_boolean_expression(condition, active_print.preset.config);
} catch (const std::runtime_error &err) {
//FIXME in case of an error, return "compatible with everything".
printf("Preset::is_compatible_with_print - parsing error of compatible_prints_condition %s:\n%s\n", active_print.preset.name.c_str(), err.what());
return true;
}
}
return preset.preset.is_default || active_print.preset.name.empty() || ! has_compatible_prints ||
std::find(compatible_prints->values.begin(), compatible_prints->values.end(), active_print.preset.name) !=
compatible_prints->values.end();
}
//BBS: If one filament or process preset is compatible with one system printer preset,
// then we think this filament or process preset should be compatible with all
// user printer preset which is inherited from this system printer preset.
// Because printer_model and nozzle_diameter in BBL system machine preset
// can't be changed by user.
bool is_compatible_with_parent_printer(const PresetWithVendorProfile& preset, const PresetWithVendorProfile& active_printer)
{
auto *compatible_printers = dynamic_cast<const ConfigOptionStrings*>(preset.preset.config.option("compatible_printers"));
bool has_compatible_printers = compatible_printers != nullptr && ! compatible_printers->values.empty();
//BBS: FIXME only check the parent now, but should check grand-parent as well.
return has_compatible_printers &&
std::find(compatible_printers->values.begin(), compatible_printers->values.end(), active_printer.preset.inherits()) !=
compatible_printers->values.end();
}
bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_printer, const DynamicPrintConfig *extra_config)
{
if (preset.vendor != nullptr && preset.vendor != active_printer.vendor)
// The current profile has a vendor assigned and it is different from the active print's vendor.
return false;
auto &condition = preset.preset.compatible_printers_condition();
auto *compatible_printers = dynamic_cast<const ConfigOptionStrings*>(preset.preset.config.option("compatible_printers"));
bool has_compatible_printers = compatible_printers != nullptr && ! compatible_printers->values.empty();
if (! has_compatible_printers && ! condition.empty()) {
try {
return PlaceholderParser::evaluate_boolean_expression(condition, active_printer.preset.config, extra_config);
} catch (const std::runtime_error &err) {
//FIXME in case of an error, return "compatible with everything".
printf("Preset::is_compatible_with_printer - parsing error of compatible_printers_condition %s:\n%s\n", active_printer.preset.name.c_str(), err.what());
return true;
}
}
return preset.preset.is_default || active_printer.preset.name.empty() || !has_compatible_printers ||
std::find(compatible_printers->values.begin(), compatible_printers->values.end(), active_printer.preset.name) !=
compatible_printers->values.end()
//BBS
|| (!active_printer.preset.is_system && is_compatible_with_parent_printer(preset, active_printer));
}
bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_printer)
{
DynamicPrintConfig config;
config.set_key_value("printer_preset", new ConfigOptionString(active_printer.preset.name));
const ConfigOption *opt = active_printer.preset.config.option("nozzle_diameter");
if (opt)
config.set_key_value("num_extruders", new ConfigOptionInt((int)static_cast<const ConfigOptionFloats*>(opt)->values.size()));
return is_compatible_with_printer(preset, active_printer, &config);
}
void Preset::set_visible_from_appconfig(const AppConfig &app_config)
{
//BBS: add config related log
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": name %1%, is_visible %2%")%name % is_visible;
if (vendor == nullptr) { return; }
if (type == TYPE_PRINTER) {
const std::string &model = config.opt_string("printer_model");
const std::string &variant = config.opt_string("printer_variant");
if (model.empty() || variant.empty())
return;
is_visible = app_config.get_variant(vendor->id, model, variant);
} else if (type == TYPE_FILAMENT || type == TYPE_SLA_MATERIAL) {
const std::string &section_name = (type == TYPE_FILAMENT) ? AppConfig::SECTION_FILAMENTS : AppConfig::SECTION_MATERIALS;
if (app_config.has_section(section_name)) {
// Check whether this profile is marked as "installed" in PrusaSlicer.ini,
// or whether a profile is marked as "installed", which this profile may have been renamed from.
const std::map<std::string, std::string> &installed = app_config.get_section(section_name);
auto has = [&installed](const std::string &name) {
auto it = installed.find(name);
return it != installed.end() && ! it->second.empty();
};
is_visible = has(this->name);
for (auto it = this->renamed_from.begin(); ! is_visible && it != this->renamed_from.end(); ++ it)
is_visible = has(*it);
}
}
//BBS: add config related log
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": name %1%, is_visible set to %2%")%name % is_visible;
}
std::string Preset::get_filament_type(std::string &display_filament_type)
{
return config.get_filament_type(display_filament_type);
}
std::string Preset::get_printer_type(PresetBundle *preset_bundle)
{
if (preset_bundle) {
auto config = &preset_bundle->printers.get_edited_preset().config;
std::string vendor_name;
for (auto vendor_profile : preset_bundle->vendors) {
for (auto vendor_model : vendor_profile.second.models)
if (vendor_model.name == config->opt_string("printer_model"))
{
vendor_name = vendor_profile.first;
return vendor_model.model_id;
}
}
}
return "";
}
std::string Preset::get_current_printer_type(PresetBundle *preset_bundle)
{
if (preset_bundle) {
auto config = &(this->config);
std::string vendor_name;
for (auto vendor_profile : preset_bundle->vendors) {
for (auto vendor_model : vendor_profile.second.models)
if (vendor_model.name == config->opt_string("printer_model")) {
vendor_name = vendor_profile.first;
return vendor_model.model_id;
}
}
}
return "";
}
bool Preset::is_custom_defined()
{
if (custom_defined == "1")
return true;
return false;
}
BedType Preset::get_default_bed_type(PresetBundle* preset_bundle)
{
if (config.has("default_bed_type") && !config.opt_string("default_bed_type").empty()) {
try {
std::string str_bed_type = config.opt_string("default_bed_type");
int bed_type_value = atoi(str_bed_type.c_str());
return BedType(bed_type_value);
} catch(...) {
;
}
}
std::string model_id = this->get_printer_type(preset_bundle);
if (model_id == "BL-P001" || model_id == "BL-P002") {
return BedType::btPC;
} else if (model_id == "C11") {
return BedType::btPEI;
}
return BedType::btPEI;
}
bool Preset::has_cali_lines(PresetBundle* preset_bundle)
{
std::string model_id = this->get_printer_type(preset_bundle);
if (model_id == "BL-P001" || model_id == "BL-P002") {
return true;
}
return false;
}
static std::vector<std::string> s_Preset_print_options {
"layer_height", "initial_layer_print_height", "wall_loops", "slice_closing_radius", "spiral_mode", "slicing_mode",
"top_shell_layers", "top_shell_thickness", "bottom_shell_layers", "bottom_shell_thickness",
"extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall",
"seam_position", "staggered_inner_seams", "wall_infill_order", "sparse_infill_density", "sparse_infill_pattern", "top_surface_pattern", "bottom_surface_pattern",
"infill_direction",
"minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern",
"ironing_type", "ironing_pattern", "ironing_flow", "ironing_speed", "ironing_spacing", "ironing_angle",
"max_travel_detour_distance",
"fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_distance",
"max_volumetric_extrusion_rate_slope", "max_volumetric_extrusion_rate_slope_segment_length",
"inner_wall_speed", "outer_wall_speed", "sparse_infill_speed", "internal_solid_infill_speed",
"top_surface_speed", "support_speed", "support_object_xy_distance", "support_interface_speed",
"bridge_speed", "internal_bridge_speed", "gap_infill_speed", "travel_speed", "travel_speed_z", "initial_layer_speed",
"outer_wall_acceleration", "initial_layer_acceleration", "top_surface_acceleration", "default_acceleration", "skirt_loops", "skirt_speed", "skirt_distance", "skirt_height", "draft_shield",
"brim_width", "brim_object_gap", "brim_type", "brim_ears_max_angle", "brim_ears_detection_length", "enable_support", "support_type", "support_threshold_angle", "enforce_support_layers",
"raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
"support_base_pattern", "support_base_pattern_spacing", "support_expansion", "support_style",
"independent_support_layer_height",
"support_angle", "support_interface_top_layers", "support_interface_bottom_layers",
"support_interface_pattern", "support_interface_spacing", "support_interface_loop_pattern",
"support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "max_bridge_length", "print_sequence", "support_remove_small_overhang",
"filename_format", "wall_filament", "support_bottom_z_distance",
"sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament",
"ooze_prevention", "standby_temperature_delta", "interface_shells", "line_width", "initial_layer_line_width",
"inner_wall_line_width", "outer_wall_line_width", "sparse_infill_line_width", "internal_solid_infill_line_width",
"top_surface_line_width", "support_line_width", "infill_wall_overlap", "bridge_flow",
"elefant_foot_compensation", "elefant_foot_compensation_layers", "xy_contour_compensation", "xy_hole_compensation", "resolution", "enable_prime_tower",
"prime_tower_width", "prime_tower_brim_width", "prime_volume",
"wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits",
"flush_into_infill", "flush_into_objects", "flush_into_support",
"tree_support_branch_angle", "tree_support_angle_slow", "tree_support_wall_count", "tree_support_top_rate", "tree_support_branch_distance", "tree_support_tip_diameter",
"tree_support_branch_diameter", "tree_support_branch_diameter_angle", "tree_support_branch_diameter_double_wall",
"detect_narrow_internal_solid_infill",
"gcode_add_line_number", "enable_arc_fitting", "infill_combination", /*"adaptive_layer_height",*/
"support_bottom_interface_spacing", "enable_overhang_speed", "slowdown_for_curled_perimeters", "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed",
"initial_layer_infill_speed", "only_one_wall_top",
"timelapse_type",
"wall_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
"wall_distribution_count", "min_feature_size", "min_bead_width", "post_process",
"small_perimeter_speed", "small_perimeter_threshold","bridge_angle", "filter_out_gap_fill", "travel_acceleration","inner_wall_acceleration", "min_width_top_surface",
"default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk", "top_surface_jerk", "initial_layer_jerk","travel_jerk",
"top_solid_infill_flow_ratio","bottom_solid_infill_flow_ratio","only_one_wall_first_layer", "print_flow_ratio", "seam_gap",
"role_based_wipe_speed", "wipe_speed", "accel_to_decel_enable", "accel_to_decel_factor", "wipe_on_loops",
"bridge_density", "precise_outer_wall", "overhang_speed_classic", "bridge_acceleration",
"sparse_infill_acceleration", "internal_solid_infill_acceleration", "tree_support_adaptive_layer_height", "tree_support_auto_brim",
"tree_support_brim_width", "gcode_comments", "gcode_label_objects",
"initial_layer_travel_speed", "exclude_object", "slow_down_layers", "infill_anchor", "infill_anchor_max","initial_layer_min_bead_width",
"make_overhang_printable", "make_overhang_printable_angle", "make_overhang_printable_hole_size" ,"notes",
"wipe_tower_cone_angle", "wipe_tower_extra_spacing", "wipe_tower_extruder", "wiping_volumes_extruders","wipe_tower_bridging", "single_extruder_multi_material_priming",
"wipe_tower_rotation_angle", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic", "tree_support_branch_angle_organic",
"hole_to_polyhole", "hole_to_polyhole_threshold", "hole_to_polyhole_twisted"
};
static std::vector<std::string> s_Preset_filament_options {
/*"filament_colour", */ "default_filament_colour","required_nozzle_HRC","filament_diameter", "filament_type", "filament_soluble", "filament_is_support",
"filament_max_volumetric_speed",
"filament_flow_ratio", "filament_density", "filament_cost", "filament_minimal_purge_on_wipe_tower",
"nozzle_temperature", "nozzle_temperature_initial_layer",
// BBS
"cool_plate_temp", "eng_plate_temp", "hot_plate_temp", "textured_plate_temp", "cool_plate_temp_initial_layer", "eng_plate_temp_initial_layer", "hot_plate_temp_initial_layer","textured_plate_temp_initial_layer",
// "bed_type",
//BBS:temperature_vitrification
"temperature_vitrification", "reduce_fan_stop_start_freq", "slow_down_for_layer_cooling", "fan_min_speed",
"fan_max_speed", "enable_overhang_bridge_fan", "overhang_fan_speed", "overhang_fan_threshold", "close_fan_the_first_x_layers", "full_fan_speed_layer", "fan_cooling_layer_time", "slow_down_layer_time", "slow_down_min_speed",
"filament_start_gcode", "filament_end_gcode",
//exhaust fan control
"activate_air_filtration","during_print_exhaust_fan_speed","complete_print_exhaust_fan_speed",
// Retract overrides
"filament_retraction_length", "filament_z_hop", "filament_z_hop_types", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_lift_enforce", "filament_retraction_speed", "filament_deretraction_speed", "filament_retract_restart_extra", "filament_retraction_minimum_travel",
"filament_retract_when_changing_layer", "filament_wipe", "filament_retract_before_wipe",
// Profile compatibility
"filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits",
//BBS
"filament_wipe_distance", "additional_cooling_fan_speed",
"nozzle_temperature_range_low", "nozzle_temperature_range_high",
//SoftFever
"enable_pressure_advance", "pressure_advance","chamber_temperature", "filament_shrink", "support_material_interface_fan_speed", "filament_notes" /*,"filament_seam_gap"*/,
"filament_loading_speed", "filament_loading_speed_start", "filament_load_time",
"filament_unloading_speed", "filament_unloading_speed_start", "filament_unload_time", "filament_toolchange_delay", "filament_cooling_moves",
"filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters",
"filament_multitool_ramming", "filament_multitool_ramming_volume", "filament_multitool_ramming_flow",
};
static std::vector<std::string> s_Preset_machine_limits_options {
"machine_max_acceleration_extruding", "machine_max_acceleration_retracting", "machine_max_acceleration_travel",
"machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e",
"machine_max_speed_x", "machine_max_speed_y", "machine_max_speed_z", "machine_max_speed_e",
"machine_min_extruding_rate", "machine_min_travel_rate",
"machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e",
};
static std::vector<std::string> s_Preset_printer_options {
"printer_technology",
"printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "gcode_flavor",
"fan_kickstart", "fan_speedup_time", "fan_speedup_overhangs",
"single_extruder_multi_material", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "layer_change_gcode", "time_lapse_gcode", "change_filament_gcode",
"printer_model", "printer_variant", "printable_height", "extruder_clearance_radius", "extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod",
"default_print_profile", "inherits",
"silent_mode",
// BBS
"scan_first_layer", "machine_load_filament_time", "machine_unload_filament_time","time_cost", "machine_pause_gcode", "template_custom_gcode",
"nozzle_type", "nozzle_hrc","auxiliary_fan", "nozzle_volume","upward_compatible_machine", "z_hop_types", "retract_lift_enforce","support_chamber_temp_control","support_air_filtration","printer_structure",
"best_object_pos",
//SoftFever
"host_type", "print_host", "printhost_apikey",
"print_host_webui",
"printhost_cafile","printhost_port","printhost_authorization_type",
"printhost_user", "printhost_password", "printhost_ssl_ignore_revoke", "thumbnails", "thumbnails_format",
"use_firmware_retraction", "use_relative_e_distances", "printer_notes",
"cooling_tube_retraction",
"cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "purge_in_prime_tower", "enable_filament_ramming"};
static std::vector<std::string> s_Preset_sla_print_options {
"layer_height",
"faded_layers",
"supports_enable",
"support_head_front_diameter",
"support_head_penetration",
"support_head_width",
"support_pillar_diameter",
"support_small_pillar_diameter_percent",
"support_max_bridges_on_pillar",
"support_pillar_connection_mode",
"support_buildplate_only",
"support_pillar_widening_factor",
"support_base_diameter",
"support_base_height",
"support_base_safety_distance",
"support_critical_angle",
"support_max_bridge_length",
"support_max_pillar_link_distance",
"support_object_elevation",
"support_points_density_relative",
"support_points_minimal_distance",
"slice_closing_radius",
"pad_enable",
"pad_wall_thickness",
"pad_wall_height",
"pad_brim_size",
"pad_max_merge_distance",
// "pad_edge_radius",
"pad_wall_slope",
"pad_object_gap",
"pad_around_object",
"pad_around_object_everywhere",
"pad_object_connector_stride",
"pad_object_connector_width",
"pad_object_connector_penetration",
"hollowing_enable",
"hollowing_min_thickness",
"hollowing_quality",
"hollowing_closing_distance",
"filename_format",
"default_sla_print_profile",
"compatible_printers",
"compatible_printers_condition",
"inherits"
};
static std::vector<std::string> s_Preset_sla_material_options {
"material_colour",
"material_type",
"initial_layer_height",
"bottle_cost",
"bottle_volume",
"bottle_weight",
"material_density",
"exposure_time",
"initial_exposure_time",
"material_correction",
"material_correction_x",
"material_correction_y",
"material_correction_z",
"material_vendor",
"material_print_speed",
"default_sla_material_profile",
"compatible_prints", "compatible_prints_condition",
"compatible_printers", "compatible_printers_condition", "inherits"
};
static std::vector<std::string> s_Preset_sla_printer_options {
"printer_technology",
"printable_area","bed_custom_texture", "bed_custom_model", "printable_height",
"display_width", "display_height", "display_pixels_x", "display_pixels_y",
"display_mirror_x", "display_mirror_y",
"display_orientation",
"fast_tilt_time", "slow_tilt_time", "area_fill",
"relative_correction",
"relative_correction_x",
"relative_correction_y",
"relative_correction_z",
"absolute_correction",
"elefant_foot_compensation",
"elefant_foot_min_width",
"gamma_correction",
"min_exposure_time", "max_exposure_time",
"min_initial_exposure_time", "max_initial_exposure_time",
"inherits"
};
const std::vector<std::string>& Preset::print_options() { return s_Preset_print_options; }
const std::vector<std::string>& Preset::filament_options() { return s_Preset_filament_options; }
const std::vector<std::string>& Preset::machine_limits_options() { return s_Preset_machine_limits_options; }
// The following nozzle options of a printer profile will be adjusted to match the size
// of the nozzle_diameter vector.
const std::vector<std::string>& Preset::nozzle_options() { return print_config_def.extruder_option_keys(); }
const std::vector<std::string>& Preset::sla_print_options() { return s_Preset_sla_print_options; }
const std::vector<std::string>& Preset::sla_material_options() { return s_Preset_sla_material_options; }
const std::vector<std::string>& Preset::sla_printer_options() { return s_Preset_sla_printer_options; }
const std::vector<std::string>& Preset::printer_options()
{
static std::vector<std::string> s_opts = [](){
std::vector<std::string> opts = s_Preset_printer_options;
append(opts, s_Preset_machine_limits_options);
append(opts, Preset::nozzle_options());
return opts;
}();
return s_opts;
}
PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name) :
m_type(type),
m_edited_preset(type, "", false),
m_saved_preset(type, "", false),
m_idx_selected(0)
{
// Insert just the default preset.
this->add_default_preset(keys, defaults, default_name);
m_edited_preset.config.apply(m_presets.front().config);
update_saved_preset_from_current_preset();
}
//BBS: add operator= implemention
PresetCollection& PresetCollection::operator=(const PresetCollection &rhs)
{
m_type = rhs.m_type;
m_presets = rhs.m_presets;
m_map_alias_to_profile_name = rhs.m_map_alias_to_profile_name;
m_map_system_profile_renamed = rhs.m_map_system_profile_renamed;
m_edited_preset = rhs.m_edited_preset;
m_saved_preset = rhs.m_saved_preset;
m_idx_selected = rhs.m_idx_selected;
m_default_suppressed = rhs.m_default_suppressed;
m_num_default_presets = rhs.m_num_default_presets;
m_dir_path = rhs.m_dir_path;
return *this;
}
void PresetCollection::reset(bool delete_files)
{
//BBS: add lock logic for sync preset in background
lock();
if (m_presets.size() > m_num_default_presets) {
if (delete_files) {
// Erase the preset files.
for (Preset &preset : m_presets)
if (! preset.is_default && ! preset.is_external && ! preset.is_system) {
//BBS remove idx and ini files
preset.remove_files();
}
}
// Don't use m_presets.resize() here as it requires a default constructor for Preset.
m_presets.erase(m_presets.begin() + m_num_default_presets, m_presets.end());
this->select_preset(0);
}
//BBS: add lock logic for sync preset in background
unlock();
m_map_alias_to_profile_name.clear();
m_map_system_profile_renamed.clear();
}
void PresetCollection::add_default_preset(const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &preset_name)
{
// Insert just the default preset.
m_presets.emplace_back(Preset(this->type(), preset_name, true));
m_presets.back().config.apply_only(defaults, keys.empty() ? defaults.keys() : keys);
m_presets.back().loaded = true;
++ m_num_default_presets;
}
// Load all presets found in dir_path.
// Throws an exception on error.
void PresetCollection::load_presets(
const std::string &dir_path, const std::string &subdir,
PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule substitution_rule)
{
// Don't use boost::filesystem::canonical() on Windows, it is broken in regard to reparse points,
// see https://github.com/prusa3d/PrusaSlicer/issues/732
boost::filesystem::path dir = boost::filesystem::absolute(boost::filesystem::path(dir_path) / subdir).make_preferred();
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, load presets from %1%, current type %2%")%dir %Preset::get_type_string(m_type);
//BBS do not parse folder if not exists
m_dir_path = dir.string();
if (!fs::exists(dir)) {
fs::create_directory(dir);
return;
}
std::string errors_cummulative;
// Store the loaded presets into a new vector, otherwise the binary search for already existing presets would be broken.
// (see the "Preset already present, not loading" message).
std::deque<Preset> presets_loaded;
//BBS: change to json format
for (auto &dir_entry : boost::filesystem::directory_iterator(dir))
{
std::string file_name = dir_entry.path().filename().string();
//if (Slic3r::is_ini_file(dir_entry)) {
if (Slic3r::is_json_file(file_name)) {
// Remove the .ini suffix.
std::string name = file_name.erase(file_name.size() - 5);
if (this->find_preset(name, false)) {
// This happens when there's is a preset (most likely legacy one) with the same name as a system preset
// that's already been loaded from a bundle.
BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << name;
continue;
}
try {
Preset preset(m_type, name, false);
preset.file = dir_entry.path().string();
// Load the preset file, apply preset values on top of defaults.
try {
fs::path idx_path(preset.file);
idx_path.replace_extension(".info");
if (fs::exists(idx_path)) {
preset.load_info(idx_path.string());
}
DynamicPrintConfig config;
//BBS: change to json format
//ConfigSubstitutions config_substitutions = config.load_from_ini(preset.file, substitution_rule);
std::map<std::string, std::string> key_values;
std::string reason;
ConfigSubstitutions config_substitutions = config.load_from_json(preset.file, substitution_rule, key_values, reason);
if (! config_substitutions.empty())
substitutions.push_back({ preset.name, m_type, PresetConfigSubstitutions::Source::UserFile, preset.file, std::move(config_substitutions) });
if (!reason.empty()) {
fs::path file_path(preset.file);
if (fs::exists(file_path))
fs::remove(file_path);
file_path.replace_extension(".info");
if (fs::exists(file_path))
fs::remove(file_path);
BOOST_LOG_TRIVIAL(error) << boost::format("parse config %1% failed")%preset.file;
continue;
}
std::string version_str = key_values[BBL_JSON_KEY_VERSION];
boost::optional<Semver> version = Semver::parse(version_str);
if (!version) continue;
Semver app_version = *(Semver::parse(SLIC3R_VERSION));
if ( version->maj() != app_version.maj()) {
BOOST_LOG_TRIVIAL(warning) << "Preset incompatibla, not loading: " << name;
continue;
}
preset.version = *version;
if (key_values.find(BBL_JSON_KEY_IS_CUSTOM) != key_values.end())
preset.custom_defined = key_values[BBL_JSON_KEY_IS_CUSTOM];
//BBS: use inherit config as the base
Preset* inherit_preset = nullptr;
ConfigOption* inherits_config = config.option(BBL_JSON_KEY_INHERITS);
// check inherits_config
if (inherits_config) {
ConfigOptionString * option_str = dynamic_cast<ConfigOptionString *> (inherits_config);
std::string inherits_value = option_str->value;
inherit_preset = this->find_preset(inherits_value, false, true);
} else {
;
}
const Preset& default_preset = this->default_preset_for(config);
if (inherit_preset) {
preset.config = inherit_preset->config;
preset.filament_id = inherit_preset->filament_id;
}
else {
if (!preset.is_custom_defined()) {
BOOST_LOG_TRIVIAL(error) << boost::format("can not find parent for config %1%!")%preset.file;
continue;
}
//should not happen
//BOOST_LOG_TRIVIAL(error) << boost::format("can not find parent for config %1%!")%preset.file;
// Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field.
preset.config = default_preset.config;
}
preset.config.apply(std::move(config));
Preset::normalize(preset.config);
// Report configuration fields, which are misplaced into a wrong group.
std::string incorrect_keys = Preset::remove_invalid_keys(preset.config, default_preset.config);
if (!incorrect_keys.empty())
BOOST_LOG_TRIVIAL(error) << "Error in a preset file: The preset \"" <<
preset.file << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
preset.loaded = true;
//BBS: add some workaround for previous incorrect settings
if ((!preset.setting_id.empty())&&(preset.setting_id == preset.base_id))
preset.setting_id.clear();
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(", preset type %1%, name %2%, path %3%, is_system %4%, is_default %5% is_visible %6%")%Preset::get_type_string(m_type) %preset.name %preset.file %preset.is_system %preset.is_default %preset.is_visible;
} catch (const std::ifstream::failure &err) {
BOOST_LOG_TRIVIAL(error) << boost::format("The user-config cannot be loaded: %1%. Reason: %2%")%preset.file %err.what();
fs::path file_path(preset.file);
if (fs::exists(file_path))
fs::remove(file_path);
file_path.replace_extension(".info");
if (fs::exists(file_path))
fs::remove(file_path);
//throw Slic3r::RuntimeError(std::string("The selected preset cannot be loaded: ") + preset.file + "\n\tReason: " + err.what());
} catch (const std::runtime_error &err) {
BOOST_LOG_TRIVIAL(error) << boost::format("Failed loading the user-config file: %1%. Reason: %2%")%preset.file %err.what();
//throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + preset.file + "\n\tReason: " + err.what());
fs::path file_path(preset.file);
if (fs::exists(file_path))
fs::remove(file_path);
file_path.replace_extension(".info");
if (fs::exists(file_path))
fs::remove(file_path);
}
presets_loaded.emplace_back(preset);
} catch (const std::runtime_error &err) {
errors_cummulative += err.what();
errors_cummulative += "\n";
}
}
}
if (presets_loaded.size() > 0)
m_presets.insert(m_presets.end(), std::make_move_iterator(presets_loaded.begin()), std::make_move_iterator(presets_loaded.end()));
std::sort(m_presets.begin() + m_num_default_presets, m_presets.end());
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": loaded %1% presets from %2%, type %3%")%presets_loaded.size() %dir %Preset::get_type_string(m_type);
//this->select_preset(first_visible_idx());
if (! errors_cummulative.empty())
throw Slic3r::RuntimeError(errors_cummulative);
}
//BBS: add function to generate differed preset for save
//the pointer should be freed by the caller
Preset* PresetCollection::get_preset_differed_for_save(Preset& preset)
{
if (preset.is_system || preset.is_default)
return nullptr;
Preset* new_preset = new Preset();
*new_preset = preset;
//BBS: only save difference for user preset
std::string& inherits = preset.inherits();
Preset* parent_preset = nullptr;
if (!inherits.empty()) {
parent_preset = this->find_preset(inherits, false, true);
}
if (parent_preset) {
DynamicPrintConfig temp_config;
std::vector<std::string> dirty_options = preset.config.diff(parent_preset->config);
for (auto option: dirty_options)
{
ConfigOption *opt_src = preset.config.option(option);
ConfigOption *opt_dst = temp_config.option(option, true);
opt_dst->set(opt_src);
}
new_preset->config = temp_config;
}
return new_preset;
}
//BBS:get the differencen values to update
int PresetCollection::get_differed_values_to_update(Preset& preset, std::map<std::string, std::string>& key_values)
{
if (preset.is_system || preset.is_default || preset.is_project_embedded) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(" Error: not a user preset! Should not happen, name %1%") %preset.name;
return -1;
}
//BBS: only save difference for user preset
std::string& inherit_preset = preset.inherits();
Preset* parent_preset = nullptr;
if (!inherit_preset.empty()) {
parent_preset = this->find_preset(inherit_preset, false, true);
}
if (parent_preset) {
DynamicPrintConfig temp_config;
std::vector<std::string> dirty_options = preset.config.diff(parent_preset->config);
for (auto option: dirty_options)
{
ConfigOption *opt_src = preset.config.option(option);
if (opt_src)
key_values[option] = opt_src->serialize();
}
//add other values
key_values[BBL_JSON_KEY_VERSION] = preset.version.to_string();
key_values[BBL_JSON_KEY_BASE_ID] = preset.base_id;
key_values[BBL_JSON_KEY_UPDATE_TIME] = std::to_string(preset.updated_time);
key_values[BBL_JSON_KEY_TYPE] = Preset::get_iot_type_string(preset.type);
}
else {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(" Error: can not find the parent! Should not happen, name %1%") %preset.name;
return -1;
}
return 0;
}
//BBS: save user presets to local
void PresetCollection::load_project_embedded_presets(std::vector<Preset*>& project_presets, const std::string& type, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule rule)
{
std::string errors_cummulative;
// Store the loaded presets into a new vector, otherwise the binary search for already existing presets would be broken.
// (see the "Preset already present, not loading" message).
std::deque<Preset> presets_loaded;
std::vector<Preset*>::iterator it;
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, type %1% , total preset counts %2%")%Preset::get_type_string(m_type) %project_presets.size();
lock();
for (it = project_presets.begin(); it != project_presets.end(); it++) {
Preset* preset = *it;
if (preset->type != Preset::get_type_from_string(type)) continue;
if (!preset->is_project_embedded) continue;
std::string name = preset->name;
if (this->find_preset(name, false)) {
BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << name;
continue;
}
try {
DynamicPrintConfig config = preset->config;
if (preset->loading_substitutions && ! preset->loading_substitutions->empty()) {
substitutions.push_back({ preset->name, m_type, PresetConfigSubstitutions::Source::ProjectFile, preset->name, std::move(*(preset->loading_substitutions))});
free(preset->loading_substitutions);
preset->loading_substitutions = NULL;
}
//BBS: use inherit config as the base
Preset* inherit_preset = nullptr;
ConfigOption* inherits_config = config.option(BBL_JSON_KEY_INHERITS);
if (inherits_config) {
ConfigOptionString * option_str = dynamic_cast<ConfigOptionString *> (inherits_config);
std::string inherits_value = option_str->value;
/*size_t pos = inherits_value.find_first_of('*');
if (pos != std::string::npos) {
inherits_value.replace(pos, 1, 1, '~');
option_str->value = inherits_value;
}*/
inherit_preset = this->find_preset(inherits_value, false, true);
}
const Preset& default_preset = this->default_preset_for(config);
if (inherit_preset) {
preset->config = inherit_preset->config;
preset->filament_id = inherit_preset->filament_id;
}
else {
// Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field.
preset->config = default_preset.config;
BOOST_LOG_TRIVIAL(warning) << boost::format("can not find parent for config %1%!")%preset->file;
//continue;
}
preset->config.apply(std::move(config));
Preset::normalize(preset->config);
// Report configuration fields, which are misplaced into a wrong group.
std::string incorrect_keys = Preset::remove_invalid_keys(preset->config, default_preset.config);
if (! incorrect_keys.empty())
BOOST_LOG_TRIVIAL(error) << "Error in a preset file: The preset \"" <<
preset->name << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
preset->loaded = true;
presets_loaded.emplace_back(*preset);
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(", %1% got preset, name %2%, path %3%, is_system %4%, is_default %5% is_visible %6%")%Preset::get_type_string(m_type) %preset->name %preset->file %preset->is_system %preset->is_default %preset->is_visible;
} catch (const std::runtime_error &err) {
errors_cummulative += err.what();
errors_cummulative += "\n";
}
}
m_presets.insert(m_presets.end(), std::make_move_iterator(presets_loaded.begin()), std::make_move_iterator(presets_loaded.end()));
std::sort(m_presets.begin() + m_num_default_presets, m_presets.end());
//don't select it here
//this->select_preset(first_visible_idx());
unlock();
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" finished, %1% got %2% presets, errors_cummulative %3%")%Preset::get_type_string(m_type) %presets_loaded.size() %errors_cummulative;
if (! errors_cummulative.empty())
throw Slic3r::RuntimeError(errors_cummulative);
}
//BBS: get project embedded presets from
std::vector<Preset*> PresetCollection::get_project_embedded_presets()
{
std::vector<Preset*> project_presets;
lock();
for (Preset &preset : m_presets) {
//if (preset.type != Preset::get_type_from_string(type)) continue;
if (!preset.is_project_embedded) continue;
Preset* new_preset = get_preset_differed_for_save(preset);
project_presets.push_back(new_preset);
}
unlock();
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, type %1% , total preset counts %2%")%Preset::get_type_string(m_type) %project_presets.size();
return project_presets;
}
//BBS: reset project embedded presets
bool PresetCollection::reset_project_embedded_presets()
{
std::deque<Preset>::iterator it = m_presets.begin();
bool re_select = false;
int count = -1;
lock();
while ( it!=m_presets.end() )
{
count++;
//if (preset.type != Preset::get_type_from_string(type)) continue;
if (it->is_project_embedded) {
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" type %1% , delete preset %2%")%Preset::get_type_string(m_type) % it->name;
if ((!re_select) && (m_idx_selected == count))
re_select = true;
if (m_idx_selected > count) {
m_idx_selected--;
count--;
}
it = m_presets.erase(it);
}
else
it++;
}
if (re_select)
m_idx_selected = -1;
unlock();
return re_select;
}
void PresetCollection::set_sync_info_and_save(std::string name, std::string setting_id, std::string syncinfo)
{
lock();
for (auto it = m_presets.begin(); it != m_presets.end(); it++) {
Preset* preset = &m_presets[it - m_presets.begin()];
if (preset->name == name) {
if (syncinfo.empty())
preset->sync_info.clear();
else
preset->sync_info = syncinfo;
preset->setting_id = setting_id;
preset->save_info();
break;
}
}
unlock();
}
//BBS: get user presets
int PresetCollection::get_user_presets(std::vector<Preset>& result_presets)
{
int count = 0;
result_presets.clear();
lock();
for (Preset &preset : m_presets) {
if (!preset.is_user()) continue;
result_presets.push_back(preset);
count++;
}
unlock();
return count;
}
//BBS: update user presets directory
void PresetCollection::update_user_presets_directory(const std::string& dir_path, const std::string& type)
{
boost::filesystem::path dir = boost::filesystem::absolute(boost::filesystem::path(dir_path) / type).make_preferred();
if (!fs::exists(dir))
fs::create_directory(dir);
m_dir_path = dir.string();
}
//BBS: save user presets to local
void PresetCollection::save_user_presets(const std::string& dir_path, const std::string& type, std::vector<std::string>& need_to_delete_list)
{
boost::filesystem::path dir = boost::filesystem::absolute(boost::filesystem::path(dir_path) / type).make_preferred();
if (!fs::exists(dir))
fs::create_directory(dir);
m_dir_path = dir.string();
std::vector<std::string> delete_name_list;
//std::map<std::string, Preset*>::iterator it;
//for (it = my_presets.begin(); it != my_presets.end(); it++) {
for (auto it = m_presets.begin(); it != m_presets.end(); it++) {
Preset* preset = &m_presets[it - m_presets.begin()];
if (!preset->is_user()) continue;
preset->file = path_from_name(preset->name);
if (preset->is_custom_defined()) {
preset->save(nullptr);
} else {
//BBS: only save difference for user preset
std::string inherits = Preset::inherits(preset->config);
if (inherits.empty()) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(" can not find inherits for %1% , should not happen")%preset->name;
// BBS add sync info
preset->sync_info = "delete";
need_to_delete_list.push_back(preset->setting_id);
delete_name_list.push_back(preset->name);
continue;
}
Preset* parent_preset = this->find_preset(inherits, false, true);
if (!parent_preset) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(" can not find parent preset for %1% , inherits %2%")%preset->name %inherits;
continue;
}
if (preset->base_id.empty())
preset->base_id = parent_preset->setting_id;
preset->save(&(parent_preset->config));
}
}
for (auto delete_name: delete_name_list)
{
this->delete_preset(delete_name);
}
delete_name_list.clear();
return;
}
//BBS: load one user preset from key-values
bool PresetCollection::load_user_preset(std::string name, std::map<std::string, std::string> preset_values, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule rule)
{
std::string errors_cummulative;
// Store the loaded presets into a new vector, otherwise the binary search for already existing presets would be broken.
// (see the "Preset already present, not loading" message).
//std::deque<Preset> presets_loaded;
int count = 0;
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, name %1% , total value counts %2%")%name %preset_values.size();
//if the version is not matching, skip it
if (preset_values.find(BBL_JSON_KEY_VERSION) == preset_values.end()) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("can not find version, not loading for user preset %1%")%name;
return false;
}
std::string version_str = preset_values[BBL_JSON_KEY_VERSION];
boost::optional<Semver> cloud_version = Semver::parse(version_str);
if (!cloud_version) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("invalid version %1%, not loading for user preset %2%")%version_str %name;
return false;
}
Semver app_version = *(Semver::parse(SLIC3R_VERSION));
if ( cloud_version->maj() != app_version.maj()) {
BOOST_LOG_TRIVIAL(warning)<< __FUNCTION__ << boost::format("version %1% mismatch with app version %2%, not loading for user preset %3%")%version_str %SLIC3R_VERSION %name;
return false;
}
//setting_id
if (preset_values.find(BBL_JSON_KEY_SETTING_ID) == preset_values.end()) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("can not find setting_id, not loading for user preset %1%")%name;
return false;
}
std::string cloud_setting_id = preset_values[BBL_JSON_KEY_SETTING_ID];
//base_id
if (preset_values.find(BBL_JSON_KEY_BASE_ID) == preset_values.end()) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("can not find base_id, not loading for user preset %1%")%name;
return false;
}
std::string cloud_base_id = preset_values[BBL_JSON_KEY_BASE_ID];
//update_time
long long cloud_update_time = 0;
if (preset_values.find(BBL_JSON_KEY_UPDATE_TIME) != preset_values.end()) {
cloud_update_time = std::atoll(preset_values[BBL_JSON_KEY_UPDATE_TIME].c_str());
}
//user_id
if (preset_values.find(BBL_JSON_KEY_USER_ID) == preset_values.end()) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("can not find user_id, not loading for user preset %1%")%name;
return false;
}
std::string cloud_user_id = preset_values[BBL_JSON_KEY_USER_ID];
//filament_id
std::string cloud_filament_id;
if ((m_type == Preset::TYPE_FILAMENT) && preset_values.find(BBL_JSON_KEY_FILAMENT_ID) != preset_values.end()) {
cloud_filament_id = preset_values[BBL_JSON_KEY_FILAMENT_ID];
}
lock();
//std::string name = preset->name;
auto iter = this->find_preset_internal(name);
bool need_update = false;
if ((iter != m_presets.end()) && (iter->name == name)) {
BOOST_LOG_TRIVIAL(info) << "Found the Preset locally: " << name;
//BBS: we should compare the time between cloud and local
if ((cloud_update_time == 0) || (cloud_update_time <= iter->updated_time)) {
if (cloud_update_time < iter->updated_time)
iter->sync_info = "update";
else
iter->sync_info.clear();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("preset %1%'s update_time is eqaul or newer, cloud update_time %2%, local update_time %3%")%name %cloud_update_time %iter->updated_time;
unlock();
return false;
}
else {
//update the one from cloud which is newer
need_update = true;
iter->sync_info.clear();
}
}
DynamicPrintConfig new_config, cloud_config;
try {
ConfigSubstitutions config_substitutions = cloud_config.load_string_map(preset_values, rule);
if (! config_substitutions.empty())
substitutions.push_back({ name, m_type, PresetConfigSubstitutions::Source::UserCloud, name, std::move(config_substitutions) });
//BBS: use inherit config as the base
Preset* inherit_preset = nullptr;
ConfigOption* inherits_config = cloud_config.option(BBL_JSON_KEY_INHERITS);
if (inherits_config) {
ConfigOptionString * option_str = dynamic_cast<ConfigOptionString *> (inherits_config);
std::string inherits_value = option_str->value;
/*size_t pos = inherits_value.find_first_of('*');
if (pos != std::string::npos) {
inherits_value.replace(pos, 1, 1, '~');
option_str->value = inherits_value;
}*/
inherit_preset = this->find_preset(inherits_value, false, true);
}
const Preset& default_preset = this->default_preset_for(cloud_config);
if (inherit_preset) {
new_config = inherit_preset->config;
}
else {
// Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field.
//new_config = default_preset.config;
//we should skip this preset here
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", can not find inherit preset for user preset %1%, just skip")%name;
unlock();
return false;
}
new_config.apply(std::move(cloud_config));
Preset::normalize(new_config);
// Report configuration fields, which are misplaced into a wrong group.
std::string incorrect_keys = Preset::remove_invalid_keys(new_config, default_preset.config);
if (! incorrect_keys.empty())
BOOST_LOG_TRIVIAL(error) << "Error in a preset file: The preset \"" <<
name << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
if (need_update) {
if (iter->name == m_edited_preset.name && iter->is_dirty) {
// Keep modifies when update from remote
new_config.apply_only(m_edited_preset.config, m_edited_preset.config.diff(iter->config));
}
iter->config = new_config;
iter->updated_time = cloud_update_time;
iter->version = cloud_version.value();
iter->user_id = cloud_user_id;
iter->setting_id = cloud_setting_id;
iter->base_id = cloud_base_id;
iter->filament_id = cloud_filament_id;
//presets_loaded.emplace_back(*it->second);
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(", update the user preset %1% from cloud, type %2%, setting_id %3%, base_id %4%, sync_info %5% inherits %6%")
% iter->name %Preset::get_type_string(m_type) %iter->setting_id %iter->base_id %iter->sync_info %iter->inherits();
}
else {
//create a new one
Preset preset(m_type, name, false);
preset.is_system = false;
preset.loaded = true;
preset.config = new_config;
preset.updated_time = cloud_update_time;
preset.version = cloud_version.value();
preset.user_id = cloud_user_id;
preset.setting_id = cloud_setting_id;
preset.base_id = cloud_base_id;
preset.filament_id = cloud_filament_id;
size_t cur_index = iter - m_presets.begin();
m_presets.insert(iter, preset);
//m_presets.emplace_back (preset);
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(", insert a new user preset %1%, type %2%, setting_id %3%, base_id %4%, sync_info %5% inherits %6%")
%preset.name %Preset::get_type_string(m_type) %preset.setting_id %preset.base_id %preset.sync_info %preset.inherits();
if (cur_index <= m_idx_selected) {
m_idx_selected ++;
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(", increase m_idx_selected to %1%, due to user preset inserted")%m_idx_selected;
}
}
} catch (const std::runtime_error &err) {
errors_cummulative += err.what();
errors_cummulative += "\n";
}
unlock();
if (! errors_cummulative.empty())
throw Slic3r::RuntimeError(errors_cummulative);
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" finished, load user preset %1% , type %2%, errors_cummulative %3%")%name %Preset::get_type_string(m_type) %errors_cummulative;
return (need_update)?false:true;
}
//re-sort and re-select
void PresetCollection::update_after_user_presets_loaded()
{
lock();
std::string selected_name = get_selected_preset_name();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", before sort, type %1%, selected_idx %2%, selected_name %3%") %m_type %m_idx_selected %selected_name;
std::sort(m_presets.begin() + m_num_default_presets, m_presets.end());
this->select_preset_by_name(selected_name, false);
unlock();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", after sort, type %1%, selected_idx %2%") %m_type %m_idx_selected;
return;
}
//BBS: validate_printers
bool PresetCollection::validate_printers(const std::string &name, DynamicPrintConfig& config, std::string &inherit)
{
std::string& original_name = config.opt_string("printer_settings_id", true);
std::deque<Preset>::iterator it = this->find_preset_internal(original_name);
bool found = it != m_presets.end() && it->name == original_name && (it->is_system || it->is_default);
if (!found) {
it = this->find_preset_renamed(original_name);
found = it != m_presets.end() && (it->is_system || it->is_default);
}
if (!found) {
if (!inherit.empty()) {
it = this->find_preset_internal(inherit);
found = it != m_presets.end() && it->name == inherit && (it->is_system || it->is_default);
}
else {
//inherit is null , should not happen , just consider it as valid
found = false;
BOOST_LOG_TRIVIAL(warning) << boost::format(": name %1%, printer_settings %2%, no inherit, set to not found")%name %original_name;
}
}
BOOST_LOG_TRIVIAL(warning) << boost::format(": name %1%, printer_settings %2%, inherit %3%, found result %4%")%name %original_name % inherit % found;
return found;
}
// Load a preset from an already parsed config file, insert it into the sorted sequence of presets
// and select it, losing previous modifications.
Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, const DynamicPrintConfig &config, bool select, Semver file_version, bool is_custom_defined)
{
DynamicPrintConfig cfg(this->default_preset().config);
cfg.apply_only(config, cfg.keys(), true);
return this->load_preset(path, name, std::move(cfg), select, file_version, is_custom_defined);
}
static bool profile_print_params_same(const DynamicPrintConfig &cfg_old, const DynamicPrintConfig &cfg_new)
{
t_config_option_keys diff = cfg_old.diff(cfg_new);
// Following keys are used by the UI, not by the slicing core, therefore they are not important
// when comparing profiles for equality. Ignore them.
for (const char *key : { "compatible_prints", "compatible_prints_condition",
"compatible_printers", "compatible_printers_condition", "inherits",
"print_settings_id", "filament_settings_id", "sla_print_settings_id", "sla_material_settings_id", "printer_settings_id",
"printer_model", "printer_variant", "default_print_profile", "default_filament_profile", "default_sla_print_profile", "default_sla_material_profile"
})
diff.erase(std::remove(diff.begin(), diff.end(), key), diff.end());
// Preset with the same name as stored inside the config exists.
return diff.empty();
}
// Load a preset from an already parsed config file, insert it into the sorted sequence of presets
// and select it, losing previous modifications.
// Only a single profile could be edited at at the same time, which introduces complexity when loading
// filament profiles for multi-extruder printers.
std::pair<Preset*, bool> PresetCollection::load_external_preset(
// Path to the profile source file (a G-code, an AMF or 3MF file, a config file)
const std::string &path,
// Name of the profile, derived from the source file name.
const std::string &name,
// Original name of the profile, extracted from the loaded config. Empty, if the name has not been stored.
const std::string &original_name,
// Config to initialize the preset from. It may contain configs of all presets merged in a single dictionary!
const DynamicPrintConfig &combined_config,
//different settings list
const std::set<std::string> &different_settings_list,
// Select the preset after loading?
LoadAndSelect select,
const Semver file_version,
const std::string filament_id)
{
// Load the preset over a default preset, so that the missing fields are filled in from the default preset.
DynamicPrintConfig cfg(this->default_preset_for(combined_config).config);
// SoftFever: ignore print connection info from project
auto keys = cfg.keys();
keys.erase(std::remove_if(keys.begin(), keys.end(),
[](std::string &val) {
return val == "print_host" || val == "print_host_webui" || val == "printhost_apikey" ||
val == "printhost_cafile";
}),
keys.end());
cfg.apply_only(combined_config, keys, true);
std::string &inherits = Preset::inherits(cfg);
//BBS: add different settings check logic, replace the old system preset's default value with new system preset's default values
std::deque<Preset>::iterator it = this->find_preset_internal(original_name);
bool found = it != m_presets.end() && it->name == original_name;
if (! found) {
// Try to match the original_name against the "renamed_from" profile names of loaded system profiles.
it = this->find_preset_renamed(original_name);
found = it != m_presets.end();
}
if (!inherits.empty() && (different_settings_list.size() > 0)) {
auto iter = this->find_preset_internal(inherits);
if (iter != m_presets.end() && iter->name == inherits) {
//std::vector<std::string> dirty_options = cfg.diff(iter->config);
for (auto &opt : keys) {
if (different_settings_list.find(opt) != different_settings_list.end())
continue;
ConfigOption *opt_src = iter->config.option(opt);
ConfigOption *opt_dst = cfg.option(opt);
if (opt_src && opt_dst && (*opt_src != *opt_dst)) {
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" change key %1% from old_value %2% to inherit's value %3%, preset_name %4%, inherits_name %5%")
%opt %(opt_dst->serialize()) %(opt_src->serialize()) %original_name %inherits;
opt_dst->set(opt_src);
}
}
}
}
else if (found && it->is_system && (different_settings_list.size() > 0)) {
for (auto &opt : keys) {
if (different_settings_list.find(opt) != different_settings_list.end())
continue;
ConfigOption *opt_src = it->config.option(opt);
ConfigOption *opt_dst = cfg.option(opt);
if (opt_src && opt_dst && (*opt_src != *opt_dst)) {
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" change key %1% from old_value %2% to new_value %3%, preset_name %4%")
%opt %(opt_dst->serialize()) %(opt_src->serialize()) %original_name;
opt_dst->set(opt_src);
}
}
}
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, type %1% , path %2%, name %3%, original_name %4%, inherits %5%")%Preset::get_type_string(m_type) %path %name %original_name %inherits;
if (select == LoadAndSelect::Never) {
// Some filament profile has been selected and modified already.
// Check whether this profile is equal to the modified edited profile.
const Preset &edited = this->get_edited_preset();
if ((edited.name == original_name || edited.name == inherits) && profile_print_params_same(edited.config, cfg)) {
// Just point to that already selected and edited profile.
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" Just point to that already selected and edited profile %1%")%edited.name;
return std::make_pair(&(*this->find_preset_internal(edited.name)), false);
}
}
// Is there a preset already loaded with the name stored inside the config?
/*std::deque<Preset>::iterator it = this->find_preset_internal(original_name);
bool found = it != m_presets.end() && it->name == original_name;
if (! found) {
// Try to match the original_name against the "renamed_from" profile names of loaded system profiles.
it = this->find_preset_renamed(original_name);
found = it != m_presets.end();
}*/
if (found && profile_print_params_same(it->config, cfg)) {
// The preset exists and it matches the values stored inside config.
if (select == LoadAndSelect::Always)
this->select_preset(it - m_presets.begin());
//BBS: set the preset to visible
if ( !it->is_visible ) {
it->is_visible = true;
//AppConfig* app_config = get_app_config();
//if (app_config)
// app_config->set(AppConfig::SECTION_FILAMENTS, it->name, "1");
}
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" The preset exists and it matches the values stored inside config. using original_name %1%")%original_name;
return std::make_pair(&(*it), false);
}
if (! found && select != LoadAndSelect::Never && ! inherits.empty()) {
// Try to use a system profile as a base to select the system profile
// and override its settings with the loaded ones.
assert(it == m_presets.end());
it = this->find_preset_internal(inherits);
found = it != m_presets.end() && it->name == inherits;
if (found && profile_print_params_same(it->config, cfg)) {
// The system preset exists and it matches the values stored inside config.
if (select == LoadAndSelect::Always)
this->select_preset(it - m_presets.begin());
//BBS: set the preset to visible
if ( !it->is_visible ) {
it->is_visible = true;
//AppConfig* app_config = get_app_config();
//if (app_config)
// app_config->set(AppConfig::SECTION_FILAMENTS, it->name, "1");
}
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" The preset exists and it matches the values stored inside config. using inherits %1%")%inherits;
return std::make_pair(&(*it), false);
}
}
if (found) {
//BBS: only select preset for always
//if (select != LoadAndSelect::Never) {
if (select == LoadAndSelect::Always) {
// Select the existing preset and override it with new values, so that
// the differences will be shown in the preset editor against the referenced profile.
this->select_preset(it - m_presets.begin());
// The source config may contain keys from many possible preset types. Just copy those that relate to this preset.
//this->get_edited_preset().config.apply_only(combined_config, keys, true);
this->get_edited_preset().config.apply_only(cfg, keys, true);
this->update_dirty();
update_saved_preset_from_current_preset();
assert(this->get_edited_preset().is_dirty);
//BBS: set the preset to visible
if ( !it->is_visible ) {
it->is_visible = true;
//AppConfig* app_config = get_app_config();
//if (app_config)
// app_config->set(AppConfig::SECTION_FILAMENTS, it->name, "1");
}
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" Select the existing preset %1% and override it with new values")%it->name;
return std::make_pair(&(*it), this->get_edited_preset().is_dirty);
}
//BBS: for other filaments under AMS
if (it->is_project_embedded) {
//update the properties back to the preset
it->config.apply_only(cfg, keys, true);
it->is_dirty = false;
return std::make_pair(&(*it), false);
}
if (inherits.empty()) {
// Update the "inherits" field.
// There is a profile with the same name already loaded. Should we update the "inherits" field?
inherits = it->vendor ? it->name : it->inherits();
}
}
// The external preset does not match an internal preset, load the external preset.
std::string new_name;
//BBS: add project embedded preset logic
//BBS: refine the name logic
for (size_t idx = 0;; ++ idx) {
std::string prefix;
if (original_name.empty()) {
if (!inherits.empty()) {
if (idx == 0)
prefix = inherits;
else
prefix = inherits + "-" + std::to_string(idx);
}
else {
if (idx > 0)
prefix = std::to_string(idx);
}
} else {
std::string reduced_name = original_name;
//TODO
//boost::regex rx("3mf\(*\)");
//boost::iterator_range<std::string::iterator> result = boost::algorithm::find_regex(reduced_name, rx);
//if (!result.empty()) {
// reduced_name = std::string(result.begin(), result.end());
//}
if (idx == 0)
prefix = reduced_name;
else
prefix = reduced_name + "-" + std::to_string(idx) ;
}
//new_name = name + suffix;
new_name = prefix + "(" + name + ")";
it = this->find_preset_internal(new_name);
if (it == m_presets.end() || it->name != new_name)
// Unique profile name. Insert a new profile.
break;
if (profile_print_params_same(it->config, cfg)) {
// The preset exists and it matches the values stored inside config.
if (select == LoadAndSelect::Always)
this->select_preset(it - m_presets.begin());
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" The preset %1% exists and it matches the values stored inside config.")%new_name;
return std::make_pair(&(*it), false);
}
// Form another profile name.
}
// Insert a new profile.
//BBS: add project embedded preset logic
bool from_project = boost::algorithm::iends_with(name, ".3mf");
if (m_type == Preset::TYPE_PRINT)
cfg.option<ConfigOptionString >("print_settings_id", true)->value = new_name;
else if (m_type == Preset::TYPE_FILAMENT)
cfg.option<ConfigOptionStrings>("filament_settings_id", true)->values[0] = new_name;
else if (m_type == Preset::TYPE_PRINTER)
cfg.option<ConfigOptionString>("printer_settings_id", true)->value = new_name;
Preset &preset = this->load_preset(path, new_name, std::move(cfg), select == LoadAndSelect::Always);
preset.is_external = true;
preset.version = file_version;
if (!filament_id.empty())
preset.filament_id = filament_id;
else {
if (!inherits.empty()) {
Preset *parent = this->find_preset(inherits, false, true);
if (parent)
preset.filament_id = parent->filament_id;
}
}
if (from_project) {
preset.is_project_embedded = true;
}
else {
//external config
preset.file = path_from_name(preset.name);
//BBS: save full config here for external
//we can not reach here
preset.save(nullptr);
}
if (&this->get_selected_preset() == &preset)
this->get_edited_preset().is_external = true;
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(", type %1% added a preset, name %2%, path %3%, is_system %4%, is_default %5% is_external %6%")%Preset::get_type_string(m_type) %preset.name %preset.file %preset.is_system %preset.is_default %preset.is_external;
return std::make_pair(&preset, false);
}
Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select, Semver file_version, bool is_custom_defined)
{
lock();
auto it = this->find_preset_internal(name);
if (it == m_presets.end() || it->name != name) {
// The preset was not found. Create a new preset.
if (m_presets.begin() + m_idx_selected >= it)
++m_idx_selected;
it = m_presets.emplace(it, Preset(m_type, name, false));
}
Preset &preset = *it;
preset.file = path;
preset.config = std::move(config);
preset.loaded = true;
preset.is_dirty = false;
preset.custom_defined = is_custom_defined ? "1": "0";
//BBS
if (file_version.valid())
preset.version = file_version;
if (select)
this->select_preset_by_name(name, true);
unlock();
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(", preset type %1%, name %2%, path %3%, is_system %4%, is_default %5% is_visible %6%")%Preset::get_type_string(m_type) %preset.name %preset.file %preset.is_system %preset.is_default %preset.is_visible;
return preset;
}
//BBS: add project embedded preset logic
void PresetCollection::save_current_preset(const std::string &new_name, bool detach, bool save_to_project, Preset* _curr_preset)
{
Preset curr_preset = _curr_preset ? *_curr_preset : m_edited_preset;
//BBS: add lock logic for sync preset in background
std::string final_inherits;
lock();
// 1) Find the preset with a new_name or create a new one,
// initialize it with the edited config.
auto it = this->find_preset_internal(new_name);
if (it != m_presets.end() && it->name == new_name) {
// Preset with the same name found.
Preset &preset = *it;
//BBS: add project embedded preset logic
if (preset.is_default || preset.is_system) {
//if (preset.is_default || preset.is_external || preset.is_system)
// Cannot overwrite the default preset.
//BBS: add lock logic for sync preset in background
unlock();
return;
}
// Overwriting an existing preset.
preset.config = std::move(curr_preset.config);
// The newly saved preset will be activated -> make it visible.
preset.is_visible = true;
//TODO: remove the detach logic
if (detach) {
// Clear the link to the parent profile.
preset.vendor = nullptr;
preset.inherits().clear();
preset.alias.clear();
preset.renamed_from.clear();
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": save preset %1% , with detach")%new_name;
}
//BBS: add lock logic for sync preset in background
final_inherits = preset.inherits();
unlock();
} else {
// Creating a new preset.
Preset &preset = *m_presets.insert(it, curr_preset);
std::string &inherits = preset.inherits();
std::string old_name = preset.name;
preset.name = new_name;
preset.file = this->path_from_name(new_name);
preset.vendor = nullptr;
preset.alias.clear();
preset.renamed_from.clear();
preset.setting_id.clear();
if (detach) {
// Clear the link to the parent profile.
inherits.clear();
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": save preset %1% , with detach")%new_name;
} else if (preset.is_system) {
// Inheriting from a system preset.
inherits = /* preset.vendor->name + "/" + */ old_name;
} else if (inherits.empty()) {
// Inheriting from a user preset. Link the new preset to the old preset.
// inherits = old_name;
} else {
// Inherited from a user preset. Just maintain the "inherited" flag,
// meaning it will inherit from either the system preset, or the inherited user preset.
}
preset.is_default = false;
preset.is_system = false;
preset.is_external = false;
// The newly saved preset will be activated -> make it visible.
preset.is_visible = true;
// Just system presets have aliases
preset.alias.clear();
//BBS: add project embedded preset logic
if (save_to_project) {
preset.is_project_embedded = true;
}
else
preset.is_project_embedded = false;
if (m_type == Preset::TYPE_PRINT)
preset.config.option<ConfigOptionString >("print_settings_id", true)->value = preset.name;
else if (m_type == Preset::TYPE_FILAMENT)
preset.config.option<ConfigOptionStrings>("filament_settings_id", true)->values[0] = preset.name;
else if (m_type == Preset::TYPE_PRINTER)
preset.config.option<ConfigOptionString>("printer_settings_id", true)->value = preset.name;
//BBS: add lock logic for sync preset in background
final_inherits = inherits;
unlock();
}
// 2) Activate the saved preset.
this->select_preset_by_name(new_name, true);
// 2) Store the active preset to disk.
//BBS: only save difference for user preset
Preset* parent_preset = nullptr;
if (!final_inherits.empty()) {
parent_preset = this->find_preset(final_inherits, false, true);
if (parent_preset && this->get_selected_preset().base_id.empty()) {
this->get_selected_preset().base_id = parent_preset->setting_id;
}
}
this->get_selected_preset().updated_time = (long long)Slic3r::Utils::get_current_time_utc();
if (parent_preset)
this->get_selected_preset().save(&(parent_preset->config));
else
this->get_selected_preset().save(nullptr);
}
bool PresetCollection::delete_current_preset()
{
Preset &selected = this->get_selected_preset();
if (selected.is_default)
return false;
//BBS: add project embedded preset logic and refine is_external
//if (! selected.is_external && ! selected.is_system) {
if (! selected.is_system) {
//BBS Erase the preset file.
selected.remove_files();
}
//BBS: add lock logic for sync preset in background
lock();
// Remove the preset from the list.
m_presets.erase(m_presets.begin() + m_idx_selected);
unlock();
// Find the next visible preset.
size_t new_selected_idx = m_idx_selected;
if (new_selected_idx < m_presets.size())
for (; new_selected_idx < m_presets.size() && ! m_presets[new_selected_idx].is_visible; ++ new_selected_idx) ;
if (new_selected_idx == m_presets.size())
for (--new_selected_idx; new_selected_idx > 0 && !m_presets[new_selected_idx].is_visible; --new_selected_idx);
this->select_preset(new_selected_idx);
return true;
}
bool PresetCollection::delete_preset(const std::string& name)
{
auto it = this->find_preset_internal(name);
Preset& preset = *it;
if (preset.is_default)
return false;
//BBS: add project embedded preset logic and refine is_external
//if (!preset.is_external && !preset.is_system) {
if (! preset.is_system) {
preset.remove_files();
}
//BBS: add lock logic for sync preset in background
lock();
m_presets.erase(it);
unlock();
return true;
}
const Preset* PresetCollection::get_selected_preset_parent() const
{
if (this->get_selected_idx() == size_t(-1))
// This preset collection has no preset activated yet. Only the get_edited_preset() is valid.
return nullptr;
const Preset &selected_preset = this->get_selected_preset();
if (selected_preset.is_system || selected_preset.is_default)
return &selected_preset;
const Preset &edited_preset = this->get_edited_preset();
const std::string &inherits = edited_preset.inherits();
const Preset *preset = nullptr;
if (inherits.empty()) {
if (selected_preset.is_external)
return nullptr;
preset = &this->default_preset(m_type == Preset::Type::TYPE_PRINTER && edited_preset.printer_technology() == ptSLA ? 1 : 0);
} else
preset = this->find_preset(inherits, false);
if (preset == nullptr) {
// Resolve the "renamed_from" field.
assert(! inherits.empty());
auto it = this->find_preset_renamed(inherits);
if (it != m_presets.end())
preset = &(*it);
}
//BBS: add project embedded preset logic and refine is_external
return (preset == nullptr/* || preset->is_default || preset->is_external*/) ? nullptr : preset;
//return (preset == nullptr/* || preset->is_default*/ || preset->is_external) ? nullptr : preset;
}
const Preset* PresetCollection::get_preset_parent(const Preset& child) const
{
const std::string &inherits = child.inherits();
if (inherits.empty())
// return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
return nullptr;
const Preset* preset = this->find_preset(inherits, false);
if (preset == nullptr) {
auto it = this->find_preset_renamed(inherits);
if (it != m_presets.end())
preset = &(*it);
}
return
// not found
(preset == nullptr/* || preset->is_default */||
// this should not happen, user profile should not derive from an external profile
//BBS: add project embedded preset logic and refine is_external
/*preset->is_external ||*/
// this should not happen, however people are creative, see GH #4996
preset == &child) ?
nullptr :
preset;
}
// Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist.
PresetWithVendorProfile PresetCollection::get_preset_with_vendor_profile(const Preset &preset) const
{
const Preset *p = &preset;
const VendorProfile *v = nullptr;
do {
if (p->vendor != nullptr) {
v = p->vendor;
break;
}
p = this->get_preset_parent(*p);
} while (p != nullptr);
return PresetWithVendorProfile(preset, v);
}
const std::string& PresetCollection::get_preset_name_by_alias(const std::string& alias) const
{
for (
// Find the 1st profile name with the alias.
auto it = Slic3r::lower_bound_by_predicate(m_map_alias_to_profile_name.begin(), m_map_alias_to_profile_name.end(), [&alias](auto &l){ return l.first < alias; });
// Continue over all profile names with the same alias.
it != m_map_alias_to_profile_name.end() && it->first == alias; ++ it)
if (auto it_preset = this->find_preset_internal(it->second);
it_preset != m_presets.end() && it_preset->name == it->second &&
it_preset->is_visible && (it_preset->is_compatible || size_t(it_preset - m_presets.begin()) == m_idx_selected))
return it_preset->name;
return alias;
}
const std::string* PresetCollection::get_preset_name_renamed(const std::string &old_name) const
{
auto it_renamed = m_map_system_profile_renamed.find(old_name);
if (it_renamed != m_map_system_profile_renamed.end())
return &it_renamed->second;
return nullptr;
}
const std::string& PresetCollection::get_suffix_modified() {
return g_suffix_modified;
}
// Return a preset by its name. If the preset is active, a temporary copy is returned.
// If a preset is not found by its name, null is returned.
Preset* PresetCollection::find_preset(const std::string &name, bool first_visible_if_not_found, bool real)
{
Preset key(m_type, name, false);
auto it = this->find_preset_internal(name);
// Ensure that a temporary copy is returned if the preset found is currently selected.
return (it != m_presets.end() && it->name == key.name) ? &this->preset(it - m_presets.begin(), real) :
first_visible_if_not_found ? &this->first_visible() : nullptr;
}
// Return index of the first visible preset. Certainly at least the '- default -' preset shall be visible.
size_t PresetCollection::first_visible_idx() const
{
//BBS: set first visible filament to fla
size_t first_visible = -1;
size_t idx = m_default_suppressed ? m_num_default_presets : 0;
for (; idx < m_presets.size(); ++ idx)
if (m_presets[idx].is_visible) {
if (first_visible == -1)
first_visible = idx;
if (m_type != Preset::TYPE_FILAMENT)
break;
else {
if (m_presets[idx].name.find("PLA") != std::string::npos) {
first_visible = idx;
break;
}
}
}
if (first_visible == -1)
first_visible = 0;
return first_visible;
}
void PresetCollection::set_default_suppressed(bool default_suppressed)
{
if (m_default_suppressed != default_suppressed) {
m_default_suppressed = default_suppressed;
bool default_visible = ! default_suppressed || m_idx_selected < m_num_default_presets;
for (size_t i = 0; i < m_num_default_presets; ++ i)
m_presets[i].is_visible = default_visible;
}
}
size_t PresetCollection::update_compatible_internal(const PresetWithVendorProfile &active_printer, const PresetWithVendorProfile *active_print, PresetSelectCompatibleType unselect_if_incompatible)
{
DynamicPrintConfig config;
config.set_key_value("printer_preset", new ConfigOptionString(active_printer.preset.name));
const ConfigOption *opt = active_printer.preset.config.option("nozzle_diameter");
if (opt)
config.set_key_value("num_extruders", new ConfigOptionInt((int)static_cast<const ConfigOptionFloats*>(opt)->values.size()));
bool some_compatible = false;
for (size_t idx_preset = m_num_default_presets; idx_preset < m_presets.size(); ++ idx_preset) {
bool selected = idx_preset == m_idx_selected;
Preset &preset_selected = m_presets[idx_preset];
Preset &preset_edited = selected ? m_edited_preset : preset_selected;
const PresetWithVendorProfile this_preset_with_vendor_profile = this->get_preset_with_vendor_profile(preset_edited);
bool was_compatible = preset_edited.is_compatible;
preset_edited.is_compatible = is_compatible_with_printer(this_preset_with_vendor_profile, active_printer, &config);
some_compatible |= preset_edited.is_compatible;
if (active_print != nullptr)
preset_edited.is_compatible &= is_compatible_with_print(this_preset_with_vendor_profile, *active_print, active_printer);
if (! preset_edited.is_compatible && selected &&
(unselect_if_incompatible == PresetSelectCompatibleType::Always || (unselect_if_incompatible == PresetSelectCompatibleType::OnlyIfWasCompatible && was_compatible)))
m_idx_selected = size_t(-1);
if (selected)
preset_selected.is_compatible = preset_edited.is_compatible;
}
// Update visibility of the default profiles here if the defaults are suppressed, the current profile is not compatible and we don't want to select another compatible profile.
if (m_idx_selected >= m_num_default_presets && m_default_suppressed)
for (size_t i = 0; i < m_num_default_presets; ++ i)
m_presets[i].is_visible = ! some_compatible;
return m_idx_selected;
}
// Update a dirty flag of the current preset
// Return true if the dirty flag changed.
bool PresetCollection::update_dirty()
{
bool was_dirty = this->get_selected_preset().is_dirty;
bool is_dirty = current_is_dirty();
this->get_selected_preset().is_dirty = is_dirty;
this->get_edited_preset().is_dirty = is_dirty;
return was_dirty != is_dirty;
}
template<class T>
void add_correct_opts_to_diff(const std::string &opt_key, t_config_option_keys& vec, const ConfigBase &other, const ConfigBase &this_c)
{
const T* opt_init = static_cast<const T*>(other.option(opt_key));
const T* opt_cur = static_cast<const T*>(this_c.option(opt_key));
int opt_init_max_id = opt_init->values.size() - 1;
for (int i = 0; i < int(opt_cur->values.size()); i++)
{
int init_id = i <= opt_init_max_id ? i : 0;
if (opt_cur->values[i] != opt_init->values[init_id])
vec.emplace_back(opt_key + "#" + std::to_string(i));
}
}
// Use deep_diff to correct return of changed options, considering individual options for each extruder.
inline t_config_option_keys deep_diff(const ConfigBase &config_this, const ConfigBase &config_other)
{
t_config_option_keys diff;
for (const t_config_option_key &opt_key : config_this.keys()) {
const ConfigOption *this_opt = config_this.option(opt_key);
const ConfigOption *other_opt = config_other.option(opt_key);
if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
{
//BBS: add bed_exclude_area
if (opt_key == "printable_area" || opt_key == "bed_exclude_area" || opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "thumbnails") {
// Scalar variable, or a vector variable, which is independent from number of extruders,
// thus the vector is presented to the user as a single input.
diff.emplace_back(opt_key);
} else if (opt_key == "default_filament_profile") {
// Ignore this field, it is not presented to the user, therefore showing a "modified" flag for this parameter does not help.
// Also the length of this field may differ, which may lead to a crash if the block below is used.
} else {
switch (other_opt->type()) {
case coInts: add_correct_opts_to_diff<ConfigOptionInts >(opt_key, diff, config_other, config_this); break;
case coBools: add_correct_opts_to_diff<ConfigOptionBools >(opt_key, diff, config_other, config_this); break;
case coFloats: add_correct_opts_to_diff<ConfigOptionFloats >(opt_key, diff, config_other, config_this); break;
case coStrings: add_correct_opts_to_diff<ConfigOptionStrings >(opt_key, diff, config_other, config_this); break;
case coPercents:add_correct_opts_to_diff<ConfigOptionPercents >(opt_key, diff, config_other, config_this); break;
case coPoints: add_correct_opts_to_diff<ConfigOptionPoints >(opt_key, diff, config_other, config_this); break;
// BBS
case coEnums: add_correct_opts_to_diff<ConfigOptionInts >(opt_key, diff, config_other, config_this); break;
default: diff.emplace_back(opt_key); break;
}
}
}
}
return diff;
}
static constexpr const std::initializer_list<const char*> optional_keys { "compatible_prints", "compatible_printers" };
//BBS: skip these keys for dirty check
static std::set<std::string> skipped_in_dirty = {"printer_settings_id", "print_settings_id", "filament_settings_id"};
bool PresetCollection::is_dirty(const Preset *edited, const Preset *reference)
{
if (edited != nullptr && reference != nullptr) {
// Only compares options existing in both configs.
if (! reference->config.equals(edited->config, &skipped_in_dirty))
return true;
// The "compatible_printers" option key is handled differently from the others:
// It is not mandatory. If the key is missing, it means it is compatible with any printer.
// If the key exists and it is empty, it means it is compatible with no printer.
for (auto &opt_key : optional_keys)
if (reference->config.has(opt_key) != edited->config.has(opt_key))
return true;
}
return false;
}
std::vector<std::string> PresetCollection::dirty_options(const Preset *edited, const Preset *reference, const bool deep_compare /*= false*/)
{
std::vector<std::string> changed;
if (edited != nullptr && reference != nullptr) {
// Only compares options existing in both configs.
changed = deep_compare ?
deep_diff(edited->config, reference->config) :
reference->config.diff(edited->config);
// The "compatible_printers" option key is handled differently from the others:
// It is not mandatory. If the key is missing, it means it is compatible with any printer.
// If the key exists and it is empty, it means it is compatible with no printer.
for (auto &opt_key : optional_keys)
if (reference->config.has(opt_key) != edited->config.has(opt_key))
changed.emplace_back(opt_key);
}
return changed;
}
//BBS: add function for dirty_options_without_option_list
std::vector<std::string> PresetCollection::dirty_options_without_option_list(const Preset *edited, const Preset *reference, const std::set<std::string>& option_ignore_list, const bool deep_compare)
{
std::vector<std::string> changed;
if (edited != nullptr && reference != nullptr) {
// Only compares options existing in both configs.
changed = deep_compare ?
deep_diff(edited->config, reference->config) :
reference->config.diff(edited->config);
// The "compatible_printers" option key is handled differently from the others:
// It is not mandatory. If the key is missing, it means it is compatible with any printer.
// If the key exists and it is empty, it means it is compatible with no printer.
for (auto &opt_key : optional_keys) {
if (reference->config.has(opt_key) != edited->config.has(opt_key))
changed.emplace_back(opt_key);
}
auto iter = changed.begin();
while (iter != changed.end()) {
if (option_ignore_list.find(*iter) != option_ignore_list.end()) {
iter = changed.erase(iter);
}
else {
++iter;
}
}
}
return changed;
}
// Select a new preset. This resets all the edits done to the currently selected preset.
// If the preset with index idx does not exist, a first visible preset is selected.
Preset& PresetCollection::select_preset(size_t idx)
{
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": %1% try to select preset %2%")%Preset::get_type_string(m_type) %idx;
for (Preset &preset : m_presets)
preset.is_dirty = false;
if (idx >= m_presets.size())
idx = first_visible_idx();
m_idx_selected = idx;
m_edited_preset = m_presets[idx];
update_saved_preset_from_current_preset();
bool default_visible = ! m_default_suppressed || m_idx_selected < m_num_default_presets;
for (size_t i = 0; i < m_num_default_presets; ++i)
m_presets[i].is_visible = default_visible;
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": %1% select success, m_idx_selected %2%, name %3%, is_system %4%, is_default %5%")%Preset::get_type_string(m_type) % m_idx_selected % m_edited_preset.name % m_edited_preset.is_system % m_edited_preset.is_default;
return m_presets[idx];
}
bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, bool force)
{
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": %1%, try to select by name %2%, force %3%")%Preset::get_type_string(m_type) %name_w_suffix %force;
std::string name = Preset::remove_suffix_modified(name_w_suffix);
// 1) Try to find the preset by its name.
auto it = this->find_preset_internal(name);
size_t idx = 0;
if (it != m_presets.end() && it->name == name && it->is_visible)
// Preset found by its name and it is visible.
idx = it - m_presets.begin();
else {
// Find the first visible preset.
for (size_t i = m_default_suppressed ? m_num_default_presets : 0; i < m_presets.size(); ++ i)
if (m_presets[i].is_visible) {
idx = i;
break;
}
// If the first visible preset was not found, return the 0th element, which is the default preset.
}
// 2) Select the new preset.
if (m_idx_selected != idx || force) {
this->select_preset(idx);
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": %1%, select %2%, success")%Preset::get_type_string(m_type) %name_w_suffix;
return true;
}
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": %1%, select %2%, failed")%Preset::get_type_string(m_type) %name_w_suffix;
return false;
}
bool PresetCollection::select_preset_by_name_strict(const std::string &name)
{
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": %1%, try to select by name %2%")%Preset::get_type_string(m_type) %name;
// 1) Try to find the preset by its name.
auto it = this->find_preset_internal(name);
size_t idx = (size_t)-1;
if (it != m_presets.end() && it->name == name && it->is_visible)
// Preset found by its name.
idx = it - m_presets.begin();
// 2) Select the new preset.
if (idx != (size_t)-1) {
this->select_preset(idx);
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": %1%, select %2%, success")%Preset::get_type_string(m_type) %name;
return true;
}
m_idx_selected = idx;
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": %1%, select %2%, failed")%Preset::get_type_string(m_type) %name;
return false;
}
// Merge one vendor's presets with the other vendor's presets, report duplicates.
std::vector<std::string> PresetCollection::merge_presets(PresetCollection &&other, const VendorMap &new_vendors)
{
std::vector<std::string> duplicates;
for (Preset &preset : other.m_presets) {
if (preset.is_default || preset.is_external)
continue;
Preset key(m_type, preset.name);
auto it = std::lower_bound(m_presets.begin() + m_num_default_presets, m_presets.end(), key);
if (it == m_presets.end() || it->name != preset.name) {
if (preset.vendor != nullptr) {
// Re-assign a pointer to the vendor structure in the new PresetBundle.
auto it = new_vendors.find(preset.vendor->id);
assert(it != new_vendors.end());
preset.vendor = &it->second;
}
m_presets.emplace(it, std::move(preset));
} else
duplicates.emplace_back(std::move(preset.name));
}
return duplicates;
}
void PresetCollection::update_vendor_ptrs_after_copy(const VendorMap &new_vendors)
{
for (Preset &preset : m_presets)
if (preset.vendor != nullptr) {
assert(! preset.is_default && ! preset.is_external);
// Re-assign a pointer to the vendor structure in the new PresetBundle.
auto it = new_vendors.find(preset.vendor->id);
assert(it != new_vendors.end());
preset.vendor = &it->second;
}
}
void PresetCollection::update_map_alias_to_profile_name()
{
m_map_alias_to_profile_name.clear();
for (const Preset &preset : m_presets)
m_map_alias_to_profile_name.emplace_back(preset.alias, preset.name);
std::sort(m_map_alias_to_profile_name.begin(), m_map_alias_to_profile_name.end(), [](auto &l, auto &r) { return l.first < r.first; });
}
void PresetCollection::update_map_system_profile_renamed()
{
m_map_system_profile_renamed.clear();
for (Preset &preset : m_presets)
for (const std::string &renamed_from : preset.renamed_from) {
const auto [it, success] = m_map_system_profile_renamed.insert(std::pair<std::string, std::string>(renamed_from, preset.name));
if (! success)
BOOST_LOG_TRIVIAL(error) << boost::format("Preset name \"%1%\" was marked as renamed from \"%2%\", though preset name \"%3%\" was marked as renamed from \"%2%\" as well.") % preset.name % renamed_from % it->second;
}
}
std::string PresetCollection::name() const
{
switch (this->type()) {
case Preset::TYPE_PRINT: return L(PRESET_PRINT_NAME);
case Preset::TYPE_FILAMENT: return L(PRESET_FILAMENT_NAME);
//case Preset::TYPE_SLA_PRINT: return L("SLA print");
//case Preset::TYPE_SLA_MATERIAL: return L("SLA material");
case Preset::TYPE_PRINTER: return L(PRESET_PRINTER_NAME);
default: return "invalid";
}
}
//BBS: change directoties by design
std::string PresetCollection::section_name() const
{
switch (this->type()) {
case Preset::TYPE_PRINT: return PRESET_PRINT_NAME;
case Preset::TYPE_FILAMENT: return PRESET_FILAMENT_NAME;
//case Preset::TYPE_SLA_PRINT: return PRESET_SLA_PRINT_NAME;
//case Preset::TYPE_SLA_MATERIAL: return PRESET_SLA_MATERIALS_NAME;
case Preset::TYPE_PRINTER: return PRESET_PRINTER_NAME;
default: return "invalid";
}
}
// Used for validating the "inherits" flag when importing user's config bundles.
// Returns names of all system presets including the former names of these presets.
std::vector<std::string> PresetCollection::system_preset_names() const
{
size_t num = 0;
for (const Preset &preset : m_presets)
if (preset.is_system)
++ num;
std::vector<std::string> out;
out.reserve(num);
for (const Preset &preset : m_presets)
if (preset.is_system) {
out.emplace_back(preset.name);
out.insert(out.end(), preset.renamed_from.begin(), preset.renamed_from.end());
}
std::sort(out.begin(), out.end());
return out;
}
// Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
std::string PresetCollection::path_from_name(const std::string &new_name) const
{
//BBS: change to json format
//std::string file_name = boost::iends_with(new_name, ".ini") ? new_name : (new_name + ".ini");
std::string file_name = boost::iends_with(new_name, ".json") ? new_name : (new_name + ".json");
return (boost::filesystem::path(m_dir_path) / file_name).make_preferred().string();
}
const Preset& PrinterPresetCollection::default_preset_for(const DynamicPrintConfig &config) const
{
const ConfigOptionEnumGeneric *opt_printer_technology = config.opt<ConfigOptionEnumGeneric>("printer_technology");
return this->default_preset((opt_printer_technology == nullptr || opt_printer_technology->value == ptFFF) ? 0 : 1);
}
const Preset* PrinterPresetCollection::find_system_preset_by_model_and_variant(const std::string &model_id, const std::string& variant) const
{
if (model_id.empty()) { return nullptr; }
const auto it = std::find_if(cbegin(), cend(), [&](const Preset &preset) {
if (!preset.is_system || preset.config.opt_string("printer_model") != model_id)
return false;
if (variant.empty())
return true;
return preset.config.opt_string("printer_variant") == variant;
});
return it != cend() ? &*it : nullptr;
}
bool PrinterPresetCollection::only_default_printers() const
{
for (const auto& printer : get_presets()) {
if (!boost::starts_with(printer.name,"Default") && printer.is_visible)
return false;
}
return true;
}
// -------------------------
// *** PhysicalPrinter ***
// -------------------------
std::string PhysicalPrinter::separator()
{
return " * ";
}
static std::vector<std::string> s_PhysicalPrinter_opts {
"preset_name", // temporary option to compatibility with older Slicer
"preset_names",
"printer_technology",
"host_type",
"print_host",
"print_host_webui",
"printhost_apikey",
"printhost_cafile",
"printhost_port",
"printhost_authorization_type",
// HTTP digest authentization (RFC 2617)
"printhost_user",
"printhost_password",
"printhost_ssl_ignore_revoke"
};
const std::vector<std::string>& PhysicalPrinter::printer_options()
{
return s_PhysicalPrinter_opts;
}
std::vector<std::string> PhysicalPrinter::presets_with_print_host_information(const PrinterPresetCollection& printer_presets)
{
std::vector<std::string> presets;
for (const Preset& preset : printer_presets)
if (has_print_host_information(preset.config))
presets.emplace_back(preset.name);
return presets;
}
bool PhysicalPrinter::has_print_host_information(const DynamicPrintConfig& config)
{
return false;
}
const std::set<std::string>& PhysicalPrinter::get_preset_names() const
{
return preset_names;
}
// temporary workaround for compatibility with older Slicer
static void update_preset_name_option(const std::set<std::string>& preset_names, DynamicPrintConfig& config)
{
std::string name;
for (auto el : preset_names)
name += el + ";";
name.pop_back();
config.set_key_value("preset_name", new ConfigOptionString(name));
}
void PhysicalPrinter::update_preset_names_in_config()
{
if (!preset_names.empty()) {
std::vector<std::string>& values = config.option<ConfigOptionStrings>("preset_names")->values;
values.clear();
for (auto preset : preset_names)
values.push_back(preset);
// temporary workaround for compatibility with older Slicer
update_preset_name_option(preset_names, config);
}
}
void PhysicalPrinter::save(const std::string& file_name_from, const std::string& file_name_to)
{
// rename the file
boost::nowide::rename(file_name_from.data(), file_name_to.data());
this->file = file_name_to;
// save configuration
//BBS: change to save
//this->config.save(this->file);
this->config.save_to_json(this->file, std::string("Physical_Printer"), std::string("User"), std::string(SLIC3R_VERSION));
}
void PhysicalPrinter::update_from_preset(const Preset& preset)
{
config.apply_only(preset.config, printer_options(), true);
// add preset names to the options list
preset_names.emplace(preset.name);
update_preset_names_in_config();
}
void PhysicalPrinter::update_from_config(const DynamicPrintConfig& new_config)
{
config.apply_only(new_config, printer_options(), false);
const std::vector<std::string>& values = config.option<ConfigOptionStrings>("preset_names")->values;
if (values.empty())
preset_names.clear();
else {
for (const std::string& val : values)
preset_names.emplace(val);
// temporary workaround for compatibility with older Slicer
update_preset_name_option(preset_names, config);
}
}
void PhysicalPrinter::reset_presets()
{
return preset_names.clear();
}
bool PhysicalPrinter::add_preset(const std::string& preset_name)
{
return preset_names.emplace(preset_name).second;
}
bool PhysicalPrinter::delete_preset(const std::string& preset_name)
{
return preset_names.erase(preset_name) > 0;
}
PhysicalPrinter::PhysicalPrinter(const std::string& name, const DynamicPrintConfig& default_config) :
name(name), config(default_config)
{
update_from_config(config);
}
PhysicalPrinter::PhysicalPrinter(const std::string& name, const DynamicPrintConfig &default_config, const Preset& preset) :
name(name), config(default_config)
{
update_from_preset(preset);
}
void PhysicalPrinter::set_name(const std::string& name)
{
this->name = name;
}
std::string PhysicalPrinter::get_full_name(std::string preset_name) const
{
return name + separator() + preset_name;
}
std::string PhysicalPrinter::get_short_name(std::string full_name)
{
int pos = full_name.find(separator());
if (pos > 0)
boost::erase_tail(full_name, full_name.length() - pos);
return full_name;
}
std::string PhysicalPrinter::get_preset_name(std::string name)
{
int pos = name.find(separator());
boost::erase_head(name, pos + 3);
return Preset::remove_suffix_modified(name);
}
// -----------------------------------
// *** PhysicalPrinterCollection ***
// -----------------------------------
PhysicalPrinterCollection::PhysicalPrinterCollection( const std::vector<std::string>& keys)
{
// Default config for a physical printer containing all key/value pairs of PhysicalPrinter::printer_options().
for (const std::string &key : keys) {
const ConfigOptionDef *opt = print_config_def.get(key);
assert(opt);
assert(opt->default_value);
m_default_config.set_key_value(key, opt->default_value->clone());
}
}
// Load all printers found in dir_path.
// Throws an exception on error.
void PhysicalPrinterCollection::load_printers(
const std::string& dir_path, const std::string& subdir,
PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule substitution_rule)
{
// Don't use boost::filesystem::canonical() on Windows, it is broken in regard to reparse points,
// see https://github.com/prusa3d/PrusaSlicer/issues/732
boost::filesystem::path dir = boost::filesystem::absolute(boost::filesystem::path(dir_path) / subdir).make_preferred();
m_dir_path = dir.string();
if(!boost::filesystem::exists(dir))
return;
std::string errors_cummulative;
// Store the loaded printers into a new vector, otherwise the binary search for already existing presets would be broken.
std::deque<PhysicalPrinter> printers_loaded;
//BBS: change to json format
for (auto& dir_entry : boost::filesystem::directory_iterator(dir))
{
std::string file_name = dir_entry.path().filename().string();
//if (Slic3r::is_ini_file(dir_entry)) {
if (Slic3r::is_json_file(file_name)) {
// Remove the .json suffix.
std::string name = file_name.erase(file_name.size() - 5);
if (this->find_printer(name, false)) {
// This happens when there's is a preset (most likely legacy one) with the same name as a system preset
// that's already been loaded from a bundle.
BOOST_LOG_TRIVIAL(warning) << "Printer already present, not loading: " << name;
continue;
}
try {
PhysicalPrinter printer(name, this->default_config());
printer.file = dir_entry.path().string();
// Load the preset file, apply preset values on top of defaults.
try {
DynamicPrintConfig config;
//ConfigSubstitutions config_substitutions = config.load_from_ini(printer.file, substitution_rule);
std::map<std::string, std::string> key_values;
std::string reason;
ConfigSubstitutions config_substitutions = config.load_from_json(printer.file, substitution_rule, key_values, reason);
if (! config_substitutions.empty())
substitutions.push_back({ name, Preset::TYPE_PHYSICAL_PRINTER, PresetConfigSubstitutions::Source::UserFile, printer.file, std::move(config_substitutions) });
printer.update_from_config(config);
printer.loaded = true;
}
catch (const std::ifstream::failure& err) {
throw Slic3r::RuntimeError(std::string("The selected preset cannot be loaded: ") + printer.file + "\n\tReason: " + err.what());
}
catch (const std::runtime_error& err) {
throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + printer.file + "\n\tReason: " + err.what());
}
printers_loaded.emplace_back(printer);
}
catch (const std::runtime_error& err) {
errors_cummulative += err.what();
errors_cummulative += "\n";
}
}
}
m_printers.insert(m_printers.end(), std::make_move_iterator(printers_loaded.begin()), std::make_move_iterator(printers_loaded.end()));
std::sort(m_printers.begin(), m_printers.end());
if (!errors_cummulative.empty())
throw Slic3r::RuntimeError(errors_cummulative);
}
void PhysicalPrinterCollection::load_printer(const std::string& path, const std::string& name, DynamicPrintConfig&& config, bool select, bool save/* = false*/)
{
auto it = this->find_printer_internal(name);
if (it == m_printers.end() || it->name != name) {
// The preset was not found. Create a new preset.
it = m_printers.emplace(it, PhysicalPrinter(name, config));
}
it->file = path;
it->config = std::move(config);
it->loaded = true;
if (select)
this->select_printer(*it);
if (save)
it->save(nullptr);
}
// if there is saved user presets, contains information about "Print Host upload",
// Create default printers with this presets
// Note! "Print Host upload" options will be cleared after physical printer creations
void PhysicalPrinterCollection::load_printers_from_presets(PrinterPresetCollection& printer_presets)
{
//BBS
#if 0
int cnt=0;
for (Preset& preset: printer_presets) {
DynamicPrintConfig& config = preset.config;
for(const char* option : legacy_print_host_options) {
if (!config.opt_string(option).empty()) {
// check if printer with those "Print Host upload" options already exist
PhysicalPrinter* existed_printer = find_printer_with_same_config(config);
if (existed_printer)
// just add preset for this printer
existed_printer->add_preset(preset.name);
else {
std::string new_printer_name = (boost::format("Printer %1%") % ++cnt ).str();
while (find_printer(new_printer_name))
new_printer_name = (boost::format("Printer %1%") % ++cnt).str();
// create new printer from this preset
PhysicalPrinter printer(new_printer_name, this->default_config(), preset);
printer.loaded = true;
save_printer(printer);
}
// erase "Print Host upload" information from the preset
for (const char *opt : legacy_print_host_options)
config.opt_string(opt).clear();
// save changes for preset
preset.save(nullptr);
// update those changes for edited preset if it's equal to the preset
Preset& edited = printer_presets.get_edited_preset();
if (preset.name == edited.name) {
for (const char *opt : legacy_print_host_options)
edited.config.opt_string(opt).clear();
}
break;
}
}
}
#endif
}
PhysicalPrinter* PhysicalPrinterCollection::find_printer( const std::string& name, bool case_sensitive_search)
{
auto it = this->find_printer_internal(name, case_sensitive_search);
// Ensure that a temporary copy is returned if the preset found is currently selected.
auto is_equal_name = [name, case_sensitive_search](const std::string& in_name) {
if (case_sensitive_search)
return in_name == name;
return boost::to_lower_copy<std::string>(in_name) == boost::to_lower_copy<std::string>(name);
};
if (it == m_printers.end() || !is_equal_name(it->name))
return nullptr;
return &this->printer(it - m_printers.begin());
}
std::deque<PhysicalPrinter>::iterator PhysicalPrinterCollection::find_printer_internal(const std::string& name, bool case_sensitive_search/* = true*/)
{
if (case_sensitive_search)
return Slic3r::lower_bound_by_predicate(m_printers.begin(), m_printers.end(), [&name](const auto& l) { return l.name < name; });
std::string low_name = boost::to_lower_copy<std::string>(name);
size_t i = 0;
for (const PhysicalPrinter& printer : m_printers) {
if (boost::to_lower_copy<std::string>(printer.name) == low_name)
break;
i++;
}
if (i == m_printers.size())
return m_printers.end();
return m_printers.begin() + i;
}
// Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
std::string PhysicalPrinterCollection::path_from_name(const std::string& new_name) const
{
//BBS: change to json format
//std::string file_name = boost::iends_with(new_name, ".ini") ? new_name : (new_name + ".ini");
std::string file_name = boost::iends_with(new_name, ".json") ? new_name : (new_name + ".json");
return (boost::filesystem::path(m_dir_path) / file_name).make_preferred().string();
}
void PhysicalPrinterCollection::save_printer(PhysicalPrinter& edited_printer, const std::string& renamed_from/* = ""*/)
{
// controll and update preset_names in edited_printer config
edited_printer.update_preset_names_in_config();
std::string name = renamed_from.empty() ? edited_printer.name : renamed_from;
// 1) Find the printer with a new_name or create a new one,
// initialize it with the edited config.
auto it = this->find_printer_internal(name);
if (it != m_printers.end() && it->name == name) {
// Printer with the same name found.
// Overwriting an existing preset.
it->config = std::move(edited_printer.config);
it->name = edited_printer.name;
it->preset_names = edited_printer.preset_names;
// sort printers and get new it
std::sort(m_printers.begin(), m_printers.end());
it = this->find_printer_internal(edited_printer.name);
}
else {
// Creating a new printer.
it = m_printers.emplace(it, edited_printer);
}
assert(it != m_printers.end());
// 2) Save printer
PhysicalPrinter& printer = *it;
if (printer.file.empty())
printer.file = this->path_from_name(printer.name);
if (printer.file == this->path_from_name(printer.name))
printer.save(nullptr);
else
// if printer was renamed, we should rename a file and than save the config
printer.save(printer.file, this->path_from_name(printer.name));
// update idx_selected
m_idx_selected = it - m_printers.begin();
}
bool PhysicalPrinterCollection::delete_printer(const std::string& name)
{
auto it = this->find_printer_internal(name);
if (it == m_printers.end())
return false;
const PhysicalPrinter& printer = *it;
// Erase the preset file.
boost::nowide::remove(printer.file.c_str());
m_printers.erase(it);
return true;
}
bool PhysicalPrinterCollection::delete_selected_printer()
{
if (!has_selection())
return false;
const PhysicalPrinter& printer = this->get_selected_printer();
// Erase the preset file.
boost::nowide::remove(printer.file.c_str());
// Remove the preset from the list.
m_printers.erase(m_printers.begin() + m_idx_selected);
// unselect all printers
unselect_printer();
return true;
}
bool PhysicalPrinterCollection::delete_preset_from_printers( const std::string& preset_name)
{
std::vector<std::string> printers_for_delete;
for (PhysicalPrinter& printer : m_printers) {
if (printer.preset_names.size() == 1 && *printer.preset_names.begin() == preset_name)
printers_for_delete.emplace_back(printer.name);
else if (printer.delete_preset(preset_name))
save_printer(printer);
}
if (!printers_for_delete.empty())
for (const std::string& printer_name : printers_for_delete)
delete_printer(printer_name);
unselect_printer();
return true;
}
// Get list of printers which have more than one preset and "preset_names" preset is one of them
std::vector<std::string> PhysicalPrinterCollection::get_printers_with_preset(const std::string& preset_name)
{
std::vector<std::string> printers;
for (auto printer : m_printers) {
if (printer.preset_names.size() == 1)
continue;
if (printer.preset_names.find(preset_name) != printer.preset_names.end())
printers.emplace_back(printer.name);
}
return printers;
}
// Get list of printers which has only "preset_names" preset
std::vector<std::string> PhysicalPrinterCollection::get_printers_with_only_preset(const std::string& preset_name)
{
std::vector<std::string> printers;
for (auto printer : m_printers)
if (printer.preset_names.size() == 1 && *printer.preset_names.begin() == preset_name)
printers.emplace_back(printer.name);
return printers;
}
std::string PhysicalPrinterCollection::get_selected_full_printer_name() const
{
return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().get_full_name(m_selected_preset);
}
void PhysicalPrinterCollection::select_printer(const std::string& full_name)
{
std::string printer_name = PhysicalPrinter::get_short_name(full_name);
auto it = this->find_printer_internal(printer_name);
if (it == m_printers.end()) {
unselect_printer();
return;
}
// update idx_selected
m_idx_selected = it - m_printers.begin();
// update name of the currently selected preset
if (printer_name == full_name)
// use first preset in the list
m_selected_preset = *it->preset_names.begin();
else
m_selected_preset = it->get_preset_name(full_name);
}
void PhysicalPrinterCollection::select_printer(const std::string& printer_name, const std::string& preset_name)
{
if (preset_name.empty())
return select_printer(printer_name);
return select_printer(printer_name + PhysicalPrinter::separator() + preset_name);
}
void PhysicalPrinterCollection::select_printer(const PhysicalPrinter& printer)
{
return select_printer(printer.name);
}
bool PhysicalPrinterCollection::has_selection() const
{
return m_idx_selected != size_t(-1);
}
void PhysicalPrinterCollection::unselect_printer()
{
m_idx_selected = size_t(-1);
m_selected_preset.clear();
}
bool PhysicalPrinterCollection::is_selected(PhysicalPrinterCollection::ConstIterator it, const std::string& preset_name) const
{
return m_idx_selected == size_t(it - m_printers.begin()) &&
m_selected_preset == preset_name;
}
namespace PresetUtils {
const VendorProfile::PrinterModel* system_printer_model(const Preset &preset)
{
const VendorProfile::PrinterModel *out = nullptr;
if (preset.vendor != nullptr) {
auto *printer_model = preset.config.opt<ConfigOptionString>("printer_model");
if (printer_model != nullptr && ! printer_model->value.empty()) {
auto it = std::find_if(preset.vendor->models.begin(), preset.vendor->models.end(), [printer_model](const VendorProfile::PrinterModel &pm) { return pm.id == printer_model->value; });
if (it != preset.vendor->models.end())
out = &(*it);
}
}
return out;
}
std::string system_printer_bed_model(const Preset& preset)
{
std::string out;
const VendorProfile::PrinterModel* pm = PresetUtils::system_printer_model(preset);
if (pm != nullptr && !pm->bed_model.empty()) {
out = Slic3r::data_dir() + "/vendor/" + preset.vendor->id + "/" + pm->bed_model;
if (!boost::filesystem::exists(boost::filesystem::path(out)))
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_model;
}
return out;
}
std::string system_printer_bed_texture(const Preset& preset)
{
std::string out;
const VendorProfile::PrinterModel* pm = PresetUtils::system_printer_model(preset);
if (pm != nullptr && !pm->bed_texture.empty()) {
out = Slic3r::data_dir() + "/vendor/" + preset.vendor->id + "/" + pm->bed_texture;
if (!boost::filesystem::exists(boost::filesystem::path(out)))
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_texture;
}
return out;
}
std::string system_printer_hotend_model(const Preset& preset)
{
std::string out;
const VendorProfile::PrinterModel* pm = PresetUtils::system_printer_model(preset);
if (pm != nullptr && !pm->hotend_model.empty()) {
out = Slic3r::data_dir() + "/vendor/" + preset.vendor->id + "/" + pm->hotend_model;
if (!boost::filesystem::exists(boost::filesystem::path(out)))
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->hotend_model;
}
if (out.empty() ||!boost::filesystem::exists(boost::filesystem::path(out)))
out = Slic3r::resources_dir() + "/profiles/hotend.stl";
return out;
}
} // namespace PresetUtils
} // namespace Slic3r