Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorYuSanka <yusanka@gmail.com>2019-07-31 14:05:47 +0300
committerYuSanka <yusanka@gmail.com>2019-07-31 14:05:47 +0300
commit3bade450b8a4d1189138bb3cbed325a479fe6b94 (patch)
treece2cbe63accbf59226ec785790e83130b640504a /src
parentaeb29b11849e2e3e9a0282c289a9a7d165bfdbc8 (diff)
parent7c2e199472524c40bbd3f50650c2a0c7345bd4dd (diff)
Merge remote-tracking branch 'origin/ys_overrides'
Diffstat (limited to 'src')
-rw-r--r--src/boost/nowide/utf8_codecvt.hpp2
-rw-r--r--src/libslic3r/Config.cpp87
-rw-r--r--src/libslic3r/Config.hpp459
-rw-r--r--src/libslic3r/GCode.cpp23
-rw-r--r--src/libslic3r/PlaceholderParser.cpp28
-rw-r--r--src/libslic3r/PlaceholderParser.hpp9
-rw-r--r--src/libslic3r/Print.cpp166
-rw-r--r--src/libslic3r/Print.hpp9
-rw-r--r--src/libslic3r/PrintBase.hpp10
-rw-r--r--src/libslic3r/PrintConfig.cpp47
-rw-r--r--src/libslic3r/PrintConfig.hpp8
-rw-r--r--src/libslic3r/SLAPrint.cpp17
-rw-r--r--src/libslic3r/SLAPrint.hpp2
-rw-r--r--src/slic3r/GUI/Field.cpp123
-rw-r--r--src/slic3r/GUI/Field.hpp22
-rw-r--r--src/slic3r/GUI/GUI.cpp7
-rw-r--r--src/slic3r/GUI/OptionsGroup.cpp54
-rw-r--r--src/slic3r/GUI/OptionsGroup.hpp3
-rw-r--r--src/slic3r/GUI/Preset.cpp4
-rw-r--r--src/slic3r/GUI/PresetBundle.cpp2
-rw-r--r--src/slic3r/GUI/Tab.cpp111
-rw-r--r--src/slic3r/GUI/Tab.hpp5
22 files changed, 873 insertions, 325 deletions
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<std::string> 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<bool(const ConfigOptionDef &)> 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<Slic3r::Vec2d>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector<unsigned char>)
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<unsigned char>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<double>, Slic3r::ConfigOptionFloat)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<double>, Slic3r::ConfigOptionFloats)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<double>, Slic3r::ConfigOptionFloatsNullable)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<int>, Slic3r::ConfigOptionInt)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<int>, Slic3r::ConfigOptionInts)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<int>, Slic3r::ConfigOptionIntsNullable)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<std::string>, Slic3r::ConfigOptionString)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<std::string>, 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::Vec2d>, Slic3r::ConfigOptionPoint)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<Slic3r::Vec2d>, Slic3r::ConfigOptionPoints)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<Slic3r::Vec3d>, Slic3r::ConfigOptionPoint3)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<bool>, Slic3r::ConfigOptionBool)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<unsigned char>, Slic3r::ConfigOptionBools)
+CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<unsigned char>, 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..2850f1cb9 100644
--- a/src/libslic3r/Config.hpp
+++ b/src/libslic3r/Config.hpp
@@ -15,6 +15,7 @@
#include "clonable_ptr.hpp"
#include "Point.hpp"
+#include <boost/algorithm/string/trim.hpp>
#include <boost/format.hpp>
#include <boost/property_tree/ptree.hpp>
@@ -124,6 +125,23 @@ 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; }
+ // Is this option overridden by another option?
+ // An option overrides another option if it is not nil and not equal.
+ virtual bool overriden_by(const ConfigOption *rhs) const {
+ assert(! this->nullable() && ! rhs->nullable());
+ return *this != *rhs;
+ }
+ // Apply an override option, possibly a nullable one.
+ virtual bool apply_override(const ConfigOption *rhs) {
+ if (*this == *rhs)
+ return false;
+ *this = *rhs;
+ return true;
+ }
};
typedef ConfigOption* ConfigOptionPtr;
@@ -183,6 +201,8 @@ public:
virtual size_t size() const = 0;
// Is this vector empty?
virtual bool empty() const = 0;
+ // Is the value nil? That should only be possible if this->nullable().
+ virtual bool is_nil(size_t idx) const = 0;
protected:
// Used to verify type compatibility when assigning to / from a scalar ConfigOption.
@@ -302,6 +322,62 @@ public:
bool operator==(const std::vector<T> &rhs) const { return this->values == rhs; }
bool operator!=(const std::vector<T> &rhs) const { return this->values != rhs; }
+ // Is this option overridden by another option?
+ // An option overrides another option if it is not nil and not equal.
+ bool overriden_by(const ConfigOption *rhs) const override {
+ if (this->nullable())
+ throw std::runtime_error("Cannot override a nullable ConfigOption.");
+ if (rhs->type() != this->type())
+ throw std::runtime_error("ConfigOptionVector.overriden_by() applied to different types.");
+ auto rhs_vec = static_cast<const ConfigOptionVector<T>*>(rhs);
+ if (! rhs->nullable())
+ // Overridding a non-nullable object with another non-nullable object.
+ return this->values != rhs_vec->values;
+ size_t i = 0;
+ size_t cnt = std::min(this->size(), rhs_vec->size());
+ for (; i < cnt; ++ i)
+ if (! rhs_vec->is_nil(i) && this->values[i] != rhs_vec->values[i])
+ return true;
+ for (; i < rhs_vec->size(); ++ i)
+ if (! rhs_vec->is_nil(i))
+ return true;
+ return false;
+ }
+ // Apply an override option, possibly a nullable one.
+ bool apply_override(const ConfigOption *rhs) override {
+ if (this->nullable())
+ throw std::runtime_error("Cannot override a nullable ConfigOption.");
+ if (rhs->type() != this->type())
+ throw std::runtime_error("ConfigOptionVector.apply_override() applied to different types.");
+ auto rhs_vec = static_cast<const ConfigOptionVector<T>*>(rhs);
+ if (! rhs->nullable()) {
+ // Overridding a non-nullable object with another non-nullable object.
+ if (this->values != rhs_vec->values) {
+ this->values = rhs_vec->values;
+ return true;
+ }
+ return false;
+ }
+ size_t i = 0;
+ size_t cnt = std::min(this->size(), rhs_vec->size());
+ bool modified = false;
+ for (; i < cnt; ++ i)
+ if (! rhs_vec->is_nil(i) && this->values[i] != rhs_vec->values[i]) {
+ this->values[i] = rhs_vec->values[i];
+ modified = true;
+ }
+ for (; i < rhs_vec->size(); ++ i)
+ if (! rhs_vec->is_nil(i)) {
+ if (this->values.empty())
+ this->values.resize(i + 1);
+ else
+ this->values.resize(i + 1, this->values.front());
+ this->values[i] = rhs_vec->values[i];
+ modified = true;
+ }
+ return false;
+ }
+
private:
friend class cereal::access;
template<class Archive> void serialize(Archive & ar) { ar(this->values); }
@@ -345,26 +421,41 @@ private:
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<double>>(this)); }
};
-class ConfigOptionFloats : public ConfigOptionVector<double>
+template<bool NULLABLE>
+class ConfigOptionFloatsTempl : public ConfigOptionVector<double>
{
public:
- ConfigOptionFloats() : ConfigOptionVector<double>() {}
- explicit ConfigOptionFloats(size_t n, double value) : ConfigOptionVector<double>(n, value) {}
- explicit ConfigOptionFloats(std::initializer_list<double> il) : ConfigOptionVector<double>(std::move(il)) {}
- explicit ConfigOptionFloats(const std::vector<double> &vec) : ConfigOptionVector<double>(vec) {}
- explicit ConfigOptionFloats(std::vector<double> &&vec) : ConfigOptionVector<double>(std::move(vec)) {}
+ ConfigOptionFloatsTempl() : ConfigOptionVector<double>() {}
+ explicit ConfigOptionFloatsTempl(size_t n, double value) : ConfigOptionVector<double>(n, value) {}
+ explicit ConfigOptionFloatsTempl(std::initializer_list<double> il) : ConfigOptionVector<double>(std::move(il)) {}
+ explicit ConfigOptionFloatsTempl(const std::vector<double> &vec) : ConfigOptionVector<double>(vec) {}
+ explicit ConfigOptionFloatsTempl(std::vector<double> &&vec) : ConfigOptionVector<double>(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 vectors_equal(this->values, rhs.values); }
+ bool operator==(const ConfigOption &rhs) const override {
+ if (rhs.type() != this->type())
+ throw std::runtime_error("ConfigOptionFloatsTempl: Comparing incompatible types");
+ assert(dynamic_cast<const ConfigOptionVector<double>*>(&rhs));
+ return vectors_equal(this->values, static_cast<const ConfigOptionVector<double>*>(&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<double>::quiet_NaN(); }
+ // 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)) return false; return true; }
+ bool is_nil(size_t idx) const override { return std::isnan(this->values[idx]); }
std::string serialize() const override
{
std::ostringstream ss;
- for (std::vector<double>::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 +464,14 @@ public:
{
std::vector<std::string> vv;
vv.reserve(this->values.size());
- for (std::vector<double>::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 +479,61 @@ 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");
+ }
+ static bool vectors_equal(const std::vector<double> &v1, const std::vector<double> &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) && std::isnan(*it2)) || *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<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<double>>(this)); }
};
+using ConfigOptionFloats = ConfigOptionFloatsTempl<false>;
+using ConfigOptionFloatsNullable = ConfigOptionFloatsTempl<true>;
+
class ConfigOptionInt : public ConfigOptionSingle<int>
{
public:
@@ -447,35 +574,45 @@ private:
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<int>>(this)); }
};
-class ConfigOptionInts : public ConfigOptionVector<int>
+template<bool NULLABLE>
+class ConfigOptionIntsTempl : public ConfigOptionVector<int>
{
public:
- ConfigOptionInts() : ConfigOptionVector<int>() {}
- explicit ConfigOptionInts(size_t n, int value) : ConfigOptionVector<int>(n, value) {}
- explicit ConfigOptionInts(std::initializer_list<int> il) : ConfigOptionVector<int>(std::move(il)) {}
+ ConfigOptionIntsTempl() : ConfigOptionVector<int>() {}
+ explicit ConfigOptionIntsTempl(size_t n, int value) : ConfigOptionVector<int>(n, value) {}
+ explicit ConfigOptionIntsTempl(std::initializer_list<int> il) : ConfigOptionVector<int>(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<int>::max(); }
+ // A scalar is nil, or all values of a vector are nil.
+ 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 override { return this->values[idx] == nil_value(); }
- std::string serialize() const override {
+ std::string serialize() const override
+ {
std::ostringstream ss;
- for (std::vector<int>::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<std::string> vserialize() const override
+ std::vector<std::string> vserialize() const override
{
std::vector<std::string> vv;
vv.reserve(this->values.size());
- for (std::vector<int>::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 +625,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<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<int>>(this)); }
};
+using ConfigOptionInts = ConfigOptionIntsTempl<false>;
+using ConfigOptionIntsNullable = ConfigOptionIntsTempl<true>;
+
class ConfigOptionString : public ConfigOptionSingle<std::string>
{
public:
@@ -544,6 +702,7 @@ public:
ConfigOption* clone() const override { return new ConfigOptionStrings(*this); }
ConfigOptionStrings& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionStrings &rhs) const { return this->values == rhs.values; }
+ bool is_nil(size_t idx) const override { return false; }
std::string serialize() const override
{
@@ -603,64 +762,61 @@ private:
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionFloat>(this)); }
};
-class ConfigOptionPercents : public ConfigOptionFloats
+template<bool NULLABLE>
+class ConfigOptionPercentsTempl : public ConfigOptionFloatsTempl<NULLABLE>
{
public:
- ConfigOptionPercents() : ConfigOptionFloats() {}
- explicit ConfigOptionPercents(size_t n, double value) : ConfigOptionFloats(n, value) {}
- explicit ConfigOptionPercents(std::initializer_list<double> il) : ConfigOptionFloats(std::move(il)) {}
+ ConfigOptionPercentsTempl() : ConfigOptionFloatsTempl<NULLABLE>() {}
+ explicit ConfigOptionPercentsTempl(size_t n, double value) : ConfigOptionFloatsTempl<NULLABLE>(n, value) {}
+ explicit ConfigOptionPercentsTempl(std::initializer_list<double> il) : ConfigOptionFloatsTempl<NULLABLE>(std::move(il)) {}
+ explicit ConfigOptionPercentsTempl(const std::vector<double>& vec) : ConfigOptionFloatsTempl<NULLABLE>(vec) {}
+ explicit ConfigOptionPercentsTempl(std::vector<double>&& vec) : ConfigOptionFloatsTempl<NULLABLE>(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<std::string> vserialize() const override
{
std::vector<std::string> 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<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionFloats>(this)); }
+ template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionFloatsTempl<NULLABLE>>(this)); }
};
+using ConfigOptionPercents = ConfigOptionPercentsTempl<false>;
+using ConfigOptionPercentsNullable = ConfigOptionPercentsTempl<true>;
+
class ConfigOptionFloatOrPercent : public ConfigOptionPercent
{
public:
@@ -761,6 +917,7 @@ public:
ConfigOption* clone() const override { return new ConfigOptionPoints(*this); }
ConfigOptionPoints& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPoints &rhs) const { return this->values == rhs.values; }
+ bool is_nil(size_t idx) const override { return false; }
std::string serialize() const override
{
@@ -887,18 +1044,29 @@ private:
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<bool>>(this)); }
};
-class ConfigOptionBools : public ConfigOptionVector<unsigned char>
+template<bool NULLABLE>
+class ConfigOptionBoolsTempl : public ConfigOptionVector<unsigned char>
{
public:
- ConfigOptionBools() : ConfigOptionVector<unsigned char>() {}
- explicit ConfigOptionBools(size_t n, bool value) : ConfigOptionVector<unsigned char>(n, (unsigned char)value) {}
- explicit ConfigOptionBools(std::initializer_list<bool> il) { values.reserve(il.size()); for (bool b : il) values.emplace_back((unsigned char)b); }
+ ConfigOptionBoolsTempl() : ConfigOptionVector<unsigned char>() {}
+ explicit ConfigOptionBoolsTempl(size_t n, bool value) : ConfigOptionVector<unsigned char>(n, (unsigned char)value) {}
+ explicit ConfigOptionBoolsTempl(std::initializer_list<bool> il) { values.reserve(il.size()); for (bool b : il) values.emplace_back((unsigned char)b); }
+ explicit ConfigOptionBoolsTempl(std::initializer_list<unsigned char> il) { values.reserve(il.size()); for (unsigned char b : il) values.emplace_back(b); }
+ explicit ConfigOptionBoolsTempl(const std::vector<unsigned char>& vec) : ConfigOptionVector<unsigned char>(vec) {}
+ explicit ConfigOptionBoolsTempl(std::vector<unsigned char>&& vec) : ConfigOptionVector<unsigned char>(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<unsigned char>::max(); }
+ // A scalar is nil, or all values of a vector are nil.
+ 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 override { return this->values[idx] == nil_value(); }
bool& get_at(size_t i) {
assert(! this->values.empty());
@@ -911,19 +1079,20 @@ public:
std::string serialize() const override
{
std::ostringstream ss;
- for (std::vector<unsigned char>::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<std::string> vserialize() const override
{
std::vector<std::string> vv;
- for (std::vector<unsigned char>::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 +1105,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<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<unsigned char>>(this)); }
};
+using ConfigOptionBools = ConfigOptionBoolsTempl<false>;
+using ConfigOptionBoolsNullable = ConfigOptionBoolsTempl<true>;
+
// Map from an enum integer value to an enum name.
typedef std::vector<std::string> t_config_enum_names;
// Map from an enum name to an enum integer value.
@@ -1096,6 +1286,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<const ConfigOption> default_value;
void set_default_value(const ConfigOption* ptr) { this->default_value = Slic3r::clonable_ptr<const ConfigOption>(ptr); }
@@ -1107,45 +1299,65 @@ public:
ConfigOption* create_default_option() const;
template<class Archive> 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<class Archive> ConfigOption* save_option_to_archive(Archive &archive, const ConfigOption *opt) const {
- switch (this->type) {
- case coFloat: archive(*static_cast<const ConfigOptionFloat*>(opt)); break;
- case coFloats: archive(*static_cast<const ConfigOptionFloats*>(opt)); break;
- case coInt: archive(*static_cast<const ConfigOptionInt*>(opt)); break;
- case coInts: archive(*static_cast<const ConfigOptionInts*>(opt)); break;
- case coString: archive(*static_cast<const ConfigOptionString*>(opt)); break;
- case coStrings: archive(*static_cast<const ConfigOptionStrings*>(opt)); break;
- case coPercent: archive(*static_cast<const ConfigOptionPercent*>(opt)); break;
- case coPercents: archive(*static_cast<const ConfigOptionPercents*>(opt)); break;
- case coFloatOrPercent: archive(*static_cast<const ConfigOptionFloatOrPercent*>(opt)); break;
- case coPoint: archive(*static_cast<const ConfigOptionPoint*>(opt)); break;
- case coPoints: archive(*static_cast<const ConfigOptionPoints*>(opt)); break;
- case coPoint3: archive(*static_cast<const ConfigOptionPoint3*>(opt)); break;
- case coBool: archive(*static_cast<const ConfigOptionBool*>(opt)); break;
- case coBools: archive(*static_cast<const ConfigOptionBools*>(opt)); break;
- case coEnum: archive(*static_cast<const ConfigOptionEnumGeneric*>(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<const ConfigOptionFloatsNullable*>(opt)); break;
+ case coInts: archive(*static_cast<const ConfigOptionIntsNullable*>(opt)); break;
+ case coPercents: archive(*static_cast<const ConfigOptionPercentsNullable*>(opt));break;
+ case coBools: archive(*static_cast<const ConfigOptionBoolsNullable*>(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<const ConfigOptionFloat*>(opt)); break;
+ case coFloats: archive(*static_cast<const ConfigOptionFloats*>(opt)); break;
+ case coInt: archive(*static_cast<const ConfigOptionInt*>(opt)); break;
+ case coInts: archive(*static_cast<const ConfigOptionInts*>(opt)); break;
+ case coString: archive(*static_cast<const ConfigOptionString*>(opt)); break;
+ case coStrings: archive(*static_cast<const ConfigOptionStrings*>(opt)); break;
+ case coPercent: archive(*static_cast<const ConfigOptionPercent*>(opt)); break;
+ case coPercents: archive(*static_cast<const ConfigOptionPercents*>(opt)); break;
+ case coFloatOrPercent: archive(*static_cast<const ConfigOptionFloatOrPercent*>(opt)); break;
+ case coPoint: archive(*static_cast<const ConfigOptionPoint*>(opt)); break;
+ case coPoints: archive(*static_cast<const ConfigOptionPoints*>(opt)); break;
+ case coPoint3: archive(*static_cast<const ConfigOptionPoint3*>(opt)); break;
+ case coBool: archive(*static_cast<const ConfigOptionBool*>(opt)); break;
+ case coBools: archive(*static_cast<const ConfigOptionBools*>(opt)); break;
+ case coEnum: archive(*static_cast<const ConfigOptionEnumGeneric*>(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 +1475,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 +1560,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,9 +1660,12 @@ 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; };
+ const ConfigDef* def() const override { return nullptr; }
template<class T> T* opt(const t_config_option_key &opt_key, bool create = false)
{ return dynamic_cast<T*>(this->option(opt_key, create)); }
template<class T> const T* opt(const t_config_option_key &opt_key) const
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 81880831f..bc3730026 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -1829,25 +1829,12 @@ void GCode::apply_print_config(const PrintConfig &print_config)
m_config.apply(print_config);
}
-void GCode::append_full_config(const Print& print, std::string& str)
+void GCode::append_full_config(const Print &print, std::string &str)
{
- const StaticPrintConfig *configs[] = { static_cast<const GCodeConfig*>(&print.config()), &print.default_object_config(), &print.default_region_config() };
- for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++i) {
- const StaticPrintConfig *cfg = configs[i];
- for (const std::string &key : cfg->keys())
- if (key != "compatible_printers")
- str += "; " + key + " = " + cfg->opt_serialize(key) + "\n";
- }
- const DynamicConfig &full_config = print.placeholder_parser().config();
- for (const char *key : {
- "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",
- "compatible_prints_condition_cummulative", "compatible_printers_condition_cummulative", "inherits_cummulative" }) {
- const ConfigOption *opt = full_config.option(key);
- if (opt != nullptr)
- str += std::string("; ") + key + " = " + opt->serialize() + "\n";
- }
+ const DynamicPrintConfig &cfg = print.full_print_config();
+ for (const std::string &key : cfg.keys())
+ if (key != "compatible_prints" && key != "compatible_printers" && ! cfg.option(key)->is_nil())
+ str += "; " + key + " = " + cfg.opt_serialize(key) + "\n";
}
void GCode::set_extruders(const std::vector<unsigned int> &extruder_ids)
diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp
index 2bfe9b745..c3ac22e96 100644
--- a/src/libslic3r/PlaceholderParser.cpp
+++ b/src/libslic3r/PlaceholderParser.cpp
@@ -62,7 +62,7 @@
namespace Slic3r {
-PlaceholderParser::PlaceholderParser()
+PlaceholderParser::PlaceholderParser(const DynamicConfig *external_config) : m_external_config(external_config)
{
this->set("version", std::string(SLIC3R_VERSION));
this->apply_env_variables();
@@ -94,14 +94,6 @@ void PlaceholderParser::update_timestamp(DynamicConfig &config)
config.set_key_value("second", new ConfigOptionInt(timeinfo->tm_sec));
}
-// Ignore this key by the placeholder parser.
-static inline bool placeholder_parser_ignore(const ConfigDef *def, const std::string &opt_key)
-{
- const ConfigOptionDef *opt_def = def->get(opt_key);
- assert(opt_def != nullptr);
- return (opt_def->multiline && boost::ends_with(opt_key, "_gcode")) || opt_key == "post_process";
-}
-
static inline bool opts_equal(const DynamicConfig &config_old, const DynamicConfig &config_new, const std::string &opt_key)
{
const ConfigOption *opt_old = config_old.option(opt_key);
@@ -119,7 +111,7 @@ std::vector<std::string> PlaceholderParser::config_diff(const DynamicPrintConfig
const ConfigDef *def = rhs.def();
std::vector<std::string> diff_keys;
for (const t_config_option_key &opt_key : rhs.keys())
- if (! placeholder_parser_ignore(def, opt_key) && ! opts_equal(m_config, rhs, opt_key))
+ if (! opts_equal(m_config, rhs, opt_key))
diff_keys.emplace_back(opt_key);
return diff_keys;
}
@@ -135,8 +127,6 @@ bool PlaceholderParser::apply_config(const DynamicPrintConfig &rhs)
const ConfigDef *def = rhs.def();
bool modified = false;
for (const t_config_option_key &opt_key : rhs.keys()) {
- if (placeholder_parser_ignore(def, opt_key))
- continue;
if (! opts_equal(m_config, rhs, opt_key)) {
// Store a copy of the config option.
// Convert FloatOrPercent values to floats first.
@@ -155,7 +145,6 @@ bool PlaceholderParser::apply_config(const DynamicPrintConfig &rhs)
void PlaceholderParser::apply_only(const DynamicPrintConfig &rhs, const std::vector<std::string> &keys)
{
for (const t_config_option_key &opt_key : keys) {
- assert(! placeholder_parser_ignore(rhs.def(), opt_key));
// Store a copy of the config option.
// Convert FloatOrPercent values to floats first.
//FIXME there are some ratio_over chains, which end with empty ratio_with.
@@ -167,6 +156,11 @@ void PlaceholderParser::apply_only(const DynamicPrintConfig &rhs, const std::vec
}
}
+void PlaceholderParser::apply_config(DynamicPrintConfig &&rhs)
+{
+ m_config += std::move(rhs);
+}
+
void PlaceholderParser::apply_env_variables()
{
for (char** env = environ; *env; ++ env) {
@@ -608,6 +602,7 @@ namespace client
}
struct MyContext {
+ const DynamicConfig *external_config = nullptr;
const DynamicConfig *config = nullptr;
const DynamicConfig *config_override = nullptr;
size_t current_extruder_id = 0;
@@ -628,6 +623,8 @@ namespace client
opt = config_override->option(opt_key);
if (opt == nullptr)
opt = config->option(opt_key);
+ if (opt == nullptr && external_config != nullptr)
+ opt = external_config->option(opt_key);
return opt;
}
@@ -1255,6 +1252,7 @@ static std::string process_macro(const std::string &templ, client::MyContext &co
std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) const
{
client::MyContext context;
+ context.external_config = this->external_config();
context.config = &this->config();
context.config_override = config_override;
context.current_extruder_id = current_extruder_id;
@@ -1266,8 +1264,8 @@ std::string PlaceholderParser::process(const std::string &templ, unsigned int cu
bool PlaceholderParser::evaluate_boolean_expression(const std::string &templ, const DynamicConfig &config, const DynamicConfig *config_override)
{
client::MyContext context;
- context.config = &config;
- context.config_override = config_override;
+ context.config = &config;
+ context.config_override = config_override;
// Let the macro processor parse just a boolean expression, not the full macro language.
context.just_boolean_expression = true;
return process_macro(templ, context) == "true";
diff --git a/src/libslic3r/PlaceholderParser.hpp b/src/libslic3r/PlaceholderParser.hpp
index 22c790e6b..14fdc5c28 100644
--- a/src/libslic3r/PlaceholderParser.hpp
+++ b/src/libslic3r/PlaceholderParser.hpp
@@ -12,13 +12,14 @@ namespace Slic3r {
class PlaceholderParser
{
public:
- PlaceholderParser();
+ PlaceholderParser(const DynamicConfig *external_config = nullptr);
// Return a list of keys, which should be changed in m_config from rhs.
// This contains keys, which are found in rhs, but not in m_config.
std::vector<std::string> config_diff(const DynamicPrintConfig &rhs);
// Return true if modified.
bool apply_config(const DynamicPrintConfig &config);
+ void apply_config(DynamicPrintConfig &&config);
// To be called on the values returned by PlaceholderParser::config_diff().
// The keys should already be valid.
void apply_only(const DynamicPrintConfig &config, const std::vector<std::string> &keys);
@@ -35,6 +36,8 @@ public:
DynamicConfig& config_writable() { return m_config; }
const DynamicConfig& config() const { return m_config; }
const ConfigOption* option(const std::string &key) const { return m_config.option(key); }
+ // External config is not owned by PlaceholderParser. It has a lowest priority when looking up an option.
+ const DynamicConfig* external_config() const { return m_external_config; }
// Fill in the template using a macro processing language.
// Throws std::runtime_error on syntax or runtime error.
@@ -50,7 +53,9 @@ public:
void update_timestamp() { update_timestamp(m_config); }
private:
- DynamicConfig m_config;
+ // config has a higher priority than external_config when looking up a symbol.
+ DynamicConfig m_config;
+ const DynamicConfig *m_external_config;
};
}
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index 5702f49e3..c423afeb9 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -485,25 +485,82 @@ bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_c
return true;
}
-Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &config_in)
+// Collect diffs of configuration values at various containers,
+// resolve the filament rectract overrides of extruder retract values.
+void Print::config_diffs(
+ const DynamicPrintConfig &new_full_config,
+ t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys &region_diff,
+ t_config_option_keys &full_config_diff,
+ DynamicPrintConfig &placeholder_parser_overrides,
+ DynamicPrintConfig &filament_overrides) const
+{
+ // Collect changes to print config, account for overrides of extruder retract values by filament presets.
+ {
+ const std::vector<std::string> &extruder_retract_keys = print_config_def.extruder_retract_keys();
+ const std::string filament_prefix = "filament_";
+ for (const t_config_option_key &opt_key : m_config.keys()) {
+ const ConfigOption *opt_old = m_config.option(opt_key);
+ assert(opt_old != nullptr);
+ const ConfigOption *opt_new = new_full_config.option(opt_key);
+ // assert(opt_new != nullptr);
+ if (opt_new == nullptr)
+ //FIXME This may happen when executing some test cases.
+ continue;
+ const ConfigOption *opt_new_filament = std::binary_search(extruder_retract_keys.begin(), extruder_retract_keys.end(), opt_key) ? new_full_config.option(filament_prefix + opt_key) : nullptr;
+ if (opt_new_filament != nullptr && ! opt_new_filament->is_nil()) {
+ // An extruder retract override is available at some of the filament presets.
+ if (*opt_old != *opt_new || opt_new->overriden_by(opt_new_filament)) {
+ auto opt_copy = opt_new->clone();
+ opt_copy->apply_override(opt_new_filament);
+ if (*opt_old == *opt_copy)
+ delete opt_copy;
+ else {
+ filament_overrides.set_key_value(opt_key, opt_copy);
+ print_diff.emplace_back(opt_key);
+ }
+ }
+ } else if (*opt_new != *opt_old)
+ print_diff.emplace_back(opt_key);
+ }
+ }
+ // Collect changes to object and region configs.
+ object_diff = m_default_object_config.diff(new_full_config);
+ region_diff = m_default_region_config.diff(new_full_config);
+ // Prepare for storing of the full print config into new_full_config to be exported into the G-code and to be used by the PlaceholderParser.
+ // As the PlaceholderParser does not interpret the FloatOrPercent values itself, these values are stored into the PlaceholderParser converted to floats.
+ for (const t_config_option_key &opt_key : new_full_config.keys()) {
+ const ConfigOption *opt_old = m_full_print_config.option(opt_key);
+ const ConfigOption *opt_new = new_full_config.option(opt_key);
+ if (opt_old == nullptr || *opt_new != *opt_old)
+ full_config_diff.emplace_back(opt_key);
+ if (opt_new->type() == coFloatOrPercent) {
+ // The m_placeholder_parser is never modified by the background processing, GCode.cpp/hpp makes a copy.
+ const ConfigOption *opt_old_pp = this->placeholder_parser().config().option(opt_key);
+ double new_value = new_full_config.get_abs_value(opt_key);
+ if (opt_old_pp == nullptr || static_cast<const ConfigOptionFloat*>(opt_old_pp)->value != new_value)
+ placeholder_parser_overrides.set_key_value(opt_key, new ConfigOptionFloat(new_value));
+ }
+ }
+}
+
+Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_config)
{
#ifdef _DEBUG
check_model_ids_validity(model);
#endif /* _DEBUG */
- // Make a copy of the config, normalize it.
- DynamicPrintConfig config(config_in);
- config.option("print_settings_id", true);
- config.option("filament_settings_id", true);
- config.option("printer_settings_id", true);
- config.normalize();
- // Collect changes to print config.
- t_config_option_keys print_diff = m_config.diff(config);
- t_config_option_keys object_diff = m_default_object_config.diff(config);
- t_config_option_keys region_diff = m_default_region_config.diff(config);
- t_config_option_keys placeholder_parser_diff = this->placeholder_parser().config_diff(config);
-
- // Do not use the ApplyStatus as we will use the max function when updating apply_status.
+ // Normalize the config.
+ new_full_config.option("print_settings_id", true);
+ new_full_config.option("filament_settings_id", true);
+ new_full_config.option("printer_settings_id", true);
+ new_full_config.normalize();
+
+ // Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles.
+ t_config_option_keys print_diff, object_diff, region_diff, full_config_diff;
+ DynamicPrintConfig placeholder_parser_overrides, filament_overrides;
+ this->config_diffs(new_full_config, print_diff, object_diff, region_diff, full_config_diff, placeholder_parser_overrides, filament_overrides);
+
+ // Do not use the ApplyStatus as we will use the max function when updating apply_status.
unsigned int apply_status = APPLY_STATUS_UNCHANGED;
auto update_apply_status = [&apply_status](bool invalidated)
{ apply_status = std::max<unsigned int>(apply_status, invalidated ? APPLY_STATUS_INVALIDATED : APPLY_STATUS_CHANGED); };
@@ -516,24 +573,25 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
// The following call may stop the background processing.
if (! print_diff.empty())
update_apply_status(this->invalidate_state_by_config_options(print_diff));
+
// Apply variables to placeholder parser. The placeholder parser is used by G-code export,
// which should be stopped if print_diff is not empty.
- if (! placeholder_parser_diff.empty()) {
+ if (! full_config_diff.empty() || ! placeholder_parser_overrides.empty()) {
update_apply_status(this->invalidate_step(psGCodeExport));
- PlaceholderParser &pp = this->placeholder_parser();
- pp.apply_only(config, placeholder_parser_diff);
+ m_placeholder_parser.apply_config(std::move(placeholder_parser_overrides));
// Set the profile aliases for the PrintBase::output_filename()
- pp.set("print_preset", config.option("print_settings_id")->clone());
- pp.set("filament_preset", config.option("filament_settings_id")->clone());
- pp.set("printer_preset", config.option("printer_settings_id")->clone());
+ m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone());
+ m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone());
+ m_placeholder_parser.set("printer_preset", new_full_config.option("printer_settings_id")->clone());
+ // It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
+ m_config.apply_only(new_full_config, print_diff, true);
+ m_config.apply(filament_overrides);
+ // Handle changes to object config defaults
+ m_default_object_config.apply_only(new_full_config, object_diff, true);
+ // Handle changes to regions config defaults
+ m_default_region_config.apply_only(new_full_config, region_diff, true);
+ m_full_print_config = std::move(new_full_config);
}
-
- // It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
- m_config.apply_only(config, print_diff, true);
- // Handle changes to object config defaults
- m_default_object_config.apply_only(config, object_diff, true);
- // Handle changes to regions config defaults
- m_default_region_config.apply_only(config, region_diff, true);
class LayerRanges
{
@@ -545,9 +603,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
m_ranges.reserve(in.size());
// Input ranges are sorted lexicographically. First range trims the other ranges.
coordf_t last_z = 0;
- for (const std::pair<const t_layer_height_range, DynamicPrintConfig> &range : in) {
-// for (auto &range : in) {
- if (range.first.second > last_z) {
+ for (const std::pair<const t_layer_height_range, DynamicPrintConfig> &range : in)
+ if (range.first.second > last_z) {
coordf_t min_z = std::max(range.first.first, 0.);
if (min_z > last_z + EPSILON) {
m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr);
@@ -559,7 +616,6 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
last_z = range.first.second;
}
}
- }
if (m_ranges.empty())
m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr);
else if (m_ranges.back().second == nullptr)
@@ -1707,9 +1763,9 @@ void Print::_make_wipe_tower()
(float)m_config.filament_cooling_initial_speed.get_at(i),
(float)m_config.filament_cooling_final_speed.get_at(i),
m_config.filament_ramming_parameters.get_at(i),
- m_config.filament_max_volumetric_speed.get_at(i),
- m_config.nozzle_diameter.get_at(i),
- m_config.filament_diameter.get_at(i));
+ (float)m_config.filament_max_volumetric_speed.get_at(i),
+ (float)m_config.nozzle_diameter.get_at(i),
+ (float)m_config.filament_diameter.get_at(i));
m_wipe_tower_data.priming = Slic3r::make_unique<std::vector<WipeTower::ToolChangeResult>>(
wipe_tower.prime((float)this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
@@ -1791,47 +1847,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/Print.hpp b/src/libslic3r/Print.hpp
index 368bf4ee8..b6d7b678d 100644
--- a/src/libslic3r/Print.hpp
+++ b/src/libslic3r/Print.hpp
@@ -296,7 +296,7 @@ public:
void clear() override;
bool empty() const override { return m_objects.empty(); }
- ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) override;
+ ApplyStatus apply(const Model &model, DynamicPrintConfig config) override;
void process() override;
// Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file.
@@ -368,6 +368,13 @@ protected:
bool invalidate_step(PrintStep step);
private:
+ void config_diffs(
+ const DynamicPrintConfig &new_full_config,
+ t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys &region_diff,
+ t_config_option_keys &full_config_diff,
+ DynamicPrintConfig &placeholder_parser_overrides,
+ DynamicPrintConfig &filament_overrides) const;
+
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
void _make_skirt();
diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp
index d84d492a0..aebc87904 100644
--- a/src/libslic3r/PrintBase.hpp
+++ b/src/libslic3r/PrintBase.hpp
@@ -217,7 +217,7 @@ protected:
class PrintBase
{
public:
- PrintBase() { this->restart(); }
+ PrintBase() : m_placeholder_parser(&m_full_print_config) { this->restart(); }
inline virtual ~PrintBase() {}
virtual PrinterTechnology technology() const noexcept = 0;
@@ -240,7 +240,7 @@ public:
// Some data was changed, which in turn invalidated already calculated steps.
APPLY_STATUS_INVALIDATED,
};
- virtual ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) = 0;
+ virtual ApplyStatus apply(const Model &model, DynamicPrintConfig config) = 0;
const Model& model() const { return m_model; }
struct TaskParams {
@@ -316,7 +316,7 @@ public:
virtual bool finished() const = 0;
const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; }
- PlaceholderParser& placeholder_parser() { return m_placeholder_parser; }
+ const DynamicPrintConfig& full_print_config() const { return m_full_print_config; }
virtual std::string output_filename(const std::string &filename_base = std::string()) const = 0;
// If the filename_base is set, it is used as the input for the template processing. In that case the path is expected to be the directory (may be empty).
@@ -341,6 +341,8 @@ protected:
void update_object_placeholders(DynamicConfig &config, const std::string &default_ext) const;
Model m_model;
+ DynamicPrintConfig m_full_print_config;
+ PlaceholderParser m_placeholder_parser;
private:
tbb::atomic<CancelStatus> m_cancel_status;
@@ -354,8 +356,6 @@ private:
// The mutex will be used to guard the worker thread against entering a stage
// while the data influencing the stage is modified.
mutable tbb::mutex m_state_mutex;
-
- PlaceholderParser m_placeholder_parser;
};
template<typename PrintStepEnum, const size_t COUNT>
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 8e091fb80..304f6f749 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -29,6 +29,7 @@ PrintConfigDef::PrintConfigDef()
this->init_common_params();
assign_printer_technology_to_unknown(this->options, ptAny);
this->init_fff_params();
+ this->init_extruder_retract_keys();
assign_printer_technology_to_unknown(this->options, ptFFF);
this->init_sla_params();
assign_printer_technology_to_unknown(this->options, ptSLA);
@@ -2238,6 +2239,48 @@ 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<const ConfigOptionFloats* >(it_opt->second.default_value.get())->values)); break;
+ case coPercents : def->set_default_value(new ConfigOptionPercentsNullable(static_cast<const ConfigOptionPercents*>(it_opt->second.default_value.get())->values)); break;
+ case coBools : def->set_default_value(new ConfigOptionBoolsNullable (static_cast<const ConfigOptionBools* >(it_opt->second.default_value.get())->values)); break;
+ default: assert(false);
+ }
+ }
+}
+
+void PrintConfigDef::init_extruder_retract_keys()
+{
+ m_extruder_retract_keys = {
+ "deretract_speed",
+ "retract_before_travel",
+ "retract_before_wipe",
+ "retract_layer_change",
+ "retract_length",
+ "retract_lift",
+ "retract_lift_above",
+ "retract_lift_below",
+ "retract_restart_extra",
+ "retract_speed",
+ "wipe"
+ };
+ assert(std::is_sorted(m_extruder_retract_keys.begin(), m_extruder_retract_keys.end()));
}
void PrintConfigDef::init_sla_params()
@@ -2997,7 +3040,7 @@ std::string FullPrintConfig::validate()
}
case coFloats:
case coPercents:
- for (double v : static_cast<const ConfigOptionFloats*>(opt)->values)
+ for (double v : static_cast<const ConfigOptionVector<double>*>(opt)->values)
if (v < optdef->min || v > optdef->max) {
out_of_range = true;
break;
@@ -3010,7 +3053,7 @@ std::string FullPrintConfig::validate()
break;
}
case coInts:
- for (int v : static_cast<const ConfigOptionInts*>(opt)->values)
+ for (int v : static_cast<const ConfigOptionVector<int>*>(opt)->values)
if (v < optdef->min || v > optdef->max) {
out_of_range = true;
break;
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index 5731bef00..f2d0775fa 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -185,10 +185,18 @@ public:
static void handle_legacy(t_config_option_key &opt_key, std::string &value);
+ // Options defining the extruder retract properties. These keys are sorted lexicographically.
+ // The extruder retract keys could be overidden by the same values defined at the Filament level
+ // (then the key is further prefixed with the "filament_" prefix).
+ const std::vector<std::string>& extruder_retract_keys() const { return m_extruder_retract_keys; }
+
private:
void init_common_params();
void init_fff_params();
+ void init_extruder_retract_keys();
void init_sla_params();
+
+ std::vector<std::string> m_extruder_retract_keys;
};
// The one and only global definition of SLic3r configuration options.
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index 5669307ac..45f8a0c83 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -145,14 +145,13 @@ static std::vector<SLAPrintObject::Instance> sla_instances(const ModelObject &mo
return instances;
}
-SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConfig &config_in)
+SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig config)
{
#ifdef _DEBUG
check_model_ids_validity(model);
#endif /* _DEBUG */
- // Make a copy of the config, normalize it.
- DynamicPrintConfig config(config_in);
+ // Normalize the config.
config.option("sla_print_settings_id", true);
config.option("sla_material_settings_id", true);
config.option("printer_settings_id", true);
@@ -162,7 +161,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
t_config_option_keys printer_diff = m_printer_config.diff(config);
t_config_option_keys material_diff = m_material_config.diff(config);
t_config_option_keys object_diff = m_default_object_config.diff(config);
- t_config_option_keys placeholder_parser_diff = this->placeholder_parser().config_diff(config);
+ t_config_option_keys placeholder_parser_diff = m_placeholder_parser.config_diff(config);
// Do not use the ApplyStatus as we will use the max function when updating apply_status.
unsigned int apply_status = APPLY_STATUS_UNCHANGED;
@@ -187,12 +186,11 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
// only to generate the output file name.
if (! placeholder_parser_diff.empty()) {
// update_apply_status(this->invalidate_step(slapsRasterize));
- PlaceholderParser &pp = this->placeholder_parser();
- pp.apply_config(config);
+ m_placeholder_parser.apply_config(config);
// Set the profile aliases for the PrintBase::output_filename()
- pp.set("print_preset", config.option("sla_print_settings_id")->clone());
- pp.set("material_preset", config.option("sla_material_settings_id")->clone());
- pp.set("printer_preset", config.option("printer_settings_id")->clone());
+ m_placeholder_parser.set("print_preset", config.option("sla_print_settings_id")->clone());
+ m_placeholder_parser.set("material_preset", config.option("sla_material_settings_id")->clone());
+ m_placeholder_parser.set("printer_preset", config.option("printer_settings_id")->clone());
}
// It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
@@ -459,6 +457,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
check_model_ids_equal(m_model, model);
#endif /* _DEBUG */
+ m_full_print_config = std::move(config);
return static_cast<ApplyStatus>(apply_status);
}
diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp
index c4e58ab39..e8cdac1b8 100644
--- a/src/libslic3r/SLAPrint.hpp
+++ b/src/libslic3r/SLAPrint.hpp
@@ -349,7 +349,7 @@ public:
void clear() override;
bool empty() const override { return m_objects.empty(); }
- ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) override;
+ ApplyStatus apply(const Model &model, DynamicPrintConfig config) override;
void set_task(const TaskParams &params) override;
void process() override;
void finalize() override;
diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp
index db935cc05..39fa9c54b 100644
--- a/src/slic3r/GUI/Field.cpp
+++ b/src/slic3r/GUI/Field.cpp
@@ -136,6 +136,8 @@ bool Field::is_matched(const std::string& string, const std::string& pattern)
return std::regex_match(string, regex_pattern);
}
+static wxString na_value() { return _(L("N/A")); }
+
void Field::get_value_by_opt_type(wxString& str)
{
switch (m_opt.type) {
@@ -165,7 +167,9 @@ void Field::get_value_by_opt_type(wxString& str)
val = 0.0;
else
{
- if (!str.ToCDouble(&val))
+ if (m_opt.nullable && str == na_value())
+ val = ConfigOptionFloatsNullable::nil_value();
+ else if (!str.ToCDouble(&val))
{
show_error(m_parent, _(L("Invalid numeric input.")));
set_value(double_to_string(val), true);
@@ -256,6 +260,7 @@ void TextCtrl::BUILD() {
m_opt.default_value->getFloat() :
m_opt.get_default_value<ConfigOptionPercents>()->get_at(m_opt_idx);
text_value = double_to_string(val);
+ m_last_meaningful_value = text_value;
break;
}
case coString:
@@ -325,24 +330,7 @@ void TextCtrl::BUILD() {
}
propagate_value();
}), temp->GetId());
- /*
- temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt)
- {
-#ifdef __WXGTK__
- if (bChangedValueEvent)
-#endif //__WXGTK__
- if(is_defined_input_value())
- on_change_field();
- }), temp->GetId());
-#ifdef __WXGTK__
- // to correct value updating on GTK we should:
- // call on_change_field() on wxEVT_KEY_UP instead of wxEVT_TEXT
- // and prevent value updating on wxEVT_KEY_DOWN
- temp->Bind(wxEVT_KEY_DOWN, &TextCtrl::change_field_value, this);
- temp->Bind(wxEVT_KEY_UP, &TextCtrl::change_field_value, this);
-#endif //__WXGTK__
-*/
// select all text using Ctrl+A
temp->Bind(wxEVT_CHAR, ([temp](wxKeyEvent& event)
{
@@ -355,14 +343,70 @@ void TextCtrl::BUILD() {
window = dynamic_cast<wxWindow*>(temp);
}
+bool TextCtrl::value_was_changed()
+{
+ if (m_value.empty())
+ return true;
+
+ boost::any val = m_value;
+ wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue();
+ // update m_value!
+ get_value_by_opt_type(ret_str);
+
+ switch (m_opt.type) {
+ case coInt:
+ return boost::any_cast<int>(m_value) != boost::any_cast<int>(val);
+ case coPercent:
+ case coPercents:
+ case coFloats:
+ case coFloat: {
+ if (m_opt.nullable && std::isnan(boost::any_cast<double>(m_value)) &&
+ std::isnan(boost::any_cast<double>(val)))
+ return false;
+ return boost::any_cast<double>(m_value) != boost::any_cast<double>(val);
+ }
+ case coString:
+ case coStrings:
+ case coFloatOrPercent:
+ return boost::any_cast<std::string>(m_value) != boost::any_cast<std::string>(val);
+ default:
+ return true;
+ }
+}
+
void TextCtrl::propagate_value()
{
- if (is_defined_input_value<wxTextCtrl>(window, m_opt.type))
+ if (is_defined_input_value<wxTextCtrl>(window, m_opt.type) && value_was_changed())
on_change_field();
else
on_kill_focus();
}
+void TextCtrl::set_value(const boost::any& value, bool change_event/* = false*/) {
+ m_disable_change_event = !change_event;
+ if (m_opt.nullable) {
+ const bool m_is_na_val = boost::any_cast<wxString>(value) == na_value();
+ if (!m_is_na_val)
+ m_last_meaningful_value = value;
+ dynamic_cast<wxTextCtrl*>(window)->SetValue(m_is_na_val ? na_value() : boost::any_cast<wxString>(value));
+ }
+ else
+ dynamic_cast<wxTextCtrl*>(window)->SetValue(boost::any_cast<wxString>(value));
+ m_disable_change_event = false;
+}
+
+void TextCtrl::set_last_meaningful_value()
+{
+ dynamic_cast<wxTextCtrl*>(window)->SetValue(boost::any_cast<wxString>(m_last_meaningful_value));
+ propagate_value();
+}
+
+void TextCtrl::set_na_value()
+{
+ dynamic_cast<wxTextCtrl*>(window)->SetValue(na_value());
+ propagate_value();
+}
+
boost::any& TextCtrl::get_value()
{
wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue();
@@ -409,6 +453,8 @@ void CheckBox::BUILD() {
m_opt.get_default_value<ConfigOptionBools>()->get_at(m_opt_idx) :
false;
+ m_last_meaningful_value = static_cast<unsigned char>(check_value);
+
// Set Label as a string of at least one space simbol to correct system scaling of a CheckBox
auto temp = new wxCheckBox(m_parent, wxID_ANY, wxString(" "), wxDefaultPosition, size);
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
@@ -416,7 +462,10 @@ void CheckBox::BUILD() {
temp->SetValue(check_value);
if (m_opt.readonly) temp->Disable();
- temp->Bind(wxEVT_CHECKBOX, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId());
+ temp->Bind(wxEVT_CHECKBOX, ([this](wxCommandEvent e) {
+ m_is_na_val = false;
+ on_change_field();
+ }), temp->GetId());
temp->SetToolTip(get_tooltip_text(check_value ? "true" : "false"));
@@ -424,6 +473,38 @@ void CheckBox::BUILD() {
window = dynamic_cast<wxWindow*>(temp);
}
+void CheckBox::set_value(const boost::any& value, bool change_event)
+{
+ m_disable_change_event = !change_event;
+ if (m_opt.nullable) {
+ m_is_na_val = boost::any_cast<unsigned char>(value) == ConfigOptionBoolsNullable::nil_value();
+ if (!m_is_na_val)
+ m_last_meaningful_value = value;
+ dynamic_cast<wxCheckBox*>(window)->SetValue(m_is_na_val ? false : boost::any_cast<unsigned char>(value) != 0);
+ }
+ else
+ dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<bool>(value));
+ m_disable_change_event = false;
+}
+
+void CheckBox::set_last_meaningful_value()
+{
+ if (m_opt.nullable) {
+ m_is_na_val = false;
+ dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<unsigned char>(m_last_meaningful_value) != 0);
+ on_change_field();
+ }
+}
+
+void CheckBox::set_na_value()
+{
+ if (m_opt.nullable) {
+ m_is_na_val = true;
+ dynamic_cast<wxCheckBox*>(window)->SetValue(false);
+ on_change_field();
+ }
+}
+
boost::any& CheckBox::get_value()
{
// boost::any m_value;
@@ -431,7 +512,7 @@ boost::any& CheckBox::get_value()
if (m_opt.type == coBool)
m_value = static_cast<bool>(value);
else
- m_value = static_cast<unsigned char>(value);
+ m_value = m_is_na_val ? ConfigOptionBoolsNullable::nil_value() : static_cast<unsigned char>(value);
return m_value;
}
diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp
index 990c40e6f..6c16f90f2 100644
--- a/src/slic3r/GUI/Field.hpp
+++ b/src/slic3r/GUI/Field.hpp
@@ -123,6 +123,8 @@ public:
/// subclasses should overload with a specific version
/// Postcondition: Method does not fire the on_change event.
virtual void set_value(const boost::any& value, bool change_event) = 0;
+ virtual void set_last_meaningful_value() {}
+ virtual void set_na_value() {}
/// Gets a boost::any representing this control.
/// subclasses should overload with a specific version
@@ -247,6 +249,8 @@ protected:
// current value
boost::any m_value;
+ // last maeningful value
+ boost::any m_last_meaningful_value;
int m_em_unit;
@@ -277,6 +281,7 @@ public:
~TextCtrl() {}
void BUILD();
+ bool value_was_changed();
// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
void propagate_value();
wxWindow* window {nullptr};
@@ -286,11 +291,9 @@ public:
dynamic_cast<wxTextCtrl*>(window)->SetValue(wxString(value));
m_disable_change_event = false;
}
- virtual void set_value(const boost::any& value, bool change_event = false) {
- m_disable_change_event = !change_event;
- dynamic_cast<wxTextCtrl*>(window)->SetValue(boost::any_cast<wxString>(value));
- m_disable_change_event = false;
- }
+ virtual void set_value(const boost::any& value, bool change_event = false) override;
+ virtual void set_last_meaningful_value() override;
+ virtual void set_na_value() override;
boost::any& get_value() override;
@@ -303,6 +306,7 @@ public:
class CheckBox : public Field {
using Field::Field;
+ bool m_is_na_val {false};
public:
CheckBox(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
CheckBox(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
@@ -316,11 +320,9 @@ public:
dynamic_cast<wxCheckBox*>(window)->SetValue(value);
m_disable_change_event = false;
}
- void set_value(const boost::any& value, bool change_event = false) {
- m_disable_change_event = !change_event;
- dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<bool>(value));
- m_disable_change_event = false;
- }
+ void set_value(const boost::any& value, bool change_event = false) override;
+ void set_last_meaningful_value() override;
+ void set_na_value() override;
boost::any& get_value() override;
void msw_rescale() override;
diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp
index 8f41ed5a3..826f2d6fc 100644
--- a/src/slic3r/GUI/GUI.cpp
+++ b/src/slic3r/GUI/GUI.cpp
@@ -148,6 +148,13 @@ void config_wizard(int reason)
void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/)
{
try{
+
+ if (config.def()->get(opt_key)->type == coBools && config.def()->get(opt_key)->nullable) {
+ ConfigOptionBoolsNullable* vec_new = new ConfigOptionBoolsNullable{ boost::any_cast<unsigned char>(value) };
+ config.option<ConfigOptionBoolsNullable>(opt_key)->set_at(vec_new, opt_index, 0);
+ return;
+ }
+
switch (config.def()->get(opt_key)->type) {
case coFloatOrPercent:{
std::string str = boost::any_cast<std::string>(value);
diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp
index 9feca2f3d..656e07a0e 100644
--- a/src/slic3r/GUI/OptionsGroup.cpp
+++ b/src/slic3r/GUI/OptionsGroup.cpp
@@ -202,7 +202,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
// so we need a horizontal sizer to arrange these things
auto sizer = new wxBoxSizer(wxHORIZONTAL);
grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1);
- sizer->Add(m_near_label_widget_ptrs.back(), 0, wxRIGHT, 7);
+ sizer->Add(m_near_label_widget_ptrs.back(), 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 7);
sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, 5);
}
}
@@ -372,30 +372,10 @@ void ConfigOptionsGroup::on_change_OG(const t_config_option_key& opt_id, const b
auto option = m_options.at(opt_id).opt;
- // get value
-//! auto field_value = get_value(opt_id);
- if (option.gui_flags.compare("serialized")==0) {
- if (opt_index != -1) {
- // die "Can't set serialized option indexed value" ;
- }
- change_opt_value(*m_config, opt_key, value);
- }
- else {
- if (opt_index == -1) {
- // change_opt_value(*m_config, opt_key, field_value);
- //!? why field_value?? in this case changed value will be lose! No?
- change_opt_value(*m_config, opt_key, value);
- }
- else {
- change_opt_value(*m_config, opt_key, value, opt_index);
-// auto value = m_config->get($opt_key);
-// $value->[$opt_index] = $field_value;
-// $self->config->set($opt_key, $value);
- }
- }
+ change_opt_value(*m_config, opt_key, value, opt_index == -1 ? 0 : opt_index);
}
- OptionsGroup::on_change_OG(opt_id, value); //!? Why doing this
+ OptionsGroup::on_change_OG(opt_id, value);
}
void ConfigOptionsGroup::back_to_initial_value(const std::string& opt_key)
@@ -578,6 +558,34 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
boost::any ret;
wxString text_value = wxString("");
const ConfigOptionDef* opt = config.def()->get(opt_key);
+
+ if (opt->nullable)
+ {
+ switch (opt->type)
+ {
+ case coPercents:
+ case coFloats: {
+ if (config.option(opt_key)->is_nil())
+ ret = _(L("N/A"));
+ else {
+ double val = opt->type == coFloats ?
+ config.option<ConfigOptionFloatsNullable>(opt_key)->get_at(idx) :
+ config.option<ConfigOptionPercentsNullable>(opt_key)->get_at(idx);
+ ret = double_to_string(val); }
+ }
+ break;
+ case coBools:
+ ret = config.option<ConfigOptionBoolsNullable>(opt_key)->values[idx];
+ break;
+ case coInts:
+ ret = config.option<ConfigOptionIntsNullable>(opt_key)->get_at(idx);
+ break;
+ default:
+ break;
+ }
+ return ret;
+ }
+
switch (opt->type) {
case coFloatOrPercent:{
const auto &value = *config.option<ConfigOptionFloatOrPercent>(opt_key);
diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp
index d720787b6..cc3d89b1f 100644
--- a/src/slic3r/GUI/OptionsGroup.hpp
+++ b/src/slic3r/GUI/OptionsGroup.hpp
@@ -252,6 +252,9 @@ public:
Option option = get_option(title, idx);
return OptionsGroup::create_single_option_line(option);
}
+ Line create_single_option_line(const Option& option) const {
+ return OptionsGroup::create_single_option_line(option);
+ }
void append_single_option_line(const Option& option) {
OptionsGroup::append_single_option_line(option);
}
diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp
index e34c9be28..fa8b5baee 100644
--- a/src/slic3r/GUI/Preset.cpp
+++ b/src/slic3r/GUI/Preset.cpp
@@ -400,6 +400,10 @@ const std::vector<std::string>& 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<ConfigOptionStrings>("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();
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index a00f7902f..9d7fc20a3 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -1497,6 +1497,105 @@ void TabPrint::OnActivate()
Tab::OnActivate();
}
+void TabFilament::add_filament_overrides_page()
+{
+ PageShp page = add_options_page(_(L("Filament Overrides")), "wrench");
+ ConfigOptionsGroupShp optgroup = page->new_optgroup(_(L("Retraction")));
+
+ auto append_single_option_line = [optgroup, this](const std::string& opt_key, int opt_index)
+ {
+ Line line {"",""};
+ if (opt_key == "filament_retract_lift_above" || opt_key == "filament_retract_lift_below") {
+ Option opt = optgroup->get_option(opt_key);
+ opt.opt.label = opt.opt.full_label;
+ line = optgroup->create_single_option_line(opt);
+ }
+ else
+ line = optgroup->create_single_option_line(optgroup->get_option(opt_key));
+
+ line.near_label_widget = [this, optgroup, opt_key, opt_index](wxWindow* parent) {
+ wxCheckBox* check_box = new wxCheckBox(parent, wxID_ANY, "");
+
+ check_box->Bind(wxEVT_CHECKBOX, [this, optgroup, opt_key, opt_index](wxCommandEvent& evt) {
+ const bool is_checked = evt.IsChecked();
+ Field* field = optgroup->get_fieldc(opt_key, opt_index);
+ if (field != nullptr) {
+ field->toggle(is_checked);
+ if (is_checked)
+ field->set_last_meaningful_value();
+ else
+ field->set_na_value();
+ }
+ }, check_box->GetId());
+
+ m_overrides_options[opt_key] = check_box;
+ return check_box;
+ };
+
+ optgroup->append_line(line);
+ };
+
+ const int extruder_idx = 0; // #ys_FIXME
+
+ for (const std::string opt_key : { "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"
+ })
+ append_single_option_line(opt_key, extruder_idx);
+}
+
+void TabFilament::update_filament_overrides_page()
+{
+ const auto page_it = std::find_if(m_pages.begin(), m_pages.end(), [](const PageShp page) {return page->title() == _(L("Filament Overrides")); });
+ if (page_it == m_pages.end())
+ return;
+ PageShp page = *page_it;
+
+ const auto og_it = std::find_if(page->m_optgroups.begin(), page->m_optgroups.end(), [](const ConfigOptionsGroupShp og) {return og->title == _(L("Retraction")); });
+ if (og_it == page->m_optgroups.end())
+ return;
+ ConfigOptionsGroupShp optgroup = *og_it;
+
+ std::vector<std::string> opt_keys = { "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"
+ };
+
+ const int extruder_idx = 0; // #ys_FIXME
+
+ const bool have_retract_length = m_config->option("filament_retract_length")->is_nil() ||
+ m_config->opt_float("filament_retract_length", extruder_idx) > 0;
+
+ for (const std::string& opt_key : opt_keys)
+ {
+ bool is_checked = opt_key=="filament_retract_length" ? true : have_retract_length;
+ m_overrides_options[opt_key]->Enable(is_checked);
+
+ is_checked &= !m_config->option(opt_key)->is_nil();
+ m_overrides_options[opt_key]->SetValue(is_checked);
+
+ Field* field = optgroup->get_fieldc(opt_key, extruder_idx);
+ if (field != nullptr)
+ field->toggle(is_checked);
+ }
+}
+
void TabFilament::build()
{
m_presets = &m_preset_bundle->filaments;
@@ -1594,10 +1693,14 @@ void TabFilament::build()
};
optgroup->append_line(line);
+
+ add_filament_overrides_page();
+
+
const int gcode_field_height = 15; // 150
const int notes_field_height = 25; // 250
- page = add_options_page(_(L("Custom G-code")), "cog");
+ page = add_options_page(_(L("Custom G-code")), "cog");
optgroup = page->new_optgroup(_(L("Start G-code")), 0);
Option option = optgroup->get_option("start_filament_gcode");
option.opt.full_width = true;
@@ -1661,7 +1764,7 @@ void TabFilament::update()
return; // ys_FIXME
m_update_cnt++;
-// Freeze();
+
wxString text = from_u8(PresetHints::cooling_description(m_presets->get_edited_preset()));
m_cooling_description_line->SetText(text);
text = from_u8(PresetHints::maximum_volumetric_flow_description(*m_preset_bundle));
@@ -1676,7 +1779,9 @@ void TabFilament::update()
for (auto el : { "min_fan_speed", "disable_fan_first_layers" })
get_field(el)->toggle(fan_always_on);
-// Thaw();
+
+ update_filament_overrides_page();
+
m_update_cnt--;
if (m_update_cnt == 0)
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index 73b6bb08d..6ff76f5c4 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -337,6 +337,11 @@ class TabFilament : public Tab
{
ogStaticText* m_volumetric_speed_description_line;
ogStaticText* m_cooling_description_line;
+
+ void add_filament_overrides_page();
+ void update_filament_overrides_page();
+
+ std::map<std::string, wxCheckBox*> m_overrides_options;
public:
TabFilament(wxNotebook* parent) :
// Tab(parent, _(L("Filament Settings")), L("filament")) {}