diff --git a/src/boost/nowide/utf8_codecvt.hpp b/src/boost/nowide/utf8_codecvt.hpp index cc5046fc8..877c9f0e0 100644 --- a/src/boost/nowide/utf8_codecvt.hpp +++ b/src/boost/nowide/utf8_codecvt.hpp @@ -102,7 +102,7 @@ protected: #ifndef BOOST_NOWIDE_DO_LENGTH_MBSTATE_CONST return from - save_from; #else - return save_max - max; + return int(save_max - max); #endif } diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 9d0649a1f..577698071 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -211,25 +211,35 @@ std::vector ConfigOptionDef::cli_args(const std::string &key) const 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); - } + if (this->nullable) { + switch (this->type) { + case coFloats: return new ConfigOptionFloatsNullable(); + case coInts: return new ConfigOptionIntsNullable(); + case coPercents: return new ConfigOptionPercentsNullable(); + case coBools: return new ConfigOptionBoolsNullable(); + default: throw std::runtime_error(std::string("Unknown option type for nullable option ") + this->label); + } + } else { + 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 @@ -254,6 +264,13 @@ ConfigOptionDef* ConfigDef::add(const t_config_option_key &opt_key, ConfigOption return opt; } +ConfigOptionDef* ConfigDef::add_nullable(const t_config_option_key &opt_key, ConfigOptionType type) +{ + ConfigOptionDef *def = this->add(opt_key, type); + def->nullable = true; + return def; +} + std::string ConfigOptionDef::nocli = "~~~noCLI"; std::ostream& ConfigDef::print_cli_help(std::ostream& out, bool show_defaults, std::function filter) const @@ -642,6 +659,17 @@ void ConfigBase::save(const std::string &file) const c.close(); } +// Set all the nullable values to nils. +void ConfigBase::null_nullables() +{ + for (const std::string &opt_key : this->keys()) { + ConfigOption *opt = this->optptr(opt_key, false); + assert(opt != nullptr); + if (opt->nullable()) + opt->deserialize("nil"); + } +} + bool DynamicConfig::operator==(const DynamicConfig &rhs) const { auto it1 = this->options.begin(); @@ -655,6 +683,19 @@ bool DynamicConfig::operator==(const DynamicConfig &rhs) const return it1 == it1_end && it2 == it2_end; } +// Remove options with all nil values, those are optional and it does not help to hold them. +size_t DynamicConfig::remove_nil_options() +{ + size_t cnt_removed = 0; + for (auto it = options.begin(); it != options.end();) + if (it->second->is_nil()) { + it = options.erase(it); + ++ cnt_removed; + } else + ++ it; + return cnt_removed; +} + ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool create) { auto it = options.find(opt_key); @@ -838,18 +879,22 @@ CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloat) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloats) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloatsNullable) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionInt) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionInts) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionIntsNullable) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionString) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionStrings) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercent) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercents) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercentsNullable) 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::ConfigOptionBoolsNullable) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionEnumGeneric) CEREAL_REGISTER_TYPE(Slic3r::ConfigBase) CEREAL_REGISTER_TYPE(Slic3r::DynamicConfig) @@ -868,17 +913,21 @@ CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::Con 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::ConfigOptionVector, Slic3r::ConfigOptionFloatsNullable) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle, Slic3r::ConfigOptionInt) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionInts) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionIntsNullable) 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::ConfigOptionFloats, Slic3r::ConfigOptionPercentsNullable) 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::ConfigOptionVector, Slic3r::ConfigOptionBoolsNullable) 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 7d96f6cf3..d8759c6eb 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -15,6 +15,7 @@ #include "clonable_ptr.hpp" #include "Point.hpp" +#include #include #include @@ -124,6 +125,10 @@ public: bool operator!=(const ConfigOption &rhs) const { return ! (*this == rhs); } bool is_scalar() const { return (int(this->type()) & int(coVectorType)) == 0; } bool is_vector() const { return ! this->is_scalar(); } + // If this option is nullable, then it may have its value or values set to nil. + virtual bool nullable() const { return false; } + // A scalar is nil, or all values of a vector are nil. + virtual bool is_nil() const { return false; } }; typedef ConfigOption* ConfigOptionPtr; @@ -345,26 +350,35 @@ private: template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; -class ConfigOptionFloats : public ConfigOptionVector +template +class ConfigOptionFloatsTempl : public ConfigOptionVector { public: - ConfigOptionFloats() : ConfigOptionVector() {} - explicit ConfigOptionFloats(size_t n, double value) : ConfigOptionVector(n, value) {} - explicit ConfigOptionFloats(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} - explicit ConfigOptionFloats(const std::vector &vec) : ConfigOptionVector(vec) {} - explicit ConfigOptionFloats(std::vector &&vec) : ConfigOptionVector(std::move(vec)) {} + ConfigOptionFloatsTempl() : ConfigOptionVector() {} + explicit ConfigOptionFloatsTempl(size_t n, double value) : ConfigOptionVector(n, value) {} + explicit ConfigOptionFloatsTempl(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} + explicit ConfigOptionFloatsTempl(const std::vector &vec) : ConfigOptionVector(vec) {} + explicit ConfigOptionFloatsTempl(std::vector &&vec) : ConfigOptionVector(std::move(vec)) {} static ConfigOptionType static_type() { return coFloats; } ConfigOptionType type() const override { return static_type(); } - ConfigOption* clone() const override { return new ConfigOptionFloats(*this); } - bool operator==(const ConfigOptionFloats &rhs) const { return this->values == rhs.values; } + ConfigOption* clone() const override { return new ConfigOptionFloatsTempl(*this); } + bool operator==(const ConfigOptionFloatsTempl &rhs) const { return this->values == rhs.values; } + // Could a special "nil" value be stored inside the vector, indicating undefined value? + bool nullable() const override { return NULLABLE; } + // Special "nil" value to be stored into the vector if this->supports_nil(). + static double nil_value() { return std::numeric_limits::quiet_NaN(); } + // A scalar is nil, or all values of a vector are nil. + virtual bool is_nil() const override { for (auto v : this->values) if (! std::isnan(v)) return false; return true; } + bool is_nil(size_t idx) const { return std::isnan(v->values[idx]); } std::string serialize() const override { std::ostringstream ss; - for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { - if (it - this->values.begin() != 0) ss << ","; - ss << *it; + for (const double &v : this->values) { + if (&v != &this->values.front()) + ss << ","; + serialize_single_value(ss, v); } return ss.str(); } @@ -373,14 +387,14 @@ public: { std::vector vv; vv.reserve(this->values.size()); - for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { + for (const double v : this->values) { std::ostringstream ss; - ss << *it; + serialize_single_value(ss, v); vv.push_back(ss.str()); } return vv; } - + bool deserialize(const std::string &str, bool append = false) override { if (! append) @@ -388,25 +402,49 @@ public: std::istringstream is(str); std::string item_str; while (std::getline(is, item_str, ',')) { - std::istringstream iss(item_str); - double value; - iss >> value; - this->values.push_back(value); + boost::trim(item_str); + if (item_str == "nil") { + if (NULLABLE) + this->values.push_back(nil_value()); + else + std::runtime_error("Deserializing nil into a non-nullable object"); + } else { + std::istringstream iss(item_str); + double value; + iss >> value; + this->values.push_back(value); + } } return true; } - ConfigOptionFloats& operator=(const ConfigOption *opt) + ConfigOptionFloatsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; } +protected: + void serialize_single_value(std::ostringstream &ss, const double v) const { + if (std::isfinite(v)) + ss << v; + else if (std::isnan(v)) { + if (NULLABLE) + ss << "nil"; + else + std::runtime_error("Serializing NaN"); + } else + std::runtime_error("Serializing invalid number"); + } + private: friend class cereal::access; template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; +using ConfigOptionFloats = ConfigOptionFloatsTempl; +using ConfigOptionFloatsNullable = ConfigOptionFloatsTempl; + class ConfigOptionInt : public ConfigOptionSingle { public: @@ -447,35 +485,45 @@ private: template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; -class ConfigOptionInts : public ConfigOptionVector +template +class ConfigOptionIntsTempl : public ConfigOptionVector { public: - ConfigOptionInts() : ConfigOptionVector() {} - explicit ConfigOptionInts(size_t n, int value) : ConfigOptionVector(n, value) {} - explicit ConfigOptionInts(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} + ConfigOptionIntsTempl() : ConfigOptionVector() {} + explicit ConfigOptionIntsTempl(size_t n, int value) : ConfigOptionVector(n, value) {} + explicit ConfigOptionIntsTempl(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} static ConfigOptionType static_type() { return coInts; } ConfigOptionType type() const override { return static_type(); } - ConfigOption* clone() const override { return new ConfigOptionInts(*this); } - ConfigOptionInts& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionInts &rhs) const { return this->values == rhs.values; } + ConfigOption* clone() const override { return new ConfigOptionIntsTempl(*this); } + ConfigOptionIntsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; } + bool operator==(const ConfigOptionIntsTempl &rhs) const { return this->values == rhs.values; } + // Could a special "nil" value be stored inside the vector, indicating undefined value? + bool nullable() const override { return NULLABLE; } + // Special "nil" value to be stored into the vector if this->supports_nil(). + static int nil_value() { return std::numeric_limits::max(); } + // A scalar is nil, or all values of a vector are nil. + virtual bool is_nil() const override { for (auto v : this->values) if (v != nil_value()) return false; return true; } + bool is_nil(size_t idx) const { return v->values[idx] == nil_value(); } - std::string serialize() const override { + std::string serialize() const override + { std::ostringstream ss; - for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { - if (it - this->values.begin() != 0) ss << ","; - ss << *it; + for (const int &v : this->values) { + if (&v != &this->values.front()) + ss << ","; + serialize_single_value(ss, v); } return ss.str(); } - std::vector vserialize() const override + std::vector vserialize() const override { std::vector vv; vv.reserve(this->values.size()); - for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { + for (const int v : this->values) { std::ostringstream ss; - ss << *it; + serialize_single_value(ss, v); vv.push_back(ss.str()); } return vv; @@ -488,19 +536,40 @@ public: std::istringstream is(str); std::string item_str; while (std::getline(is, item_str, ',')) { - std::istringstream iss(item_str); - int value; - iss >> value; - this->values.push_back(value); + boost::trim(item_str); + if (item_str == "nil") { + if (NULLABLE) + this->values.push_back(nil_value()); + else + std::runtime_error("Deserializing nil into a non-nullable object"); + } else { + std::istringstream iss(item_str); + int value; + iss >> value; + this->values.push_back(value); + } } return true; } private: + void serialize_single_value(std::ostringstream &ss, const int v) const { + if (v == nil_value()) { + if (NULLABLE) + ss << "nil"; + else + std::runtime_error("Serializing NaN"); + } else + ss << v; + } + friend class cereal::access; template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; +using ConfigOptionInts = ConfigOptionIntsTempl; +using ConfigOptionIntsNullable = ConfigOptionIntsTempl; + class ConfigOptionString : public ConfigOptionSingle { public: @@ -603,64 +672,61 @@ private: template void serialize(Archive &ar) { ar(cereal::base_class(this)); } }; -class ConfigOptionPercents : public ConfigOptionFloats +template +class ConfigOptionPercentsTempl : public ConfigOptionFloatsTempl { public: - ConfigOptionPercents() : ConfigOptionFloats() {} - explicit ConfigOptionPercents(size_t n, double value) : ConfigOptionFloats(n, value) {} - explicit ConfigOptionPercents(std::initializer_list il) : ConfigOptionFloats(std::move(il)) {} + ConfigOptionPercentsTempl() : ConfigOptionFloatsTempl() {} + explicit ConfigOptionPercentsTempl(size_t n, double value) : ConfigOptionFloatsTempl(n, value) {} + explicit ConfigOptionPercentsTempl(std::initializer_list il) : ConfigOptionFloatsTempl(std::move(il)) {} + explicit ConfigOptionPercentsTempl(const std::vector& vec) : ConfigOptionFloatsTempl(vec) {} + explicit ConfigOptionPercentsTempl(std::vector&& vec) : ConfigOptionFloatsTempl(std::move(vec)) {} static ConfigOptionType static_type() { return coPercents; } ConfigOptionType type() const override { return static_type(); } - ConfigOption* clone() const override { return new ConfigOptionPercents(*this); } - ConfigOptionPercents& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionPercents &rhs) const { return this->values == rhs.values; } + ConfigOption* clone() const override { return new ConfigOptionPercentsTempl(*this); } + ConfigOptionPercentsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; } + bool operator==(const ConfigOptionPercentsTempl &rhs) const { return this->values == rhs.values; } std::string serialize() const override { std::ostringstream ss; - for (const auto &v : this->values) { - if (&v != &this->values.front()) ss << ","; - ss << v << "%"; + for (const double &v : this->values) { + if (&v != &this->values.front()) + ss << ","; + this->serialize_single_value(ss, v); + if (! std::isnan(v)) + ss << "%"; } std::string str = ss.str(); return str; } - + std::vector vserialize() const override { std::vector vv; vv.reserve(this->values.size()); - for (const auto v : this->values) { + for (const double v : this->values) { std::ostringstream ss; - ss << v; - std::string sout = ss.str() + "%"; - vv.push_back(sout); + this->serialize_single_value(ss, v); + if (! std::isnan(v)) + ss << "%"; + vv.push_back(ss.str()); } return vv; } - bool deserialize(const std::string &str, bool append = false) override - { - if (! append) - this->values.clear(); - std::istringstream is(str); - std::string item_str; - while (std::getline(is, item_str, ',')) { - std::istringstream iss(item_str); - double value; - // don't try to parse the trailing % since it's optional - iss >> value; - this->values.push_back(value); - } - return true; - } + // The float's deserialize function shall ignore the trailing optional %. + // bool deserialize(const std::string &str, bool append = false) override; private: friend class cereal::access; - template void serialize(Archive &ar) { ar(cereal::base_class(this)); } + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; +using ConfigOptionPercents = ConfigOptionPercentsTempl; +using ConfigOptionPercentsNullable = ConfigOptionPercentsTempl; + class ConfigOptionFloatOrPercent : public ConfigOptionPercent { public: @@ -887,18 +953,28 @@ private: template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; -class ConfigOptionBools : public ConfigOptionVector +template +class ConfigOptionBoolsTempl : public ConfigOptionVector { public: - ConfigOptionBools() : ConfigOptionVector() {} - explicit ConfigOptionBools(size_t n, bool value) : ConfigOptionVector(n, (unsigned char)value) {} - explicit ConfigOptionBools(std::initializer_list il) { values.reserve(il.size()); for (bool b : il) values.emplace_back((unsigned char)b); } + ConfigOptionBoolsTempl() : ConfigOptionVector() {} + explicit ConfigOptionBoolsTempl(size_t n, bool value) : ConfigOptionVector(n, (unsigned char)value) {} + explicit ConfigOptionBoolsTempl(std::initializer_list il) { values.reserve(il.size()); for (bool b : il) values.emplace_back((unsigned char)b); } + explicit ConfigOptionBoolsTempl(const std::vector& vec) : ConfigOptionVector(vec) {} + explicit ConfigOptionBoolsTempl(std::vector&& vec) : ConfigOptionVector(std::move(vec)) {} static ConfigOptionType static_type() { return coBools; } ConfigOptionType type() const override { return static_type(); } - ConfigOption* clone() const override { return new ConfigOptionBools(*this); } - ConfigOptionBools& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionBools &rhs) const { return this->values == rhs.values; } + ConfigOption* clone() const override { return new ConfigOptionBoolsTempl(*this); } + ConfigOptionBoolsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; } + bool operator==(const ConfigOptionBoolsTempl &rhs) const { return this->values == rhs.values; } + // Could a special "nil" value be stored inside the vector, indicating undefined value? + bool nullable() const override { return NULLABLE; } + // Special "nil" value to be stored into the vector if this->supports_nil(). + static unsigned char nil_value() { return std::numeric_limits::max(); } + // A scalar is nil, or all values of a vector are nil. + virtual bool is_nil() const override { for (auto v : this->values) if (v != nil_value()) return false; return true; } + bool is_nil(size_t idx) const { return v->values[idx] == nil_value(); } bool& get_at(size_t i) { assert(! this->values.empty()); @@ -911,19 +987,20 @@ public: std::string serialize() const override { std::ostringstream ss; - for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { - if (it - this->values.begin() != 0) ss << ","; - ss << (*it ? "1" : "0"); - } + for (const unsigned char &v : this->values) { + if (&v != &this->values.front()) + ss << ","; + this->serialize_single_value(ss, v); + } return ss.str(); } std::vector vserialize() const override { std::vector vv; - for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { - std::ostringstream ss; - ss << (*it ? "1" : "0"); + for (const unsigned char v : this->values) { + std::ostringstream ss; + this->serialize_single_value(ss, v); vv.push_back(ss.str()); } return vv; @@ -936,16 +1013,37 @@ public: std::istringstream is(str); std::string item_str; while (std::getline(is, item_str, ',')) { - this->values.push_back(item_str.compare("1") == 0); + boost::trim(item_str); + if (item_str == "nil") { + if (NULLABLE) + this->values.push_back(nil_value()); + else + std::runtime_error("Deserializing nil into a non-nullable object"); + } else + this->values.push_back(item_str.compare("1") == 0); } return true; } +protected: + void serialize_single_value(std::ostringstream &ss, const unsigned char v) const { + if (v == nil_value()) { + if (NULLABLE) + ss << "nil"; + else + std::runtime_error("Serializing NaN"); + } else + ss << (v ? "1" : "0"); + } + private: friend class cereal::access; template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; +using ConfigOptionBools = ConfigOptionBoolsTempl; +using ConfigOptionBoolsNullable = ConfigOptionBoolsTempl; + // Map from an enum integer value to an enum name. typedef std::vector t_config_enum_names; // Map from an enum name to an enum integer value. @@ -1096,6 +1194,8 @@ public: t_config_option_key opt_key; // What type? bool, int, string etc. ConfigOptionType type = coNone; + // If a type is nullable, then it accepts a "nil" value (scalar) or "nil" values (vector). + bool nullable = false; // 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); } @@ -1107,45 +1207,65 @@ public: 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); - } + if (this->nullable) { + switch (this->type) { + case coFloats: { auto opt = new ConfigOptionFloatsNullable(); archive(*opt); return opt; } + case coInts: { auto opt = new ConfigOptionIntsNullable(); archive(*opt); return opt; } + case coPercents: { auto opt = new ConfigOptionPercentsNullable();archive(*opt); return opt; } + case coBools: { auto opt = new ConfigOptionBoolsNullable(); archive(*opt); return opt; } + default: throw std::runtime_error(std::string("ConfigOptionDef::load_option_from_archive(): Unknown nullable option type for option ") + this->opt_key); + } + } else { + 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); - } + if (this->nullable) { + switch (this->type) { + case coFloats: archive(*static_cast(opt)); break; + case coInts: archive(*static_cast(opt)); break; + case coPercents: archive(*static_cast(opt));break; + case coBools: archive(*static_cast(opt)); break; + default: throw std::runtime_error(std::string("ConfigOptionDef::save_option_to_archive(): Unknown nullable option type for option ") + this->opt_key); + } + } else { + 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; } @@ -1263,6 +1383,7 @@ public: protected: ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type); + ConfigOptionDef* add_nullable(const t_config_option_key &opt_key, ConfigOptionType type); }; // An abstract configuration store. @@ -1347,6 +1468,9 @@ public: void load(const boost::property_tree::ptree &tree); void save(const std::string &file) const; + // Set all the nullable values to nils. + void null_nullables(); + private: // Set a configuration value from a string. bool set_deserialize_raw(const t_config_option_key &opt_key_src, const std::string &str, bool append); @@ -1444,6 +1568,9 @@ public: return true; } + // Remove options with all nil values, those are optional and it does not help to hold them. + size_t remove_nil_options(); + // Allow DynamicConfig to be instantiated on ints own without a definition. // If the definition is not defined, the method requiring the definition will throw NoDefinitionException. const ConfigDef* def() const override { return nullptr; }; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index a9b8a827f..af2479e5a 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1783,47 +1783,7 @@ std::string Print::output_filename(const std::string &filename_base) const DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders(); return this->PrintBase::output_filename(m_config.output_filename_format.value, ".gcode", filename_base, &config); } -/* -// Shorten the dhms time by removing the seconds, rounding the dhm to full minutes -// and removing spaces. -static std::string short_time(const std::string &time) -{ - // Parse the dhms time format. - int days = 0; - int hours = 0; - int minutes = 0; - int seconds = 0; - if (time.find('d') != std::string::npos) - ::sscanf(time.c_str(), "%dd %dh %dm %ds", &days, &hours, &minutes, &seconds); - else if (time.find('h') != std::string::npos) - ::sscanf(time.c_str(), "%dh %dm %ds", &hours, &minutes, &seconds); - else if (time.find('m') != std::string::npos) - ::sscanf(time.c_str(), "%dm %ds", &minutes, &seconds); - else if (time.find('s') != std::string::npos) - ::sscanf(time.c_str(), "%ds", &seconds); - // Round to full minutes. - if (days + hours + minutes > 0 && seconds >= 30) { - if (++ minutes == 60) { - minutes = 0; - if (++ hours == 24) { - hours = 0; - ++ days; - } - } - } - // Format the dhm time. - char buffer[64]; - if (days > 0) - ::sprintf(buffer, "%dd%dh%dm", days, hours, minutes); - else if (hours > 0) - ::sprintf(buffer, "%dh%dm", hours, minutes); - else if (minutes > 0) - ::sprintf(buffer, "%dm", minutes); - else - ::sprintf(buffer, "%ds", seconds); - return buffer; -} -*/ + DynamicConfig PrintStatistics::config() const { DynamicConfig config; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 7d451a4cb..e147e1087 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2228,6 +2228,30 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(0)); + + // Declare retract values for filament profile, overriding the printer's extruder profile. + for (const char *opt_key : { + // floats + "retract_length", "retract_lift", "retract_lift_above", "retract_lift_below", "retract_speed", "deretract_speed", "retract_restart_extra", "retract_before_travel", + // bools + "retract_layer_change", "wipe", + // percents + "retract_before_wipe"}) { + auto it_opt = options.find(opt_key); + assert(it_opt != options.end()); + def = this->add_nullable(std::string("filament_") + opt_key, it_opt->second.type); + def->label = it_opt->second.label; + def->full_label = it_opt->second.full_label; + def->tooltip = it_opt->second.tooltip; + def->sidetext = it_opt->second.sidetext; + def->mode = it_opt->second.mode; + switch (def->type) { + case coFloats : def->set_default_value(new ConfigOptionFloatsNullable (static_cast(it_opt->second.default_value.get())->values)); break; + case coPercents : def->set_default_value(new ConfigOptionPercentsNullable(static_cast(it_opt->second.default_value.get())->values)); break; + case coBools : def->set_default_value(new ConfigOptionBoolsNullable (static_cast(it_opt->second.default_value.get())->values)); break; + default: assert(false); + } + } } void PrintConfigDef::init_sla_params() @@ -2987,7 +3011,7 @@ std::string FullPrintConfig::validate() } case coFloats: case coPercents: - for (double v : static_cast(opt)->values) + for (double v : static_cast*>(opt)->values) if (v < optdef->min || v > optdef->max) { out_of_range = true; break; @@ -3000,7 +3024,7 @@ std::string FullPrintConfig::validate() break; } case coInts: - for (int v : static_cast(opt)->values) + for (int v : static_cast*>(opt)->values) if (v < optdef->min || v > optdef->max) { out_of_range = true; break; diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 3d467d7f8..26e6c4e1e 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -400,6 +400,10 @@ const std::vector& Preset::filament_options() "temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", "start_filament_gcode", "end_filament_gcode", + // Retract overrides + "filament_retract_length", "filament_retract_lift", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_speed", "filament_deretract_speed", "filament_retract_restart_extra", "filament_retract_before_travel", + "filament_retract_layer_change", "filament_wipe", "filament_retract_before_wipe", + // Profile compatibility "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits" }; return s_opts; diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index 00c1f8168..7c9f7af4e 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -72,6 +72,8 @@ PresetBundle::PresetBundle() : this->filaments.default_preset().config.option("filament_settings_id", true)->values = { "" }; this->filaments.default_preset().compatible_printers_condition(); this->filaments.default_preset().inherits(); + // Set all the nullable values to nils. + this->filaments.default_preset().config.null_nullables(); this->sla_materials.default_preset().config.optptr("sla_material_settings_id", true); this->sla_materials.default_preset().compatible_printers_condition();