diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 562399b47..dac9993c3 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -170,6 +170,8 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/GLShader.hpp ${LIBDIR}/slic3r/GUI/Preset.cpp ${LIBDIR}/slic3r/GUI/Preset.hpp + ${LIBDIR}/slic3r/GUI/PresetBundle.cpp + ${LIBDIR}/slic3r/GUI/PresetBundle.hpp ${LIBDIR}/slic3r/GUI/GUI.cpp ${LIBDIR}/slic3r/GUI/GUI.hpp ) diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp index 3871253fd..cb8dd98f7 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/xs/src/libslic3r/Config.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #if defined(_WIN32) && !defined(setenv) && defined(_putenv_s) @@ -329,11 +328,15 @@ void ConfigBase::load(const std::string &file) void ConfigBase::load_from_ini(const std::string &file) { - namespace pt = boost::property_tree; - pt::ptree tree; + boost::property_tree::ptree tree; boost::nowide::ifstream ifs(file); - pt::read_ini(ifs, tree); - for (const pt::ptree::value_type &v : tree) { + boost::property_tree::read_ini(ifs, tree); + this->load(tree); +} + +void ConfigBase::load(const boost::property_tree::ptree &tree) +{ + for (const boost::property_tree::ptree::value_type &v : tree) { try { t_config_option_key opt_key = v.first; this->set_deserialize(opt_key, v.second.get_value()); @@ -428,6 +431,18 @@ void ConfigBase::save(const std::string &file) const c.close(); } +bool DynamicConfig::operator==(const DynamicConfig &rhs) const +{ + t_options_map::const_iterator it1 = this->options.begin(); + t_options_map::const_iterator it1_end = this->options.end(); + t_options_map::const_iterator it2 = rhs.options.begin(); + t_options_map::const_iterator it2_end = rhs.options.end(); + for (; it1 != it1_end && it2 != it2_end; ++ it1, ++ it2) + if (*it1->second != *it2->second) + return false; + return it1 == it1_end && it2 == it2_end; +} + ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool create) { t_options_map::iterator it = options.find(opt_key); diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 5cf24d8ed..2228b8e7c 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -13,6 +13,8 @@ #include "libslic3r.h" #include "Point.hpp" +#include + namespace Slic3r { // Name of the configuration option. @@ -62,7 +64,7 @@ enum ConfigOptionType { // A generic value of a configuration option. class ConfigOption { public: - virtual ~ConfigOption() {}; + virtual ~ConfigOption() {} virtual ConfigOptionType type() const = 0; virtual std::string serialize() const = 0; @@ -94,7 +96,7 @@ public: throw std::runtime_error("ConfigOptionSingle: Assigning an incompatible type"); assert(dynamic_cast*>(rhs)); this->value = static_cast*>(rhs)->value; - }; + } bool operator==(const ConfigOption &rhs) const override { @@ -122,6 +124,9 @@ public: // This function is useful to split values from multiple extrder / filament settings into separate configurations. virtual void set_at(const ConfigOption *rhs, size_t i, size_t j) = 0; + virtual void resize(size_t n, const ConfigOption *opt_default = nullptr) = 0; + + protected: // Used to verify type compatibility when assigning to / from a scalar ConfigOption. ConfigOptionType scalar_type() const { return static_cast(this->type() - coVectorType); } @@ -132,6 +137,9 @@ template class ConfigOptionVector : public ConfigOptionVectorBase { public: + ConfigOptionVector() {} + explicit ConfigOptionVector(size_t n, const T &value) : values(n, value) {} + explicit ConfigOptionVector(std::initializer_list il) : values(std::move(il)) {} std::vector values; void set(const ConfigOption *rhs) override @@ -194,6 +202,31 @@ public: const T& get_at(size_t i) const { return const_cast*>(this)->get_at(i); } + // Resize this vector by duplicating the last value. + // If the current vector is empty, the default value is used instead. + void resize(size_t n, const ConfigOption *opt_default = nullptr) override + { + assert(opt_default == nullptr || opt_default->is_vector()); + assert(opt_default == nullptr || dynamic_cast>(opt_default)); + assert(! this->values.empty() || opt_default != nullptr); + if (n == 0) + this->values.clear(); + else if (n < this->values.size()) + this->values.erase(this->values.begin() + n, this->values.end()); + else if (n > this->values.size()) + if (this->values.empty()) { + if (opt_default == nullptr) { + throw std::runtime_error("ConfigOptionVector::resize(): No default value provided."); + if (opt_default->type() != this->type()) + throw std::runtime_error("ConfigOptionVector::resize(): Extending with an incompatible type."); + this->values.resize(n, static_cast*>(opt_default)->values.front()); + } else { + // Resize by duplicating the last value. + this->values.resize(n, this->values.back()); + } + } + } + bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) @@ -209,8 +242,8 @@ public: class ConfigOptionFloat : public ConfigOptionSingle { public: - ConfigOptionFloat() : ConfigOptionSingle(0) {}; - explicit ConfigOptionFloat(double _value) : ConfigOptionSingle(_value) {}; + ConfigOptionFloat() : ConfigOptionSingle(0) {} + explicit ConfigOptionFloat(double _value) : ConfigOptionSingle(_value) {} ConfigOptionType type() const override { return coFloat; } double getFloat() const override { return this->value; } @@ -242,6 +275,10 @@ public: class ConfigOptionFloats : public ConfigOptionVector { public: + ConfigOptionFloats() : ConfigOptionVector() {} + explicit ConfigOptionFloats(size_t n, double value) : ConfigOptionVector(n, value) {} + explicit ConfigOptionFloats(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} + ConfigOptionType type() const override { return coFloats; } ConfigOption* clone() const override { return new ConfigOptionFloats(*this); } bool operator==(const ConfigOptionFloats &rhs) const { return this->values == rhs.values; } @@ -254,7 +291,7 @@ public: ss << *it; } return ss.str(); - }; + } std::vector vserialize() const override { @@ -293,13 +330,13 @@ public: class ConfigOptionInt : public ConfigOptionSingle { public: - ConfigOptionInt() : ConfigOptionSingle(0) {}; - explicit ConfigOptionInt(int value) : ConfigOptionSingle(value) {}; - explicit ConfigOptionInt(double _value) : ConfigOptionSingle(int(floor(_value + 0.5))) {}; + ConfigOptionInt() : ConfigOptionSingle(0) {} + explicit ConfigOptionInt(int value) : ConfigOptionSingle(value) {} + explicit ConfigOptionInt(double _value) : ConfigOptionSingle(int(floor(_value + 0.5))) {} ConfigOptionType type() const override { return coInt; } - int getInt() const override { return this->value; }; - void setInt(int val) { this->value = val; }; + int getInt() const override { return this->value; } + void setInt(int val) { this->value = val; } ConfigOption* clone() const override { return new ConfigOptionInt(*this); } bool operator==(const ConfigOptionInt &rhs) const { return this->value == rhs.value; } @@ -328,6 +365,10 @@ public: class ConfigOptionInts : public ConfigOptionVector { public: + ConfigOptionInts() : ConfigOptionVector() {} + explicit ConfigOptionInts(size_t n, int value) : ConfigOptionVector(n, value) {} + explicit ConfigOptionInts(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} + ConfigOptionType type() const override { return coInts; } ConfigOption* clone() const override { return new ConfigOptionInts(*this); } ConfigOptionInts& operator=(const ConfigOption *opt) { this->set(opt); return *this; } @@ -373,8 +414,8 @@ public: class ConfigOptionString : public ConfigOptionSingle { public: - ConfigOptionString() : ConfigOptionSingle("") {}; - explicit ConfigOptionString(std::string _value) : ConfigOptionSingle(_value) {}; + ConfigOptionString() : ConfigOptionSingle("") {} + explicit ConfigOptionString(const std::string &value) : ConfigOptionSingle(value) {} ConfigOptionType type() const override { return coString; } ConfigOption* clone() const override { return new ConfigOptionString(*this); } @@ -397,6 +438,10 @@ public: class ConfigOptionStrings : public ConfigOptionVector { public: + ConfigOptionStrings() : ConfigOptionVector() {} + explicit ConfigOptionStrings(size_t n, const std::string &value) : ConfigOptionVector(n, value) {} + explicit ConfigOptionStrings(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} + ConfigOptionType type() const override { return coStrings; } ConfigOption* clone() const override { return new ConfigOptionStrings(*this); } ConfigOptionStrings& operator=(const ConfigOption *opt) { this->set(opt); return *this; } @@ -423,8 +468,8 @@ public: class ConfigOptionPercent : public ConfigOptionFloat { public: - ConfigOptionPercent() : ConfigOptionFloat(0) {}; - explicit ConfigOptionPercent(double _value) : ConfigOptionFloat(_value) {}; + ConfigOptionPercent() : ConfigOptionFloat(0) {} + explicit ConfigOptionPercent(double _value) : ConfigOptionFloat(_value) {} ConfigOptionType type() const override { return coPercent; } ConfigOption* clone() const override { return new ConfigOptionPercent(*this); } @@ -454,6 +499,10 @@ public: class ConfigOptionPercents : public ConfigOptionFloats { public: + ConfigOptionPercents() : ConfigOptionFloats() {} + explicit ConfigOptionPercents(size_t n, double value) : ConfigOptionFloats(n, value) {} + explicit ConfigOptionPercents(std::initializer_list il) : ConfigOptionFloats(std::move(il)) {} + ConfigOptionType type() const override { return coPercents; } ConfigOption* clone() const override { return new ConfigOptionPercents(*this); } ConfigOptionPercents& operator=(const ConfigOption *opt) { this->set(opt); return *this; } @@ -504,9 +553,9 @@ class ConfigOptionFloatOrPercent : public ConfigOptionPercent { public: bool percent; - ConfigOptionFloatOrPercent() : ConfigOptionPercent(0), percent(false) {}; - explicit ConfigOptionFloatOrPercent(double _value, bool _percent) : ConfigOptionPercent(_value), percent(_percent) {}; - + ConfigOptionFloatOrPercent() : ConfigOptionPercent(0), percent(false) {} + explicit ConfigOptionFloatOrPercent(double _value, bool _percent) : ConfigOptionPercent(_value), percent(_percent) {} + ConfigOptionType type() const override { return coFloatOrPercent; } ConfigOption* clone() const override { return new ConfigOptionFloatOrPercent(*this); } ConfigOptionFloatOrPercent& operator=(const ConfigOption *opt) { this->set(opt); return *this; } @@ -544,8 +593,8 @@ public: class ConfigOptionPoint : public ConfigOptionSingle { public: - ConfigOptionPoint() : ConfigOptionSingle(Pointf(0,0)) {}; - explicit ConfigOptionPoint(const Pointf &value) : ConfigOptionSingle(value) {}; + ConfigOptionPoint() : ConfigOptionSingle(Pointf(0,0)) {} + explicit ConfigOptionPoint(const Pointf &value) : ConfigOptionSingle(value) {} ConfigOptionType type() const override { return coPoint; } ConfigOption* clone() const override { return new ConfigOptionPoint(*this); } @@ -576,6 +625,10 @@ public: class ConfigOptionPoints : public ConfigOptionVector { public: + ConfigOptionPoints() : ConfigOptionVector() {} + explicit ConfigOptionPoints(size_t n, const Pointf &value) : ConfigOptionVector(n, value) {} + explicit ConfigOptionPoints(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} + ConfigOptionType type() const override { return coPoints; } ConfigOption* clone() const override { return new ConfigOptionPoints(*this); } ConfigOptionPoints& operator=(const ConfigOption *opt) { this->set(opt); return *this; } @@ -629,11 +682,11 @@ public: class ConfigOptionBool : public ConfigOptionSingle { public: - ConfigOptionBool() : ConfigOptionSingle(false) {}; - explicit ConfigOptionBool(bool _value) : ConfigOptionSingle(_value) {}; + ConfigOptionBool() : ConfigOptionSingle(false) {} + explicit ConfigOptionBool(bool _value) : ConfigOptionSingle(_value) {} ConfigOptionType type() const override { return coBool; } - bool getBool() const override { return this->value; }; + bool getBool() const override { return this->value; } ConfigOption* clone() const override { return new ConfigOptionBool(*this); } ConfigOptionBool& operator=(const ConfigOption *opt) { this->set(opt); return *this; } bool operator==(const ConfigOptionBool &rhs) const { return this->value == rhs.value; } @@ -654,6 +707,10 @@ public: class ConfigOptionBools : public ConfigOptionVector { public: + ConfigOptionBools() : ConfigOptionVector() {} + explicit ConfigOptionBools(size_t n, bool value) : ConfigOptionVector(n, (unsigned char)value) {} + explicit ConfigOptionBools(std::initializer_list il) { values.reserve(il.size()); for (bool b : il) values.emplace_back((unsigned char)b); } + ConfigOptionType type() const override { return coBools; } ConfigOption* clone() const override { return new ConfigOptionBools(*this); } ConfigOptionBools& operator=(const ConfigOption *opt) { this->set(opt); return *this; } @@ -711,8 +768,8 @@ class ConfigOptionEnum : public ConfigOptionSingle { public: // by default, use the first value (0) of the T enum type - ConfigOptionEnum() : ConfigOptionSingle(static_cast(0)) {}; - explicit ConfigOptionEnum(T _value) : ConfigOptionSingle(_value) {}; + ConfigOptionEnum() : ConfigOptionSingle(static_cast(0)) {} + explicit ConfigOptionEnum(T _value) : ConfigOptionSingle(_value) {} ConfigOptionType type() const override { return coEnum; } ConfigOption* clone() const override { return new ConfigOptionEnum(*this); } @@ -954,6 +1011,7 @@ public: void load(const std::string &file); void load_from_ini(const std::string &file); void load_from_gcode(const std::string &file); + void load(const boost::property_tree::ptree &tree); void save(const std::string &file) const; private: @@ -986,6 +1044,9 @@ public: return *this; } + bool operator==(const DynamicConfig &rhs) const; + bool operator!=(const DynamicConfig &rhs) const { return ! (*this == rhs); } + void swap(DynamicConfig &other) { std::swap(this->options, other.options); diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 8986bb59d..fa5ea4a8d 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -23,14 +23,7 @@ PrintConfigDef::PrintConfigDef() def = this->add("bed_shape", coPoints); def->label = "Bed shape"; - { - ConfigOptionPoints* opt = new ConfigOptionPoints(); - opt->values.push_back(Pointf(0,0)); - opt->values.push_back(Pointf(200,0)); - opt->values.push_back(Pointf(200,200)); - opt->values.push_back(Pointf(0,200)); - def->default_value = opt; - } + def->default_value = new ConfigOptionPoints { Pointf(0,0), Pointf(200,0), Pointf(200,200), Pointf(0,200) }; def = this->add("bed_temperature", coInts); def->label = "Other layers"; @@ -39,11 +32,7 @@ PrintConfigDef::PrintConfigDef() def->full_label = "Bed temperature"; def->min = 0; def->max = 300; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 0 }; def = this->add("before_layer_gcode", coString); def->label = "Before layer change G-code"; @@ -87,11 +76,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "bridge-fan-speed=i@"; def->min = 0; def->max = 100; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(100); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 100 }; def = this->add("bridge_flow_ratio", coFloat); def->label = "Bridge flow ratio"; @@ -139,11 +124,7 @@ PrintConfigDef::PrintConfigDef() def->label = "Enable auto cooling"; def->tooltip = "This flag enables the automatic cooling logic that adjusts print speed and fan speed according to layer printing time."; def->cli = "cooling!"; - { - ConfigOptionBools* opt = new ConfigOptionBools(); - opt->values.push_back(true); - def->default_value = opt; - } + def->default_value = new ConfigOptionBools { true }; def = this->add("default_acceleration", coFloat); def->label = "Default"; @@ -160,11 +141,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "disable-fan-first-layers=i@"; def->min = 0; def->max = 1000; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(3); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 3 }; def = this->add("dont_support_bridges", coBool); def->label = "Don't support bridges"; @@ -207,11 +184,7 @@ PrintConfigDef::PrintConfigDef() def->multiline = true; def->full_width = true; def->height = 120; - { - ConfigOptionStrings* opt = new ConfigOptionStrings(); - opt->values.push_back("; Filament-specific end gcode \n;END gcode for filament\n"); - def->default_value = opt; - } + def->default_value = new ConfigOptionStrings { "; Filament-specific end gcode \n;END gcode for filament\n" }; def = this->add("ensure_vertical_shell_thickness", coBool); def->label = "Ensure vertical shell thickness"; @@ -305,24 +278,15 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "This is only used in the Slic3r interface as a visual help."; def->cli = "extruder-color=s@"; def->gui_type = "color"; - { - ConfigOptionStrings* opt = new ConfigOptionStrings(); - // Empty string means no color assigned yet. -// opt->values.push_back("#FFFFFF"); - opt->values.push_back(""); - def->default_value = opt; - } + // Empty string means no color assigned yet. + def->default_value = new ConfigOptionStrings { "" }; def = this->add("extruder_offset", coPoints); def->label = "Extruder offset"; def->tooltip = "If your firmware doesn't handle the extruder displacement you need the G-code to take it into account. This option lets you specify the displacement of each extruder with respect to the first one. It expects positive coordinates (they will be subtracted from the XY coordinate)."; def->sidetext = "mm"; def->cli = "extruder-offset=s@"; - { - ConfigOptionPoints* opt = new ConfigOptionPoints(); - opt->values.push_back(Pointf(0,0)); - def->default_value = opt; - } + def->default_value = new ConfigOptionPoints { Pointf(0,0) }; def = this->add("extrusion_axis", coString); def->label = "Extrusion axis"; @@ -334,11 +298,7 @@ PrintConfigDef::PrintConfigDef() def->label = "Extrusion multiplier"; def->tooltip = "This factor changes the amount of flow proportionally. You may need to tweak this setting to get nice surface finish and correct single wall widths. Usual values are between 0.9 and 1.1. If you think you need to change this more, check filament diameter and your firmware E steps."; def->cli = "extrusion-multiplier=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(1); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 1. }; def = this->add("extrusion_width", coFloatOrPercent); def->label = "Default extrusion width"; @@ -352,11 +312,7 @@ PrintConfigDef::PrintConfigDef() def->label = "Keep fan always on"; def->tooltip = "If this is enabled, fan will never be disabled and will be kept running at least at its minimum speed. Useful for PLA, harmful for ABS."; def->cli = "fan-always-on!"; - { - ConfigOptionBools* opt = new ConfigOptionBools(); - opt->values.push_back(false); - def->default_value = opt; - } + def->default_value = new ConfigOptionBools { false }; def = this->add("fan_below_layer_time", coInts); def->label = "Enable fan if layer print time is below"; @@ -366,22 +322,14 @@ PrintConfigDef::PrintConfigDef() def->width = 60; def->min = 0; def->max = 1000; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(60); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 60 }; def = this->add("filament_colour", coStrings); def->label = "Color"; def->tooltip = "This is only used in the Slic3r interface as a visual help."; def->cli = "filament-color=s@"; def->gui_type = "color"; - { - ConfigOptionStrings* opt = new ConfigOptionStrings(); - opt->values.push_back("#FFFFFF"); - def->default_value = opt; - } + def->default_value = new ConfigOptionStrings { "#29b2b2" }; def = this->add("filament_notes", coStrings); def->label = "Filament notes"; @@ -390,11 +338,7 @@ PrintConfigDef::PrintConfigDef() def->multiline = true; def->full_width = true; def->height = 130; - { - ConfigOptionStrings* opt = new ConfigOptionStrings(); - opt->values.push_back(""); - def->default_value = opt; - } + def->default_value = new ConfigOptionStrings { "" }; def = this->add("filament_max_volumetric_speed", coFloats); def->label = "Max volumetric speed"; @@ -402,11 +346,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = "mm³/s"; def->cli = "filament-max-volumetric-speed=f@"; def->min = 0; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0.f); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0. }; def = this->add("filament_diameter", coFloats); def->label = "Diameter"; @@ -414,11 +354,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = "mm"; def->cli = "filament-diameter=f@"; def->min = 0; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(3); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 3. }; def = this->add("filament_density", coFloats); def->label = "Density"; @@ -426,11 +362,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = "g/cm^3"; def->cli = "filament-density=f@"; def->min = 0; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0. }; def = this->add("filament_type", coStrings); def->label = "Filament type"; @@ -447,21 +379,13 @@ PrintConfigDef::PrintConfigDef() def->enum_values.push_back("EDGE"); def->enum_values.push_back("NGEN"); def->enum_values.push_back("PVA"); - { - ConfigOptionStrings* opt = new ConfigOptionStrings(); - opt->values.push_back("PLA"); - def->default_value = opt; - } + def->default_value = new ConfigOptionStrings { "PLA" }; def = this->add("filament_soluble", coBools); def->label = "Soluble material"; def->tooltip = "Soluble material is most likely used for a soluble support."; def->cli = "filament-soluble!"; - { - ConfigOptionBools* opt = new ConfigOptionBools(); - opt->values.push_back(false); - def->default_value = opt; - } + def->default_value = new ConfigOptionBools { false }; def = this->add("filament_cost", coFloats); def->label = "Cost"; @@ -469,15 +393,11 @@ PrintConfigDef::PrintConfigDef() def->sidetext = "money/kg"; def->cli = "filament-cost=f@"; def->min = 0; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0); - def->default_value = opt; - } - - def = this->add("filament_settings_id", coString); - def->default_value = new ConfigOptionString(""); + def->default_value = new ConfigOptionFloats { 0. }; + def = this->add("filament_settings_id", coStrings); + def->default_value = new ConfigOptionStrings { "" }; + def = this->add("fill_angle", coFloat); def->label = "Fill angle"; def->category = "Infill"; @@ -574,11 +494,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "first-layer-bed-temperature=i@"; def->max = 0; def->max = 300; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 0 }; def = this->add("first_layer_extrusion_width", coFloatOrPercent); def->label = "First layer"; @@ -612,11 +528,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "first-layer-temperature=i@"; def->min = 0; def->max = 500; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(200); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 200 }; def = this->add("gap_fill_speed", coFloat); def->label = "Gap fill"; @@ -757,11 +669,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "max-fan-speed=i@"; def->min = 0; def->max = 100; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(100); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 100 }; def = this->add("max_layer_height", coFloats); def->label = "Max"; @@ -769,11 +677,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = "mm"; def->cli = "max-layer-height=f@"; def->min = 0; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0. }; def = this->add("max_print_speed", coFloat); def->label = "Max print speed"; @@ -816,11 +720,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "min-fan-speed=i@"; def->min = 0; def->max = 100; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(35); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 35 }; def = this->add("min_layer_height", coFloats); def->label = "Min"; @@ -828,11 +728,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = "mm"; def->cli = "min-layer-height=f@"; def->min = 0; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0.07); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0.07 }; def = this->add("min_print_speed", coFloats); def->label = "Min print speed"; @@ -840,11 +736,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = "mm/s"; def->cli = "min-print-speed=f@"; def->min = 0; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(10.); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 10. }; def = this->add("min_skirt_length", coFloat); def->label = "Minimum extrusion length"; @@ -868,11 +760,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "This is the diameter of your extruder nozzle (for example: 0.5, 0.35 etc.)"; def->sidetext = "mm"; def->cli = "nozzle-diameter=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0.5); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0.5 }; def = this->add("octoprint_apikey", coString); def->label = "API Key"; @@ -1003,32 +891,20 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "Retraction is not triggered when travel moves are shorter than this length."; def->sidetext = "mm"; def->cli = "retract-before-travel=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(2); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 2. }; def = this->add("retract_before_wipe", coPercents); def->label = "Retract amount before wipe"; def->tooltip = "With bowden extruders, it may be wise to do some amount of quick retract before doing the wipe movement."; def->sidetext = "%"; def->cli = "retract-before-wipe=s@"; - { - ConfigOptionPercents* opt = new ConfigOptionPercents(); - opt->values.push_back(0.f); - def->default_value = opt; - } + def->default_value = new ConfigOptionPercents { 0. }; def = this->add("retract_layer_change", coBools); def->label = "Retract on layer change"; def->tooltip = "This flag enforces a retraction whenever a Z move is done."; def->cli = "retract-layer-change!"; - { - ConfigOptionBools* opt = new ConfigOptionBools(); - opt->values.push_back(false); - def->default_value = opt; - } + def->default_value = new ConfigOptionBools { false }; def = this->add("retract_length", coFloats); def->label = "Length"; @@ -1036,11 +912,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "When retraction is triggered, filament is pulled back by the specified amount (the length is measured on raw filament, before it enters the extruder)."; def->sidetext = "mm (zero to disable)"; def->cli = "retract-length=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(2); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 2. }; def = this->add("retract_length_toolchange", coFloats); def->label = "Length"; @@ -1048,22 +920,14 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "When retraction is triggered before changing tool, filament is pulled back by the specified amount (the length is measured on raw filament, before it enters the extruder)."; def->sidetext = "mm (zero to disable)"; def->cli = "retract-length-toolchange=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(10); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 10. }; def = this->add("retract_lift", coFloats); def->label = "Lift Z"; def->tooltip = "If you set this to a positive value, Z is quickly raised every time a retraction is triggered. When using multiple extruders, only the setting for the first extruder will be considered."; def->sidetext = "mm"; def->cli = "retract-lift=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_lift_above", coFloats); def->label = "Above Z"; @@ -1071,11 +935,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "If you set this to a positive value, Z lift will only take place above the specified absolute Z. You can tune this setting for skipping lift on the first layers."; def->sidetext = "mm"; def->cli = "retract-lift-above=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_lift_below", coFloats); def->label = "Below Z"; @@ -1083,33 +943,21 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "If you set this to a positive value, Z lift will only take place below the specified absolute Z. You can tune this setting for limiting lift to the first layers."; def->sidetext = "mm"; def->cli = "retract-lift-below=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_restart_extra", coFloats); def->label = "Extra length on restart"; def->tooltip = "When the retraction is compensated after the travel move, the extruder will push this additional amount of filament. This setting is rarely needed."; def->sidetext = "mm"; def->cli = "retract-restart-extra=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_restart_extra_toolchange", coFloats); def->label = "Extra length on restart"; def->tooltip = "When the retraction is compensated after changing tool, the extruder will push this additional amount of filament."; def->sidetext = "mm"; def->cli = "retract-restart-extra-toolchange=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0. }; def = this->add("retract_speed", coFloats); def->label = "Retraction Speed"; @@ -1117,11 +965,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "The speed for retractions (it only applies to the extruder motor)."; def->sidetext = "mm/s"; def->cli = "retract-speed=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(40); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 40. }; def = this->add("deretract_speed", coFloats); def->label = "Deretraction Speed"; @@ -1129,11 +973,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "The speed for loading of a filament into extruder after retraction (it only applies to the extruder motor). If left to zero, the retraction speed is used."; def->sidetext = "mm/s"; def->cli = "retract-speed=f@"; - { - ConfigOptionFloats* opt = new ConfigOptionFloats(); - opt->values.push_back(0); - def->default_value = opt; - } + def->default_value = new ConfigOptionFloats { 0. }; def = this->add("seam_position", coEnum); def->label = "Seam position"; @@ -1227,11 +1067,7 @@ PrintConfigDef::PrintConfigDef() def->width = 60; def->min = 0; def->max = 1000; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(5); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 5 }; def = this->add("small_perimeter_speed", coFloatOrPercent); def->label = "Small perimeters"; @@ -1327,11 +1163,7 @@ PrintConfigDef::PrintConfigDef() def->multiline = true; def->full_width = true; def->height = 120; - { - ConfigOptionStrings* opt = new ConfigOptionStrings(); - opt->values.push_back("; Filament gcode\n"); - def->default_value = opt; - } + def->default_value = new ConfigOptionStrings { "; Filament gcode\n" }; def = this->add("single_extruder_multi_material", coBool); def->label = "Single Extruder Multi Material"; @@ -1522,11 +1354,7 @@ PrintConfigDef::PrintConfigDef() def->full_label = "Temperature"; def->max = 0; def->max = 500; - { - ConfigOptionInts* opt = new ConfigOptionInts(); - opt->values.push_back(200); - def->default_value = opt; - } + def->default_value = new ConfigOptionInts { 200 }; def = this->add("thin_walls", coBool); def->label = "Detect thin walls"; @@ -1619,11 +1447,7 @@ PrintConfigDef::PrintConfigDef() def->label = "Wipe while retracting"; def->tooltip = "This flag will move the nozzle while retracting to minimize the possible blob on leaky extruders."; def->cli = "wipe!"; - { - ConfigOptionBools* opt = new ConfigOptionBools(); - opt->values.push_back(false); - def->default_value = opt; - } + def->default_value = new ConfigOptionBools { false }; def = this->add("wipe_tower", coBool); def->label = "Enable"; @@ -1798,7 +1622,7 @@ std::string DynamicPrintConfig::validate() { // Full print config is initialized from the defaults. FullPrintConfig fpc; - fpc.apply(*this); + fpc.apply(*this, true); // Verify this print options through the FullPrintConfig. return fpc.validate(); } diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index ac6021ff9..9e4043818 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -34,7 +34,7 @@ extern std::string normalize_utf8_nfc(const char *src); extern std::string timestamp_str(); // Standard "generated by Slic3r version xxx timestamp xxx" header string, // to be placed at the top of Slic3r generated files. -inline std::string header_slic3r_generated() { return std::string("generated by Slic3r " SLIC3R_VERSION " on ") + timestamp_str(); } +inline std::string header_slic3r_generated() { return std::string("generated by " SLIC3R_FORK_NAME " " SLIC3R_VERSION " " ) + timestamp_str(); } // Compute the next highest power of 2 of 32-bit v // http://graphics.stanford.edu/~seander/bithacks.html diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 4dcd3855a..e10e136d1 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -237,21 +237,13 @@ std::string normalize_utf8_nfc(const char *src) std::string timestamp_str() { -#if 1 - std::time_t now; - time(&now); - char buf[sizeof "0000-00-00 00:00:00"]; - strftime(buf, sizeof(buf), "%F %T", gmtime(&now)); -#else const auto now = boost::posix_time::second_clock::local_time(); const auto date = now.date(); char buf[2048]; sprintf(buf, "on %04d-%02d-%02d at %02d:%02d:%02d", - SLIC3R_VERSION, // Local date in an ANSII format. int(now.date().year()), int(now.date().month()), int(now.date().day()), int(now.time_of_day().hours()), int(now.time_of_day().minutes()), int(now.time_of_day().seconds())); -#endif return buf; } diff --git a/xs/src/slic3r/GUI/AppConfig.cpp b/xs/src/slic3r/GUI/AppConfig.cpp index b0a94b486..b61ce89d2 100644 --- a/xs/src/slic3r/GUI/AppConfig.cpp +++ b/xs/src/slic3r/GUI/AppConfig.cpp @@ -143,7 +143,7 @@ void AppConfig::update_last_output_dir(const std::string &dir) std::string AppConfig::config_path() { - return boost::filesystem::canonical(boost::filesystem::path(Slic3r::data_dir()) / "config.ini").make_preferred().string(); + return (boost::filesystem::path(Slic3r::data_dir()) / "slic3r.ini").make_preferred().string(); } bool AppConfig::exists() diff --git a/xs/src/slic3r/GUI/AppConfig.hpp b/xs/src/slic3r/GUI/AppConfig.hpp index d64680011..c6d7766a4 100644 --- a/xs/src/slic3r/GUI/AppConfig.hpp +++ b/xs/src/slic3r/GUI/AppConfig.hpp @@ -16,9 +16,9 @@ public: // Override missing or keys with their defaults. void set_defaults(); - // Load the config.ini from a user profile directory (or a datadir, if configured). + // Load the slic3r.ini from a user profile directory (or a datadir, if configured). void load(); - // Store the config.ini into a user profile directory (or a datadir, if configured). + // Store the slic3r.ini into a user profile directory (or a datadir, if configured). void save(); // Does this config need to be saved? @@ -62,6 +62,9 @@ public: bool has(const std::string &key) const { return this->has("", key); } + void clear_section(const std::string §ion) + { m_storage[section].clear(); } + // return recent/skein_directory or recent/config_directory or empty string. std::string get_last_dir() const; void update_config_dir(const std::string &dir); diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index fcc20cd9c..d02653a4a 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -28,6 +28,39 @@ namespace Slic3r { +ConfigFileType guess_config_file_type(const boost::property_tree::ptree &tree) +{ + size_t app_config = 0; + size_t bundle = 0; + size_t config = 0; + for (const boost::property_tree::ptree::value_type &v : tree) { + if (v.second.empty()) { + if (v.first == "background_processing" || + v.first == "last_output_path" || + v.first == "no_controller" || + v.first == "no_defaults") + ++ app_config; + else if (v.first == "nozzle_diameter" || + v.first == "filament_diameter") + ++ config; + } else if (boost::algorithm::starts_with(v.first, "print:") || + boost::algorithm::starts_with(v.first, "filament:") || + boost::algorithm::starts_with(v.first, "printer:") || + v.first == "settings") + ++ bundle; + else if (v.first == "presets") { + ++ app_config; + ++ bundle; + } else if (v.first == "recent") { + for (auto &kvp : v.second) + if (kvp.first == "config_directory" || kvp.first == "skein_directory") + ++ app_config; + } + } + return (app_config > bundle && app_config > config) ? CONFIG_FILE_TYPE_APP_CONFIG : + (bundle > config) ? CONFIG_FILE_TYPE_CONFIG_BUNDLE : CONFIG_FILE_TYPE_CONFIG; +} + // Suffix to be added to a modified preset name in the combo box. static std::string g_suffix_modified = " (modified)"; const std::string& Preset::suffix_modified() @@ -43,40 +76,26 @@ std::string Preset::remove_suffix_modified(const std::string &name) name; } -// Load keys from a config file or a G-code. -// Throw exceptions with reasonable messages if something goes wrong. -void Preset::load_config_file(DynamicPrintConfig &config, const std::string &path) +void Preset::set_num_extruders(DynamicPrintConfig &config, unsigned int num_extruders) { - try { - if (boost::algorithm::iends_with(path, ".gcode") || boost::algorithm::iends_with(path, ".g")) - config.load_from_gcode(path); - else - config.load(path); - } catch (const std::ifstream::failure&) { - throw std::runtime_error(std::string("The selected preset does not exist anymore: ") + path); - } catch (const std::runtime_error&) { - throw std::runtime_error(std::string("Failed loading the preset file: ") + path); - } - - // Update new extruder fields at the printer profile. - auto keys = config.keys(); const auto &defaults = FullPrintConfig::defaults(); - if (std::find(keys.begin(), keys.end(), "nozzle_diameter") != keys.end()) { - // Loaded the Printer settings. Verify, that all extruder dependent values have enough values. - auto *nozzle_diameter = dynamic_cast(config.option("nozzle_diameter")); - size_t num_extruders = nozzle_diameter->values.size(); - auto *deretract_speed = dynamic_cast(config.option("deretract_speed")); - deretract_speed->values.resize(num_extruders, deretract_speed->values.empty() ? - defaults.deretract_speed.values.front() : deretract_speed->values.front()); - auto *extruder_colour = dynamic_cast(config.option("extruder_colour")); - extruder_colour->values.resize(num_extruders, extruder_colour->values.empty() ? - defaults.extruder_colour.values.front() : extruder_colour->values.front()); - auto *retract_before_wipe = dynamic_cast(config.option("retract_before_wipe")); - retract_before_wipe->values.resize(num_extruders, retract_before_wipe->values.empty() ? - defaults.retract_before_wipe.values.front() : retract_before_wipe->values.front()); + for (const std::string &key : Preset::nozzle_options()) { + auto *opt = config.option(key, false); + assert(opt != nullptr); + assert(opt->is_vector()); + static_cast(opt)->resize(num_extruders, defaults.option(key)); } } +// Update new extruder fields at the printer profile. +void Preset::normalize(DynamicPrintConfig &config) +{ + auto *nozzle_diameter = dynamic_cast(config.option("nozzle_diameter")); + if (nozzle_diameter != nullptr) + // Loaded the Printer settings. Verify, that all extruder dependent values have enough values. + set_num_extruders(config, (unsigned int)nozzle_diameter->values.size()); +} + // Load a config file, return a C++ class Slic3r::DynamicPrintConfig with $keys initialized from the config file. // In case of a "default" config item, return the default values. DynamicPrintConfig& Preset::load(const std::vector &keys) @@ -84,9 +103,17 @@ DynamicPrintConfig& Preset::load(const std::vector &keys) // Set the configuration from the defaults. Slic3r::FullPrintConfig defaults; this->config.apply_only(defaults, keys.empty() ? defaults.keys() : keys); - if (! this->is_default) + if (! this->is_default) { // Load the preset file, apply preset values on top of defaults. - load_config_file(this->config, this->file); + try { + this->config.load(this->file); + Preset::normalize(this->config); + } catch (const std::ifstream::failure&) { + throw std::runtime_error(std::string("The selected preset does not exist anymore: ") + this->file); + } catch (const std::runtime_error&) { + throw std::runtime_error(std::string("Failed loading the preset file: ") + this->file); + } + } this->loaded = true; return this->config; } @@ -111,6 +138,74 @@ bool Preset::enable_compatible(const std::string &active_printer) return this->is_visible; } +const std::vector& Preset::print_options() +{ + static std::vector s_opts { + "layer_height", "first_layer_height", "perimeters", "spiral_vase", "top_solid_layers", "bottom_solid_layers", + "extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs", + "seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "external_fill_pattern", + "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle", + "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", "max_print_speed", + "max_volumetric_speed", "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative", + "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed", + "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed", + "bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration", + "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", + "min_skirt_length", "brim_width", "support_material", "support_material_threshold", "support_material_enforce_layers", + "raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", + "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", + "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", + "support_material_buildplate_only", "dont_support_bridges", "notes", "complete_objects", "extruder_clearance_radius", + "extruder_clearance_height", "gcode_comments", "output_filename_format", "post_process", "perimeter_extruder", + "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", + "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", + "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", + "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects", + "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", + "wipe_tower_width", "wipe_tower_per_color_wipe" + }; + return s_opts; +} + +const std::vector& Preset::filament_options() +{ + static std::vector s_opts { + "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed", + "extrusion_multiplier", "filament_density", "filament_cost", "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" + }; + return s_opts; +} + +const std::vector& Preset::printer_options() +{ + static std::vector s_opts; + if (s_opts.empty()) { + s_opts = { + "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", + "octoprint_host", "octoprint_apikey", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", + "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", + "printer_notes" + }; + s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end()); + } + return s_opts; +} + +const std::vector& Preset::nozzle_options() +{ + // ConfigOptionFloats, ConfigOptionPercents, ConfigOptionBools, ConfigOptionStrings + static std::vector s_opts { + "nozzle_diameter", "min_layer_height", "max_layer_height", "extruder_offset", + "retract_length", "retract_lift", "retract_lift_above", "retract_lift_below", "retract_speed", "deretract_speed", + "retract_before_wipe", "retract_restart_extra", "retract_before_travel", "wipe", + "retract_layer_change", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour" + }; + return s_opts; +} + PresetCollection::PresetCollection(Preset::Type type, const std::vector &keys) : m_type(type), m_edited_preset(type, "", false), @@ -203,6 +298,7 @@ void PresetCollection::save_current_preset(const std::string &new_name) } m_edited_preset = m_presets[m_idx_selected]; m_presets[m_idx_selected].save(); + m_presets.front().is_visible = ! m_default_suppressed || m_idx_selected > 0; } void PresetCollection::delete_current_preset() @@ -254,7 +350,7 @@ void PresetCollection::set_default_suppressed(bool default_suppressed) { if (m_default_suppressed != default_suppressed) { m_default_suppressed = default_suppressed; - m_presets.front().is_visible = ! default_suppressed || m_presets.size() > 1; + m_presets.front().is_visible = ! default_suppressed || (m_presets.size() > 1 && m_idx_selected > 0); } } @@ -320,6 +416,7 @@ bool PresetCollection::update_dirty_ui(wxItemContainer *ui) bool was_dirty = this->get_selected_preset().is_dirty; bool is_dirty = current_is_dirty(); this->get_selected_preset().is_dirty = is_dirty; + this->get_edited_preset().is_dirty = is_dirty; // 2) Update the labels. for (unsigned int ui_id = 0; ui_id < ui->GetCount(); ++ ui_id) { std::string old_label = ui->GetString(ui_id).utf8_str().data(); @@ -345,6 +442,7 @@ Preset& PresetCollection::select_preset(size_t idx) idx = first_visible_idx(); m_idx_selected = idx; m_edited_preset = m_presets[idx]; + m_presets.front().is_visible = ! m_default_suppressed || m_idx_selected > 0; return m_presets[idx]; } diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp index 8bad02ce8..85280eccf 100644 --- a/xs/src/slic3r/GUI/Preset.hpp +++ b/xs/src/slic3r/GUI/Preset.hpp @@ -13,6 +13,16 @@ class wxItemContainer; namespace Slic3r { +enum ConfigFileType +{ + CONFIG_FILE_TYPE_UNKNOWN, + CONFIG_FILE_TYPE_APP_CONFIG, + CONFIG_FILE_TYPE_CONFIG, + CONFIG_FILE_TYPE_CONFIG_BUNDLE, +}; + +extern ConfigFileType guess_config_file_type(const boost::property_tree::ptree &tree); + class Preset { public: @@ -70,13 +80,25 @@ public: // Mark this preset as visible if it is compatible with active_printer. bool enable_compatible(const std::string &active_printer); + // Resize the extruder specific fields, initialize them with the content of the 1st extruder. + void set_num_extruders(unsigned int n) { set_num_extruders(this->config, n); } + // Sort lexicographically by a preset name. The preset name shall be unique across a single PresetCollection. bool operator<(const Preset &other) const { return this->name < other.name; } + static const std::vector& print_options(); + static const std::vector& filament_options(); + // Printer options contain the nozzle options. + static const std::vector& printer_options(); + // Nozzle options of the printer options. + static const std::vector& nozzle_options(); + protected: friend class PresetCollection; friend class PresetBundle; - static void load_config_file(DynamicPrintConfig &config, const std::string &path); + static void normalize(DynamicPrintConfig &config); + // Resize the extruder specific vectors () + static void set_num_extruders(DynamicPrintConfig &config, unsigned int n); static const std::string& suffix_modified(); static std::string remove_suffix_modified(const std::string &name); }; @@ -117,7 +139,7 @@ public: // Enable / disable the "- default -" preset. void set_default_suppressed(bool default_suppressed); - bool is_default_suppressed() const { return m_default_suppressed || m_presets.size() <= 1; } + bool is_default_suppressed() const { return m_default_suppressed; } // Select a preset. If an invalid index is provided, the first visible preset is selected. Preset& select_preset(size_t idx); diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp index 4c229edab..4abb7a88f 100644 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ b/xs/src/slic3r/GUI/PresetBundle.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -23,9 +24,9 @@ namespace Slic3r { PresetBundle::PresetBundle() : - prints(Preset::TYPE_PRINT, print_options()), - filaments(Preset::TYPE_FILAMENT, filament_options()), - printers(Preset::TYPE_PRINTER, printer_options()), + prints(Preset::TYPE_PRINT, Preset::print_options()), + filaments(Preset::TYPE_FILAMENT, Preset::filament_options()), + printers(Preset::TYPE_PRINTER, Preset::printer_options()), m_bitmapCompatible(new wxBitmap), m_bitmapIncompatible(new wxBitmap) { @@ -66,7 +67,7 @@ void PresetBundle::setup_directories() throw std::runtime_error(std::string("datadir does not exist: ") + Slic3r::data_dir()); std::initializer_list names = { "print", "filament", "printer" }; for (const char *name : names) { - boost::filesystem::path subdir = (dir / subdir).make_preferred(); + boost::filesystem::path subdir = (dir / name).make_preferred(); if (! boost::filesystem::is_directory(subdir) && ! boost::filesystem::create_directory(subdir)) throw std::runtime_error(std::string("Slic3r was unable to create its data directory at ") + subdir.string()); @@ -87,28 +88,33 @@ void PresetBundle::load_selections(const AppConfig &config) { prints.select_preset_by_name(config.get("presets", "print"), true); filaments.select_preset_by_name(config.get("presets", "filament"), true); + printers.select_preset_by_name(config.get("presets", "printer"), true); + auto *nozzle_diameter = dynamic_cast(printers.get_selected_preset().config.option("nozzle_diameter")); + size_t num_extruders = nozzle_diameter->values.size(); this->set_filament_preset(0, filaments.get_selected_preset().name); - for (int i = 1; i < 1000; ++ i) { + for (unsigned int i = 1; i < (unsigned int)num_extruders; ++ i) { char name[64]; sprintf(name, "filament_%d", i); if (! config.has("presets", name)) break; - this->set_filament_preset(i, name); + this->set_filament_preset(i, config.get("presets", name)); } - printers.select_preset_by_name(config.get("presets", "printer"), true); } // Export selections (current print, current filaments, current printer) into config.ini void PresetBundle::export_selections(AppConfig &config) { - config.set("presets", "print", prints .get_selected_preset().name); - config.set("presets", "filament", filaments.get_selected_preset().name); - for (int i = 1; i < 1000; ++ i) { + assert(filament_presets.size() >= 1); + assert(filament_presets.size() > 1 || filaments.get_selected_preset().name == filament_presets.front()); + config.clear_section("presets"); + config.set("presets", "print", prints.get_selected_preset().name); + config.set("presets", "filament", filament_presets.front()); + for (int i = 1; i < filament_presets.size(); ++i) { char name[64]; sprintf(name, "filament_%d", i); config.set("presets", name, filament_presets[i]); } - config.set("presets", "printer", printers .get_selected_preset().name); + config.set("presets", "printer", printers.get_selected_preset().name); } bool PresetBundle::load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible) @@ -175,7 +181,7 @@ DynamicPrintConfig PresetBundle::full_config() const std::string key = std::string(keys[i]) + "_extruder"; auto *opt = dynamic_cast(out.option(key, false)); assert(opt != nullptr); - opt->value = std::min(opt->value, std::min(0, int(num_extruders) - 1)); + opt->value = boost::algorithm::clamp(opt->value, 0, int(num_extruders)); } return out; @@ -186,16 +192,44 @@ DynamicPrintConfig PresetBundle::full_config() const // In the future the configuration will likely be read from an AMF file as well. // If the file is loaded successfully, its print / filament / printer profiles will be activated. void PresetBundle::load_config_file(const std::string &path) +{ + // 1) Try to load the config file into a boost property tree. + boost::property_tree::ptree tree; + try { + boost::nowide::ifstream ifs(path); + boost::property_tree::read_ini(ifs, tree); + } catch (const std::ifstream::failure&) { + throw std::runtime_error(std::string("The config file cannot be loaded: ") + path); + } catch (const std::runtime_error&) { + throw std::runtime_error(std::string("Failed loading the preset file: ") + path); + } + + // 2) Continue based on the type of the configuration file. + ConfigFileType config_file_type = guess_config_file_type(tree); + switch (config_file_type) { + case CONFIG_FILE_TYPE_UNKNOWN: + throw std::runtime_error(std::string("Unknown configuration file type: ") + path); + case CONFIG_FILE_TYPE_APP_CONFIG: + throw std::runtime_error(std::string("Invalid configuration file: ") + path + ". This is an application config file."); + case CONFIG_FILE_TYPE_CONFIG: + load_config_file_config(path, tree); + break; + case CONFIG_FILE_TYPE_CONFIG_BUNDLE: + load_config_file_config_bundle(path, tree); + break; + } +} + +// Load a config file from a boost property_tree. This is a private method called from load_config_file. +void PresetBundle::load_config_file_config(const std::string &path, const boost::property_tree::ptree &tree) { // 1) Initialize a config from full defaults. DynamicPrintConfig config; config.apply(FullPrintConfig()); + config.load(tree); + Preset::normalize(config); - // 2) Try to load the config file. - // Throw exceptions with reasonable messages if something goes wrong. - Preset::load_config_file(config, path); - - // 3) Create a name from the file name. + // 2) Create a name from the file name. // Keep the suffix (.ini, .gcode, .amf, .3mf etc) to differentiate it from the normal profiles. std::string name = boost::filesystem::path(path).filename().string(); @@ -245,6 +279,32 @@ void PresetBundle::load_config_file(const std::string &path) } } +// Load the active configuration of a config bundle from a boost property_tree. This is a private method called from load_config_file. +void PresetBundle::load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree) +{ + // 1) Load the config bundle into a temp data. + PresetBundle tmp_bundle; + tmp_bundle.load_configbundle(path); + + // 2) Extract active configs from the config bundle, copy them and activate them in this bundle. + if (tmp_bundle.prints.get_selected_preset().is_default) + this->prints.select_preset(0); + else { + std::string new_name = tmp_bundle.prints.get_selected_preset().name; + Preset *existing = this->prints.find_preset(new_name, false); + if (existing == nullptr) { + // Save under the new_name. + } else if (existing->config == tmp_bundle.prints.get_selected_preset().config) { + // Don't save as the config exists in the current bundle and its content is the same. + new_name.clear(); + } else { + // Generate a new unique name. + } + if (! new_name.empty()) + this->prints.load_preset(path, new_name, tmp_bundle.prints.get_selected_preset().config); + } +} + // Load a config bundle file, into presets and store the loaded presets into separate files // of the local configuration directory. size_t PresetBundle::load_configbundle(const std::string &path) @@ -333,17 +393,18 @@ size_t PresetBundle::load_configbundle(const std::string &path) void PresetBundle::update_multi_material_filament_presets() { // Verify and select the filament presets. - auto *nozzle_diameter = static_cast(printers.get_selected_preset().config.option("nozzle_diameter")); + auto *nozzle_diameter = static_cast(printers.get_edited_preset().config.option("nozzle_diameter")); size_t num_extruders = nozzle_diameter->values.size(); // Verify validity of the current filament presets. + printf("PresetBundle::update_multi_material_filament_presets, old: %d, new: %d\n", int(this->filament_presets.size()), int(num_extruders)); for (size_t i = 0; i < std::min(this->filament_presets.size(), num_extruders); ++ i) this->filament_presets[i] = this->filaments.find_preset(this->filament_presets[i], true)->name; // Append the rest of filament presets. - if (this->filament_presets.size() < num_extruders) - this->filament_presets.resize(num_extruders, this->filaments.first_visible().name); +// if (this->filament_presets.size() < num_extruders) + this->filament_presets.resize(num_extruders, this->filament_presets.empty() ? this->filaments.first_visible().name : this->filament_presets.back()); } -void PresetBundle::export_configbundle(const std::string &path, const DynamicPrintConfig &settings) +void PresetBundle::export_configbundle(const std::string &path) //, const DynamicPrintConfig &settings { boost::nowide::ofstream c; c.open(path, std::ios::out | std::ios::trunc); @@ -358,14 +419,14 @@ void PresetBundle::export_configbundle(const std::string &path, const DynamicPri if (preset.is_default || preset.is_external) // Only export the common presets, not external files or the default preset. continue; - c << "[" << presets.name() << ":" << preset.name << "]" << std::endl; + c << std::endl << "[" << presets.name() << ":" << preset.name << "]" << std::endl; for (const std::string &opt_key : preset.config.keys()) c << opt_key << " = " << preset.config.serialize(opt_key) << std::endl; } } // Export the names of the active presets. - c << "[presets]" << std::endl; + c << std::endl << "[presets]" << std::endl; c << "print = " << this->prints.get_selected_preset().name << std::endl; c << "printer = " << this->printers.get_selected_preset().name << std::endl; for (size_t i = 0; i < this->filament_presets.size(); ++ i) { @@ -377,12 +438,13 @@ void PresetBundle::export_configbundle(const std::string &path, const DynamicPri c << "filament" << suffix << " = " << this->filament_presets[i] << std::endl; } +#if 0 // Export the following setting values from the provided setting repository. static const char *settings_keys[] = { "autocenter" }; - c << "[presets]" << std::endl; - c << "print = " << this->prints.get_selected_preset().name << std::endl; + c << "[settings]" << std::endl; for (size_t i = 0; i < sizeof(settings_keys) / sizeof(settings_keys[0]); ++ i) c << settings_keys[i] << " = " << settings.serialize(settings_keys[i]) << std::endl; +#endif c.close(); } @@ -530,69 +592,6 @@ void PresetBundle::update_platter_filament_ui_colors(unsigned int idx_extruder, ui->Thaw(); } -const std::vector& PresetBundle::print_options() -{ - const char *opts[] = { - "layer_height", "first_layer_height", "perimeters", "spiral_vase", "top_solid_layers", "bottom_solid_layers", - "extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs", - "seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "external_fill_pattern", - "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle", - "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", "max_print_speed", - "max_volumetric_speed", "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative", - "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed", - "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed", - "bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration", - "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", - "min_skirt_length", "brim_width", "support_material", "support_material_threshold", "support_material_enforce_layers", - "raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", - "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", - "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", - "support_material_buildplate_only", "dont_support_bridges", "notes", "complete_objects", "extruder_clearance_radius", - "extruder_clearance_height", "gcode_comments", "output_filename_format", "post_process", "perimeter_extruder", - "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", - "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", - "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", - "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects", - "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", - "wipe_tower_width", "wipe_tower_per_color_wipe" - }; - static std::vector s_opts; - if (s_opts.empty()) - s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); - return s_opts; -} - -const std::vector& PresetBundle::filament_options() -{ - const char *opts[] = { - "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed", - "extrusion_multiplier", "filament_density", "filament_cost", "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" - }; - static std::vector s_opts; - if (s_opts.empty()) - s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); - return s_opts; -} - -const std::vector& PresetBundle::printer_options() -{ - const char *opts[] = { - "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", - "octoprint_host", "octoprint_apikey", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", - "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", - "nozzle_diameter", "extruder_offset", "retract_length", "retract_lift", "retract_speed", "deretract_speed", - "retract_before_wipe", "retract_restart_extra", "retract_before_travel", "retract_layer_change", "wipe", - "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour", "printer_notes" - }; - static std::vector s_opts; - if (s_opts.empty()) - s_opts.assign(opts, opts + (sizeof(opts) / sizeof(opts[0]))); - return s_opts; -} - void PresetBundle::set_default_suppressed(bool default_suppressed) { prints.set_default_suppressed(default_suppressed); diff --git a/xs/src/slic3r/GUI/PresetBundle.hpp b/xs/src/slic3r/GUI/PresetBundle.hpp index b00e148f1..7add3055d 100644 --- a/xs/src/slic3r/GUI/PresetBundle.hpp +++ b/xs/src/slic3r/GUI/PresetBundle.hpp @@ -47,17 +47,13 @@ public: size_t load_configbundle(const std::string &path); // Export a config bundle file containing all the presets and the names of the active presets. - void export_configbundle(const std::string &path, const DynamicPrintConfig &settings); + void export_configbundle(const std::string &path); // , const DynamicPrintConfig &settings); // Update a filament selection combo box on the platter for an idx_extruder. void update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui); // Update the colors preview at the platter extruder combo box. void update_platter_filament_ui_colors(unsigned int idx_extruder, wxBitmapComboBox *ui); - static const std::vector& print_options(); - static const std::vector& filament_options(); - static const std::vector& printer_options(); - // Enable / disable the "- default -" preset. void set_default_suppressed(bool default_suppressed); @@ -70,6 +66,8 @@ public: void update_multi_material_filament_presets(); private: + void load_config_file_config(const std::string &path, const boost::property_tree::ptree &tree); + void load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree); bool load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible); // Indicator, that the preset is compatible with the selected printer. diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index dcf54bdbc..49981b74b 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -54,6 +54,8 @@ extern "C" { #ifdef _MSC_VER // Undef some of the macros set by Perl , which cause compilation errors on Win32 #undef connect + #undef link + #undef unlink #undef seek #undef send #undef write diff --git a/xs/xsp/GUI_AppConfig.xsp b/xs/xsp/GUI_AppConfig.xsp index c91711635..d220c3580 100644 --- a/xs/xsp/GUI_AppConfig.xsp +++ b/xs/xsp/GUI_AppConfig.xsp @@ -16,6 +16,7 @@ void load(); void save(); bool exists(); + bool dirty(); std::string get(char *name); void set(char *name, char *value); diff --git a/xs/xsp/GUI_Preset.xsp b/xs/xsp/GUI_Preset.xsp index 16dc3399e..34184a588 100644 --- a/xs/xsp/GUI_Preset.xsp +++ b/xs/xsp/GUI_Preset.xsp @@ -19,8 +19,9 @@ bool loaded() %code%{ RETVAL = THIS->loaded; %}; - Ref config_ref() %code%{ RETVAL = &THIS->config; %}; - Clone config() %code%{ RETVAL = &THIS->config; %}; + Ref config() %code%{ RETVAL = &THIS->config; %}; + + void set_num_extruders(int num_extruders); }; %name{Slic3r::GUI::PresetCollection} class PresetCollection { @@ -94,7 +95,9 @@ PresetCollection::presets_hash() void setup_directories(); void load_presets(const char *dir_path); + void load_config_file(const char *path); size_t load_configbundle(const char *path); + void export_configbundle(char *path); void set_default_suppressed(bool default_suppressed); void load_selections (AppConfig *config) %code%{ THIS->load_selections(*config); %};