diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 03ce0ad40..b6b496ae9 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -233,6 +233,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const case coFloats: return new ConfigOptionFloatsNullable(); case coInts: return new ConfigOptionIntsNullable(); case coPercents: return new ConfigOptionPercentsNullable(); + case coFloatsOrPercents: return new ConfigOptionFloatsOrPercentsNullable(); case coBools: return new ConfigOptionBoolsNullable(); default: throw Slic3r::RuntimeError(std::string("Unknown option type for nullable option ") + this->label); } @@ -247,6 +248,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const case coPercent: return new ConfigOptionPercent(); case coPercents: return new ConfigOptionPercents(); case coFloatOrPercent: return new ConfigOptionFloatOrPercent(); + case coFloatsOrPercents: return new ConfigOptionFloatsOrPercents(); case coPoint: return new ConfigOptionPoint(); case coPoints: return new ConfigOptionPoints(); case coPoint3: return new ConfigOptionPoint3(); @@ -950,6 +952,8 @@ CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercent) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercents) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercentsNullable) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloatOrPercent) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloatsOrPercents) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloatsOrPercentsNullable) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoint) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoints) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoint3) @@ -984,6 +988,8 @@ CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionFloat, Slic3r::ConfigOp 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::ConfigOptionVector, Slic3r::ConfigOptionFloatsOrPercents) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionFloatsOrPercentsNullable) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle, Slic3r::ConfigOptionPoint) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionPoints) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle, Slic3r::ConfigOptionPoint3) diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 6ef6f143c..8cdacd59f 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -86,6 +86,8 @@ enum ConfigOptionType { coPercents = coPercent + coVectorType, // a fraction or an absolute value coFloatOrPercent = 5, + // vector of the above + coFloatsOrPercents = coFloatOrPercent + coVectorType, // single 2d point (Point2f). Currently not used. coPoint = 6, // vector of 2d points (Point2f). Currently used for the definition of the print bed and for the extruder offsets. @@ -889,6 +891,143 @@ private: template void serialize(Archive &ar) { ar(cereal::base_class(this), percent); } }; + +struct FloatOrPercent +{ + double value; + bool percent; + +private: + friend class cereal::access; + template void serialize(Archive & ar) { ar(this->value); ar(this->percent); } +}; + +inline bool operator==(const FloatOrPercent &l, const FloatOrPercent &r) +{ + return l.value == r.value && l.percent == r.percent; +} + +inline bool operator!=(const FloatOrPercent& l, const FloatOrPercent& r) +{ + return !(l == r); +} + +template +class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVector +{ +public: + ConfigOptionFloatsOrPercentsTempl() : ConfigOptionVector() {} + explicit ConfigOptionFloatsOrPercentsTempl(size_t n, FloatOrPercent value) : ConfigOptionVector(n, value) {} + explicit ConfigOptionFloatsOrPercentsTempl(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} + explicit ConfigOptionFloatsOrPercentsTempl(const std::vector &vec) : ConfigOptionVector(vec) {} + explicit ConfigOptionFloatsOrPercentsTempl(std::vector &&vec) : ConfigOptionVector(std::move(vec)) {} + + static ConfigOptionType static_type() { return coFloatsOrPercents; } + ConfigOptionType type() const override { return static_type(); } + ConfigOption* clone() const override { return new ConfigOptionFloatsOrPercentsTempl(*this); } + bool operator==(const ConfigOptionFloatsOrPercentsTempl &rhs) const { return vectors_equal(this->values, rhs.values); } + bool operator==(const ConfigOption &rhs) const override { + if (rhs.type() != this->type()) + throw Slic3r::RuntimeError("ConfigOptionFloatsOrPercentsTempl: Comparing incompatible types"); + assert(dynamic_cast*>(&rhs)); + return vectors_equal(this->values, static_cast*>(&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 FloatOrPercent nil_value() { return { std::numeric_limits::quiet_NaN(), false }; } + // A scalar is nil, or all values of a vector are nil. + bool is_nil() const override { for (auto v : this->values) if (! std::isnan(v.value)) return false; return true; } + bool is_nil(size_t idx) const override { return std::isnan(this->values[idx].value); } + + std::string serialize() const override + { + std::ostringstream ss; + for (const FloatOrPercent &v : this->values) { + if (&v != &this->values.front()) + ss << ","; + serialize_single_value(ss, v); + } + return ss.str(); + } + + std::vector vserialize() const override + { + std::vector vv; + vv.reserve(this->values.size()); + for (const FloatOrPercent &v : this->values) { + std::ostringstream ss; + serialize_single_value(ss, v); + 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, ',')) { + boost::trim(item_str); + if (item_str == "nil") { + if (NULLABLE) + this->values.push_back(nil_value()); + else + throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object"); + } else { + bool percent = item_str.find_first_of("%") != std::string::npos; + std::istringstream iss(item_str); + double value; + iss >> value; + this->values.push_back({ value, percent }); + } + } + return true; + } + + ConfigOptionFloatsOrPercentsTempl& operator=(const ConfigOption *opt) + { + this->set(opt); + return *this; + } + +protected: + void serialize_single_value(std::ostringstream &ss, const FloatOrPercent &v) const { + if (std::isfinite(v.value)) { + ss << v.value; + if (v.percent) + ss << "%"; + } else if (std::isnan(v.value)) { + if (NULLABLE) + ss << "nil"; + else + throw Slic3r::RuntimeError("Serializing NaN"); + } else + throw Slic3r::RuntimeError("Serializing invalid number"); + } + static bool vectors_equal(const std::vector &v1, const std::vector &v2) { + if (NULLABLE) { + if (v1.size() != v2.size()) + return false; + for (auto it1 = v1.begin(), it2 = v2.begin(); it1 != v1.end(); ++ it1, ++ it2) + if (! ((std::isnan(it1->value) && std::isnan(it2->value)) || *it1 == *it2)) + return false; + return true; + } else + // Not supporting nullable values, the default vector compare is cheaper. + return v1 == v2; + } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } +}; + +using ConfigOptionFloatsOrPercents = ConfigOptionFloatsOrPercentsTempl; +using ConfigOptionFloatsOrPercentsNullable = ConfigOptionFloatsOrPercentsTempl; + class ConfigOptionPoint : public ConfigOptionSingle { public: