diff --git a/CMakeLists.txt b/CMakeLists.txt index ba264c8c3..9d6d754e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -344,6 +344,10 @@ if (NOT GLEW_FOUND) endif () include_directories(${GLEW_INCLUDE_DIRS}) +# Find the Cereal serialization library +add_library(cereal INTERFACE) +target_include_directories(cereal INTERFACE include) + # l10n set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization") add_custom_target(pot diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 5bc33c896..1c468607e 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -88,6 +88,7 @@ if (MSVC) dep_libcurl dep_wxwidgets dep_gtest + dep_cereal dep_nlopt # dep_qhull # Experimental dep_zlib # on Windows we still need zlib @@ -102,6 +103,7 @@ else() dep_libcurl dep_wxwidgets dep_gtest + dep_cereal dep_nlopt dep_qhull dep_libigl diff --git a/deps/deps-unix-common.cmake b/deps/deps-unix-common.cmake index c44a6ec20..6d9d6fd75 100644 --- a/deps/deps-unix-common.cmake +++ b/deps/deps-unix-common.cmake @@ -19,6 +19,16 @@ ExternalProject_Add(dep_gtest CMAKE_ARGS -DBUILD_GMOCK=OFF ${DEP_CMAKE_OPTS} -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local ) +ExternalProject_Add(dep_cereal + EXCLUDE_FROM_ALL 1 + URL "https://github.com/USCiLab/cereal/archive/v1.2.2.tar.gz" +# URL_HASH SHA256=c6dd7a5701fff8ad5ebb45a3dc8e757e61d52658de3918e38bab233e7fd3b4ae + CMAKE_ARGS + -DJUST_INSTALL_CEREAL=on + -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local + ${DEP_CMAKE_OPTS} +) + ExternalProject_Add(dep_nlopt EXCLUDE_FROM_ALL 1 URL "https://github.com/stevengj/nlopt/archive/v2.5.0.tar.gz" diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index d7daf8425..2595f94d8 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -115,6 +115,20 @@ if (${DEP_DEBUG}) endif () +ExternalProject_Add(dep_cereal + EXCLUDE_FROM_ALL 1 + URL "https://github.com/USCiLab/cereal/archive/v1.2.2.tar.gz" +# URL_HASH SHA256=c6dd7a5701fff8ad5ebb45a3dc8e757e61d52658de3918e38bab233e7fd3b4ae + CMAKE_GENERATOR "${DEP_MSVC_GEN}" + CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" + CMAKE_ARGS + -DJUST_INSTALL_CEREAL=on + "-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local" + BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj + INSTALL_COMMAND "" +) + + ExternalProject_Add(dep_nlopt EXCLUDE_FROM_ALL 1 URL "https://github.com/stevengj/nlopt/archive/v2.5.0.tar.gz" diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index dc52257aa..e1423b1e1 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -189,6 +189,7 @@ target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIBNE target_link_libraries(libslic3r libnest2d admesh + cereal libigl miniz boost_libs diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 0738b77c6..794beff21 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -209,6 +209,51 @@ std::vector ConfigOptionDef::cli_args(const std::string &key) const return args; } +ConfigOption* ConfigOptionDef::create_empty_option() const +{ + switch (this->type) { + case coFloat: return new ConfigOptionFloat(); + case coFloats: return new ConfigOptionFloats(); + case coInt: return new ConfigOptionInt(); + case coInts: return new ConfigOptionInts(); + case coString: return new ConfigOptionString(); + case coStrings: return new ConfigOptionStrings(); + case coPercent: return new ConfigOptionPercent(); + case coPercents: return new ConfigOptionPercents(); + case coFloatOrPercent: return new ConfigOptionFloatOrPercent(); + case coPoint: return new ConfigOptionPoint(); + case coPoints: return new ConfigOptionPoints(); + case coPoint3: return new ConfigOptionPoint3(); +// case coPoint3s: return new ConfigOptionPoint3s(); + case coBool: return new ConfigOptionBool(); + case coBools: return new ConfigOptionBools(); + case coEnum: return new ConfigOptionEnumGeneric(this->enum_keys_map); + default: throw std::runtime_error(std::string("Unknown option type for option ") + this->label); + } +} + +ConfigOption* ConfigOptionDef::create_default_option() const +{ + if (this->default_value) + return (this->default_value->type() == coEnum) ? + // Special case: For a DynamicConfig, convert a templated enum to a generic enum. + new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt()) : + this->default_value->clone(); + return this->create_empty_option(); +} + +// Assignment of the serialization IDs is not thread safe. The Defs shall be initialized from the main thread! +ConfigOptionDef* ConfigDef::add(const t_config_option_key &opt_key, ConfigOptionType type) +{ + static size_t serialization_key_ordinal_last = 0; + ConfigOptionDef *opt = &this->options[opt_key]; + opt->opt_key = opt_key; + opt->type = type; + opt->serialization_key_ordinal = ++ serialization_key_ordinal_last; + this->by_serialization_key_ordinal[opt->serialization_key_ordinal] = opt; + return opt; +} + std::string ConfigOptionDef::nocli = "~~~noCLI"; std::ostream& ConfigDef::print_cli_help(std::ostream& out, bool show_defaults, std::function filter) const @@ -358,7 +403,7 @@ t_config_option_keys ConfigBase::equal(const ConfigBase &other) const return equal; } -std::string ConfigBase::serialize(const t_config_option_key &opt_key) const +std::string ConfigBase::opt_serialize(const t_config_option_key &opt_key) const { const ConfigOption* opt = this->option(opt_key); assert(opt != nullptr); @@ -469,7 +514,7 @@ void ConfigBase::setenv_() const for (size_t i = 0; i < envname.size(); ++i) envname[i] = (envname[i] <= 'z' && envname[i] >= 'a') ? envname[i]-('a'-'A') : envname[i]; - boost::nowide::setenv(envname.c_str(), this->serialize(*it).c_str(), 1); + boost::nowide::setenv(envname.c_str(), this->opt_serialize(*it).c_str(), 1); } } @@ -593,16 +638,16 @@ void ConfigBase::save(const std::string &file) const c.open(file, std::ios::out | std::ios::trunc); c << "# " << Slic3r::header_slic3r_generated() << std::endl; for (const std::string &opt_key : this->keys()) - c << opt_key << " = " << this->serialize(opt_key) << std::endl; + c << opt_key << " = " << this->opt_serialize(opt_key) << std::endl; c.close(); } bool DynamicConfig::operator==(const DynamicConfig &rhs) const { - t_options_map::const_iterator it1 = this->options.begin(); - t_options_map::const_iterator it1_end = this->options.end(); - t_options_map::const_iterator it2 = rhs.options.begin(); - t_options_map::const_iterator it2_end = rhs.options.end(); + auto it1 = this->options.begin(); + auto it1_end = this->options.end(); + auto it2 = rhs.options.begin(); + auto it2_end = rhs.options.end(); for (; it1 != it1_end && it2 != it2_end; ++ it1, ++ it2) if (it1->first != it2->first || *it1->second != *it2->second) // key or value differ @@ -612,10 +657,10 @@ bool DynamicConfig::operator==(const DynamicConfig &rhs) const ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool create) { - t_options_map::iterator it = options.find(opt_key); + auto it = options.find(opt_key); if (it != options.end()) // Option was found. - return it->second; + return it->second.get(); if (! create) // Option was not found and a new option shall not be created. return nullptr; @@ -628,34 +673,8 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre // throw std::runtime_error(std::string("Invalid option name: ") + opt_key); // Let the parent decide what to do if the opt_key is not defined by this->def(). return nullptr; - ConfigOption *opt = nullptr; - if (optdef->default_value) { - opt = (optdef->default_value->type() == coEnum) ? - // Special case: For a DynamicConfig, convert a templated enum to a generic enum. - new ConfigOptionEnumGeneric(optdef->enum_keys_map, optdef->default_value->getInt()) : - optdef->default_value->clone(); - } else { - switch (optdef->type) { - case coFloat: opt = new ConfigOptionFloat(); break; - case coFloats: opt = new ConfigOptionFloats(); break; - case coInt: opt = new ConfigOptionInt(); break; - case coInts: opt = new ConfigOptionInts(); break; - case coString: opt = new ConfigOptionString(); break; - case coStrings: opt = new ConfigOptionStrings(); break; - case coPercent: opt = new ConfigOptionPercent(); break; - case coPercents: opt = new ConfigOptionPercents(); break; - case coFloatOrPercent: opt = new ConfigOptionFloatOrPercent(); break; - case coPoint: opt = new ConfigOptionPoint(); break; - case coPoints: opt = new ConfigOptionPoints(); break; - case coPoint3: opt = new ConfigOptionPoint3(); break; - // case coPoint3s: opt = new ConfigOptionPoint3s(); break; - case coBool: opt = new ConfigOptionBool(); break; - case coBools: opt = new ConfigOptionBools(); break; - case coEnum: opt = new ConfigOptionEnumGeneric(optdef->enum_keys_map); break; - default: throw std::runtime_error(std::string("Unknown option type for option ") + opt_key); - } - } - this->options[opt_key] = opt; + ConfigOption *opt = optdef->create_default_option(); + this->options.insert(it, std::make_pair(opt_key, opt)); return opt; } @@ -802,3 +821,63 @@ t_config_option_keys StaticConfig::keys() const } } + +CEREAL_REGISTER_TYPE(Slic3r::ConfigOption) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVectorBase) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloat) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloats) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionInt) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionInts) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionString) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionStrings) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercent) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercents) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloatOrPercent) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoint) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoints) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoint3) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionBool) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionBools) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionEnumGeneric) +CEREAL_REGISTER_TYPE(Slic3r::ConfigBase) +CEREAL_REGISTER_TYPE(Slic3r::DynamicConfig) + +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionVectorBase) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle, Slic3r::ConfigOptionFloat) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionFloats) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle, Slic3r::ConfigOptionInt) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionInts) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle, Slic3r::ConfigOptionString) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionStrings) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionFloat, Slic3r::ConfigOptionPercent) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionFloats, Slic3r::ConfigOptionPercents) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionPercent, Slic3r::ConfigOptionFloatOrPercent) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle, Slic3r::ConfigOptionPoint) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionPoints) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle, Slic3r::ConfigOptionPoint3) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle, Slic3r::ConfigOptionBool) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionBools) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionInt, Slic3r::ConfigOptionEnumGeneric) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigBase, Slic3r::DynamicConfig) diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index ee4bc4e46..7b3c5c73a 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -18,6 +18,12 @@ #include #include +#include +#include +#include +#include +#include + namespace Slic3r { // Name of the configuration option. @@ -152,6 +158,10 @@ public: bool operator==(const T &rhs) const { return this->value == rhs; } bool operator!=(const T &rhs) const { return this->value != rhs; } + +private: + friend class cereal::access; + template void serialize(Archive & ar) { ar(this->value); } }; // Value of a vector valued option (bools, ints, floats, strings, points) @@ -290,6 +300,10 @@ public: bool operator==(const std::vector &rhs) const { return this->values == rhs; } bool operator!=(const std::vector &rhs) const { return this->values != rhs; } + +private: + friend class cereal::access; + template void serialize(Archive & ar) { ar(this->values); } }; class ConfigOptionFloat : public ConfigOptionSingle @@ -324,6 +338,10 @@ public: this->set(opt); return *this; } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; class ConfigOptionFloats : public ConfigOptionVector @@ -382,6 +400,10 @@ public: this->set(opt); return *this; } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; class ConfigOptionInt : public ConfigOptionSingle @@ -418,6 +440,10 @@ public: this->set(opt); return *this; } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; class ConfigOptionInts : public ConfigOptionVector @@ -468,6 +494,10 @@ public: } return true; } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; class ConfigOptionString : public ConfigOptionSingle @@ -492,6 +522,10 @@ public: UNUSED(append); return unescape_string_cstyle(str, this->value); } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; // semicolon-separated strings @@ -526,6 +560,10 @@ public: this->values.clear(); return unescape_strings_cstyle(str, this->values); } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; class ConfigOptionPercent : public ConfigOptionFloat @@ -558,6 +596,10 @@ public: iss >> this->value; return !iss.fail(); } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class(this)); } }; class ConfigOptionPercents : public ConfigOptionFloats @@ -612,6 +654,10 @@ public: } return true; } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class(this)); } }; class ConfigOptionFloatOrPercent : public ConfigOptionPercent @@ -661,6 +707,10 @@ public: iss >> this->value; return !iss.fail(); } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class(this), percent); } }; class ConfigOptionPoint : public ConfigOptionSingle @@ -691,6 +741,10 @@ public: return sscanf(str.data(), " %lf , %lf %c", &this->value(0), &this->value(1), &dummy) == 2 || sscanf(str.data(), " %lf x %lf %c", &this->value(0), &this->value(1), &dummy) == 2; } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(this->value.x(), this->value.y()); } }; class ConfigOptionPoints : public ConfigOptionVector @@ -750,8 +804,21 @@ public: } return true; } -}; +private: + friend class cereal::access; + template void save(Archive& archive) const { + size_t cnt = this->values.size(); + archive(cnt); + archive.saveBinary((const char*)this->values.data(), sizeof(Vec2d) * cnt); + } + template void load(Archive& archive) { + size_t cnt; + archive(cnt); + this->values.assign(cnt, Vec2d()); + archive.loadBinary((char*)this->values.data(), sizeof(Vec2d) * cnt); + } +}; class ConfigOptionPoint3 : public ConfigOptionSingle { @@ -783,6 +850,10 @@ public: return sscanf(str.data(), " %lf , %lf , %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 2 || sscanf(str.data(), " %lf x %lf x %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 2; } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(this->value.x(), this->value.y(), this->value.z()); } }; class ConfigOptionBool : public ConfigOptionSingle @@ -809,6 +880,10 @@ public: this->value = (str.compare("1") == 0); return true; } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; class ConfigOptionBools : public ConfigOptionVector @@ -864,6 +939,10 @@ public: } return true; } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; // Map from an enum integer value to an enum name. @@ -1002,19 +1081,73 @@ public: this->value = it->second; return true; } + +private: + friend class cereal::access; + template void serialize(Archive& ar) { ar(cereal::base_class(this)); } }; // Definition of a configuration value for the purpose of GUI presentation, editing, value mapping and config file handling. class ConfigOptionDef { public: + // Identifier of this option. It is stored here so that it is accessible through the by_serialization_key_ordinal map. + t_config_option_key opt_key; // What type? bool, int, string etc. ConfigOptionType type = coNone; // Default value of this option. The default value object is owned by ConfigDef, it is released in its destructor. Slic3r::clonable_ptr default_value; - void set_default_value(const ConfigOption* ptr) { this->default_value = Slic3r::clonable_ptr(ptr); } - template - const T* get_default_value() const { return static_cast(this->default_value.get()); } + void set_default_value(const ConfigOption* ptr) { this->default_value = Slic3r::clonable_ptr(ptr); } + template const T* get_default_value() const { return static_cast(this->default_value.get()); } + + // Create an empty option to be used as a base for deserialization of DynamicConfig. + ConfigOption* create_empty_option() const; + // Create a default option to be inserted into a DynamicConfig. + ConfigOption* create_default_option() const; + + template ConfigOption* load_option_from_archive(Archive &archive) const { + switch (this->type) { + case coFloat: { auto opt = new ConfigOptionFloat(); archive(*opt); return opt; } + case coFloats: { auto opt = new ConfigOptionFloats(); archive(*opt); return opt; } + case coInt: { auto opt = new ConfigOptionInt(); archive(*opt); return opt; } + case coInts: { auto opt = new ConfigOptionInts(); archive(*opt); return opt; } + case coString: { auto opt = new ConfigOptionString(); archive(*opt); return opt; } + case coStrings: { auto opt = new ConfigOptionStrings(); archive(*opt); return opt; } + case coPercent: { auto opt = new ConfigOptionPercent(); archive(*opt); return opt; } + case coPercents: { auto opt = new ConfigOptionPercents(); archive(*opt); return opt; } + case coFloatOrPercent: { auto opt = new ConfigOptionFloatOrPercent(); archive(*opt); return opt; } + case coPoint: { auto opt = new ConfigOptionPoint(); archive(*opt); return opt; } + case coPoints: { auto opt = new ConfigOptionPoints(); archive(*opt); return opt; } + case coPoint3: { auto opt = new ConfigOptionPoint3(); archive(*opt); return opt; } + case coBool: { auto opt = new ConfigOptionBool(); archive(*opt); return opt; } + case coBools: { auto opt = new ConfigOptionBools(); archive(*opt); return opt; } + case coEnum: { auto opt = new ConfigOptionEnumGeneric(this->enum_keys_map); archive(*opt); return opt; } + default: throw std::runtime_error(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key); + } + } + + template ConfigOption* save_option_to_archive(Archive &archive, const ConfigOption *opt) const { + switch (this->type) { + case coFloat: archive(*static_cast(opt)); break; + case coFloats: archive(*static_cast(opt)); break; + case coInt: archive(*static_cast(opt)); break; + case coInts: archive(*static_cast(opt)); break; + case coString: archive(*static_cast(opt)); break; + case coStrings: archive(*static_cast(opt)); break; + case coPercent: archive(*static_cast(opt)); break; + case coPercents: archive(*static_cast(opt)); break; + case coFloatOrPercent: archive(*static_cast(opt)); break; + case coPoint: archive(*static_cast(opt)); break; + case coPoints: archive(*static_cast(opt)); break; + case coPoint3: archive(*static_cast(opt)); break; + case coBool: archive(*static_cast(opt)); break; + case coBools: archive(*static_cast(opt)); break; + case coEnum: archive(*static_cast(opt)); break; + default: throw std::runtime_error(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") + this->opt_key); + } + // Make the compiler happy, shut up the warnings. + return nullptr; + } // Usually empty. // Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection, @@ -1084,6 +1217,9 @@ public: return false; } + // 0 is an invalid key. + size_t serialization_key_ordinal = 0; + // Returns the alternative CLI arguments for the given option. // If there are no cli arguments defined, use the key and replace underscores with dashes. std::vector cli_args(const std::string &key) const; @@ -1103,7 +1239,8 @@ typedef std::map t_optiondef_map; class ConfigDef { public: - t_optiondef_map options; + t_optiondef_map options; + std::map by_serialization_key_ordinal; bool has(const t_config_option_key &opt_key) const { return this->options.count(opt_key) > 0; } const ConfigOptionDef* get(const t_config_option_key &opt_key) const { @@ -1124,11 +1261,7 @@ public: std::function filter = [](const ConfigOptionDef &){ return true; }) const; protected: - ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type) { - ConfigOptionDef* opt = &this->options[opt_key]; - opt->type = type; - return opt; - } + ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type); }; // An abstract configuration store. @@ -1197,7 +1330,7 @@ public: bool equals(const ConfigBase &other) const { return this->diff(other).empty(); } t_config_option_keys diff(const ConfigBase &other) const; t_config_option_keys equal(const ConfigBase &other) const; - std::string serialize(const t_config_option_key &opt_key) const; + std::string opt_serialize(const t_config_option_key &opt_key) const; // Set a configuration value from a string, it will call an overridable handle_legacy() // to resolve renamed and removed configuration keys. bool set_deserialize(const t_config_option_key &opt_key, const std::string &str, bool append = false); @@ -1235,7 +1368,7 @@ public: assert(this->def() == nullptr || this->def() == rhs.def()); this->clear(); for (const auto &kvp : rhs.options) - this->options[kvp.first] = kvp.second->clone(); + this->options[kvp.first].reset(kvp.second->clone()); return *this; } @@ -1258,15 +1391,13 @@ public: for (const auto &kvp : rhs.options) { auto it = this->options.find(kvp.first); if (it == this->options.end()) - this->options[kvp.first] = kvp.second->clone(); + this->options[kvp.first].reset(kvp.second->clone()); else { assert(it->second->type() == kvp.second->type()); if (it->second->type() == kvp.second->type()) *it->second = *kvp.second; - else { - delete it->second; - it->second = kvp.second->clone(); - } + else + it->second.reset(kvp.second->clone()); } } return *this; @@ -1277,14 +1408,13 @@ public: DynamicConfig& operator+=(DynamicConfig &&rhs) { assert(this->def() == nullptr || this->def() == rhs.def()); - for (const auto &kvp : rhs.options) { + for (auto &kvp : rhs.options) { auto it = this->options.find(kvp.first); if (it == this->options.end()) { - this->options[kvp.first] = kvp.second; + this->options.insert(std::make_pair(kvp.first, std::move(kvp.second))); } else { assert(it->second->type() == kvp.second->type()); - delete it->second; - it->second = kvp.second; + it->second = std::move(kvp.second); } } rhs.options.clear(); @@ -1301,8 +1431,6 @@ public: void clear() { - for (auto &opt : this->options) - delete opt.second; this->options.clear(); } @@ -1311,7 +1439,6 @@ public: auto it = this->options.find(opt_key); if (it == this->options.end()) return false; - delete it->second; this->options.erase(it); return true; } @@ -1336,11 +1463,10 @@ public: { auto it = this->options.find(opt_key); if (it == this->options.end()) { - this->options[opt_key] = opt; + this->options[opt_key].reset(opt); return true; } else { - delete it->second; - it->second = opt; + it->second.reset(opt); return false; } } @@ -1370,12 +1496,15 @@ public: void read_cli(const std::vector &tokens, t_config_option_keys* extra, t_config_option_keys* keys = nullptr); bool read_cli(int argc, char** argv, t_config_option_keys* extra, t_config_option_keys* keys = nullptr); - typedef std::map t_options_map; - t_options_map::const_iterator cbegin() const { return options.cbegin(); } - t_options_map::const_iterator cend() const { return options.cend(); } + std::map>::const_iterator cbegin() const { return options.cbegin(); } + std::map>::const_iterator cend() const { return options.cend(); } + size_t size() const { return options.size(); } private: - t_options_map options; + std::map> options; + + friend class cereal::access; + template void serialize(Archive &ar) { ar(options); } }; /// Configuration store with a static definition of configuration values. diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 4793586e3..0cb0af119 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -2060,7 +2060,7 @@ namespace Slic3r { for (const std::string &key : config.keys()) if (key != "compatible_printers") - out += "; " + key + " = " + config.serialize(key) + "\n"; + out += "; " + key + " = " + config.opt_serialize(key) + "\n"; if (!out.empty()) { @@ -2094,7 +2094,7 @@ namespace Slic3r { // stores object's config data for (const std::string& key : obj->config.keys()) { - stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << obj->config.serialize(key) << "\"/>\n"; + stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << obj->config.opt_serialize(key) << "\"/>\n"; } for (const ModelVolume* volume : obj_metadata.second.object->volumes) @@ -2124,7 +2124,7 @@ namespace Slic3r { // stores volume's config data for (const std::string& key : volume->config.keys()) { - stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.serialize(key) << "\"/>\n"; + stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.opt_serialize(key) << "\"/>\n"; } stream << " \n"; diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index a33d21c9f..0228bd906 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -873,7 +873,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) std::string str_config = "\n"; for (const std::string &key : config->keys()) if (key != "compatible_printers") - str_config += "; " + key + " = " + config->serialize(key) + "\n"; + str_config += "; " + key + " = " + config->opt_serialize(key) + "\n"; stream << "" << xml_escape(str_config) << "\n"; } @@ -885,7 +885,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) for (const auto &attr : material.second->attributes) stream << " " << attr.second << "\n"; for (const std::string &key : material.second->config.keys()) - stream << " " << material.second->config.serialize(key) << "\n"; + stream << " " << material.second->config.opt_serialize(key) << "\n"; stream << " \n"; } std::string instances; @@ -893,7 +893,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) ModelObject *object = model->objects[object_id]; stream << " \n"; for (const std::string &key : object->config.keys()) - stream << " " << object->config.serialize(key) << "\n"; + stream << " " << object->config.opt_serialize(key) << "\n"; if (!object->name.empty()) stream << " " << xml_escape(object->name) << "\n"; const std::vector &layer_height_profile = object->layer_height_profile; @@ -952,7 +952,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) else stream << " material_id() << "\">\n"; for (const std::string &key : volume->config.keys()) - stream << " " << volume->config.serialize(key) << "\n"; + stream << " " << volume->config.opt_serialize(key) << "\n"; if (!volume->name.empty()) stream << " " << xml_escape(volume->name) << "\n"; if (volume->is_modifier()) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index c42669de0..f868aa079 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1749,7 +1749,7 @@ void GCode::append_full_config(const Print& print, std::string& str) const StaticPrintConfig *cfg = configs[i]; for (const std::string &key : cfg->keys()) if (key != "compatible_printers") - str += "; " + key + " = " + cfg->serialize(key) + "\n"; + str += "; " + key + " = " + cfg->opt_serialize(key) + "\n"; } const DynamicConfig &full_config = print.placeholder_parser().config(); for (const char *key : { diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index c1d92c6bb..a8160867a 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -128,7 +128,7 @@ void Layer::make_perimeters() && config.external_perimeter_speed == other_config.external_perimeter_speed && config.gap_fill_speed == other_config.gap_fill_speed && config.overhangs == other_config.overhangs - && config.serialize("perimeter_extrusion_width").compare(other_config.serialize("perimeter_extrusion_width")) == 0 + && config.opt_serialize("perimeter_extrusion_width") == other_config.opt_serialize("perimeter_extrusion_width") && config.thin_walls == other_config.thin_walls && config.external_perimeters_first == other_config.external_perimeters_first) { layerms.push_back(other_layerm); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 89e21934a..97787fff6 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -406,10 +406,13 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionEnum(ipRectilinear)); def = this->add("bottom_fill_pattern", coEnum); - *def = *def_top_fill_pattern; def->label = L("Bottom fill pattern"); + def->category = L("Infill"); def->tooltip = L("Fill pattern for bottom infill. This only affects the bottom external visible layer, and not its adjacent solid shells."); def->cli = "bottom-fill-pattern|external-fill-pattern|solid-fill-pattern"; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values = def_top_fill_pattern->enum_values; + def->aliases = def_top_fill_pattern->aliases; def->set_default_value(new ConfigOptionEnum(ipRectilinear)); def = this->add("external_perimeter_extrusion_width", coFloatOrPercent); @@ -3194,3 +3197,6 @@ void DynamicPrintAndCLIConfig::handle_legacy(t_config_option_key &opt_key, std:: } } + +CEREAL_REGISTER_TYPE(Slic3r::DynamicPrintConfig) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::DynamicConfig, Slic3r::DynamicPrintConfig) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 248b89e32..8dbce9ea3 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1190,6 +1190,8 @@ private: this->options.insert(cli_actions_config_def.options.begin(), cli_actions_config_def.options.end()); this->options.insert(cli_transform_config_def.options.begin(), cli_transform_config_def.options.end()); this->options.insert(cli_misc_config_def.options.begin(), cli_misc_config_def.options.end()); + for (const auto &kvp : this->options) + this->by_serialization_key_ordinal[kvp.second.serialization_key_ordinal] = &kvp.second; } // Do not release the default values, they are handled by print_config_def & cli_actions_config_def / cli_transform_config_def / cli_misc_config_def. ~PrintAndCLIConfigDef() { this->options.clear(); } @@ -1199,4 +1201,38 @@ private: } // namespace Slic3r +// Serialization through the Cereal library +namespace cereal { + // Let cereal know that there are load / save non-member functions declared for DynamicPrintConfig, ignore serialize / load / save from parent class DynamicConfig. + template struct specialize {}; + + template void load(Archive& archive, Slic3r::DynamicPrintConfig &config) + { + size_t cnt; + archive(cnt); + config.clear(); + for (size_t i = 0; i < cnt; ++ i) { + size_t serialization_key_ordinal; + archive(serialization_key_ordinal); + assert(serialization_key_ordinal > 0); + auto it = Slic3r::print_config_def.by_serialization_key_ordinal.find(serialization_key_ordinal); + assert(it != Slic3r::print_config_def.by_serialization_key_ordinal.end()); + config.set_key_value(it->second->opt_key, it->second->load_option_from_archive(archive)); + } + } + + template void save(Archive& archive, const Slic3r::DynamicPrintConfig &config) + { + size_t cnt = config.size(); + archive(cnt); + for (auto it = config.cbegin(); it != config.cend(); ++it) { + const Slic3r::ConfigOptionDef* optdef = Slic3r::print_config_def.get(it->first); + assert(optdef != nullptr); + assert(optdef->serialization_key_ordinal > 0); + archive(optdef->serialization_key_ordinal); + optdef->save_option_to_archive(archive, it->second.get()); + } + } +} + #endif diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 570e23baa..1867a8186 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -159,7 +159,7 @@ endif () add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) -target_link_libraries(libslic3r_gui libslic3r avrdude imgui ${GLEW_LIBRARIES}) +target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES}) if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) endif () diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index b28cb2eda..00c1f8168 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -1366,7 +1366,7 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst continue; c << std::endl << "[" << presets->section_name() << ":" << preset.name << "]" << std::endl; for (const std::string &opt_key : preset.config.keys()) - c << opt_key << " = " << preset.config.serialize(opt_key) << std::endl; + c << opt_key << " = " << preset.config.opt_serialize(opt_key) << std::endl; } }