diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index bfbeee88cb..022c206f11 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -87,8 +87,10 @@ sub load { my $class = shift; my ($file) = @_; - my $ini = __PACKAGE__->read_ini($file); - return $class->load_ini_hash($ini->{_}); + # legacy syntax of load() + my $config = $class->new; + $config->_load($file); + return $config; } sub load_ini_hash { @@ -186,13 +188,6 @@ sub as_ini { return $ini; } -sub save { - my $self = shift; - my ($file) = @_; - - __PACKAGE__->write_ini($file, $self->as_ini); -} - # this method is idempotent by design and only applies to ::DynamicConfig or ::Full # objects because it performs cross checks sub validate { diff --git a/src/slic3r.cpp b/src/slic3r.cpp index 23cd8c7ff7..8a75564cdb 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -24,17 +24,28 @@ main(const int argc, const char **argv) t_config_option_keys input_files; config.read_cli(argc, argv, &input_files); - // apply command line options to a more specific DynamicPrintConfig which provides normalize() - DynamicPrintConfig print_config; - print_config.apply(config, true); - print_config.normalize(); - // apply command line options to a more handy CLIConfig CLIConfig cli_config; cli_config.apply(config, true); - /* TODO: loop through the config files supplied on the command line (now stored in - cli_config), load each one, normalize it and apply it to print_config */ + DynamicPrintConfig print_config; + + // load config files supplied via --load + for (std::vector::const_iterator file = cli_config.load.values.begin(); + file != cli_config.load.values.end(); ++file) { + DynamicPrintConfig c; + c.load(*file); + c.normalize(); + print_config.apply(c); + } + + // apply command line options to a more specific DynamicPrintConfig which provides normalize() + // (command line options override --load files) + print_config.apply(config, true); + print_config.normalize(); + + // write config if requested + if (!cli_config.save.value.empty()) print_config.save(cli_config.save.value); // read input file(s) if any std::vector models; diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp index dbc3ca89b4..cac2010f4d 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/xs/src/libslic3r/Config.cpp @@ -1,8 +1,14 @@ #include "Config.hpp" #include // for setenv() #include +#include +#include +#include #include #include +#include +#include +#include #if defined(_WIN32) && !defined(setenv) && defined(_putenv_s) #define setenv(k, v, o) _putenv_s(k, v) @@ -116,7 +122,7 @@ ConfigBase::serialize(const t_config_option_key &opt_key) const { } bool -ConfigBase::set_deserialize(const t_config_option_key &opt_key, std::string str) { +ConfigBase::set_deserialize(const t_config_option_key &opt_key, std::string str, bool append) { const ConfigOptionDef* optdef = this->def->get(opt_key); if (optdef == NULL) throw "Calling set_deserialize() on unknown option"; if (!optdef->shortcut.empty()) { @@ -128,7 +134,7 @@ ConfigBase::set_deserialize(const t_config_option_key &opt_key, std::string str) ConfigOption* opt = this->option(opt_key, true); assert(opt != NULL); - return opt->deserialize(str); + return opt->deserialize(str, append); } double @@ -189,6 +195,38 @@ ConfigBase::option(const t_config_option_key &opt_key, bool create) { return this->optptr(opt_key, create); } +void +ConfigBase::load(const std::string &file) +{ + namespace pt = boost::property_tree; + pt::ptree tree; + pt::read_ini(file, tree); + BOOST_FOREACH(const pt::ptree::value_type &v, tree) { + this->set_deserialize(v.first.c_str(), v.second.get_value().c_str()); + } +} + +void +ConfigBase::save(const std::string &file) const +{ + using namespace std; + ofstream c; + c.open(file.c_str(), ios::out | ios::trunc); + + { + time_t now; + time(&now); + char buf[sizeof "0000-00-00 00:00:00"]; + strftime(buf, sizeof buf, "%F %T", gmtime(&now)); + c << "# generated by Slic3r " << SLIC3R_VERSION << " on " << buf << endl; + } + + t_config_option_keys my_keys = this->keys(); + for (t_config_option_keys::const_iterator opt_key = my_keys.begin(); opt_key != my_keys.end(); ++opt_key) + c << *opt_key << " = " << this->serialize(*opt_key) << endl; + c.close(); +} + DynamicConfig& DynamicConfig::operator= (DynamicConfig other) { this->swap(other); @@ -325,11 +363,11 @@ DynamicConfig::read_cli(const int argc, const char** argv, t_config_option_keys* opt->values.push_back(!boost::starts_with(token, "no-")); } else { // we expect one more token carrying the value - if (i == argc) { + if (i == (argc-1)) { printf("No value supplied for --%s\n", token.c_str()); exit(1); } - this->option(opt_key, true)->deserialize(argv[++i]); + this->set_deserialize(opt_key, argv[++i], true); } } else { extra->push_back(token); diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index acbbaf9ff2..7892e3358e 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -22,7 +22,7 @@ class ConfigOption { virtual ~ConfigOption() {}; virtual ConfigOption* clone() const = 0; virtual std::string serialize() const = 0; - virtual bool deserialize(std::string str) = 0; + virtual bool deserialize(std::string str, bool append = false) = 0; virtual void set(const ConfigOption &option) = 0; virtual int getInt() const { return 0; }; virtual double getFloat() const { return 0; }; @@ -90,7 +90,7 @@ class ConfigOptionFloat : public ConfigOptionSingle return ss.str(); }; - bool deserialize(std::string str) { + bool deserialize(std::string str, bool append = false) { std::istringstream iss(str); iss >> this->value; return !iss.fail(); @@ -123,8 +123,8 @@ class ConfigOptionFloats : public ConfigOptionVector return vv; }; - bool deserialize(std::string str) { - this->values.clear(); + bool deserialize(std::string str, bool append = false) { + if (!append) this->values.clear(); std::istringstream is(str); std::string item_str; while (std::getline(is, item_str, ',')) { @@ -153,7 +153,7 @@ class ConfigOptionInt : public ConfigOptionSingle return ss.str(); }; - bool deserialize(std::string str) { + bool deserialize(std::string str, bool append = false) { std::istringstream iss(str); iss >> this->value; return !iss.fail(); @@ -186,8 +186,8 @@ class ConfigOptionInts : public ConfigOptionVector return vv; }; - bool deserialize(std::string str) { - this->values.clear(); + bool deserialize(std::string str, bool append = false) { + if (!append) this->values.clear(); std::istringstream is(str); std::string item_str; while (std::getline(is, item_str, ',')) { @@ -222,7 +222,7 @@ class ConfigOptionString : public ConfigOptionSingle return str; }; - bool deserialize(std::string str) { + bool deserialize(std::string str, bool append = false) { // s/\\n/\n/g size_t pos = 0; while ((pos = str.find("\\n", pos)) != std::string::npos) { @@ -256,8 +256,8 @@ class ConfigOptionStrings : public ConfigOptionVector return this->values; }; - bool deserialize(std::string str) { - this->values.clear(); + bool deserialize(std::string str, bool append = false) { + if (!append) this->values.clear(); std::istringstream is(str); std::string item_str; while (std::getline(is, item_str, ';')) { @@ -286,7 +286,7 @@ class ConfigOptionPercent : public ConfigOptionFloat return s; }; - bool deserialize(std::string str) { + bool deserialize(std::string str, bool append = false) { // don't try to parse the trailing % since it's optional std::istringstream iss(str); iss >> this->value; @@ -327,7 +327,7 @@ class ConfigOptionFloatOrPercent : public ConfigOptionPercent return s; }; - bool deserialize(std::string str) { + bool deserialize(std::string str, bool append = false) { this->percent = str.find_first_of("%") != std::string::npos; std::istringstream iss(str); iss >> this->value; @@ -350,7 +350,7 @@ class ConfigOptionPoint : public ConfigOptionSingle return ss.str(); }; - bool deserialize(std::string str) { + bool deserialize(std::string str, bool append = false) { std::istringstream iss(str); iss >> this->value.x; iss.ignore(std::numeric_limits::max(), ','); @@ -388,8 +388,8 @@ class ConfigOptionPoints : public ConfigOptionVector return vv; }; - bool deserialize(std::string str) { - this->values.clear(); + bool deserialize(std::string str, bool append = false) { + if (!append) this->values.clear(); std::istringstream is(str); std::string point_str; while (std::getline(is, point_str, ',')) { @@ -421,7 +421,7 @@ class ConfigOptionBool : public ConfigOptionSingle return std::string(this->value ? "1" : "0"); }; - bool deserialize(std::string str) { + bool deserialize(std::string str, bool append = false) { this->value = (str.compare("1") == 0); return true; }; @@ -453,8 +453,8 @@ class ConfigOptionBools : public ConfigOptionVector return vv; }; - bool deserialize(std::string str) { - this->values.clear(); + bool deserialize(std::string str, bool append = false) { + if (!append) this->values.clear(); std::istringstream is(str); std::string item_str; while (std::getline(is, item_str, ',')) { @@ -483,7 +483,7 @@ class ConfigOptionEnum : public ConfigOptionSingle return ""; }; - bool deserialize(std::string str) { + bool deserialize(std::string str, bool append = false) { t_config_enum_values enum_keys_map = ConfigOptionEnum::get_enum_values(); if (enum_keys_map.count(str) == 0) return false; this->value = static_cast(enum_keys_map[str]); @@ -507,7 +507,7 @@ class ConfigOptionEnumGeneric : public ConfigOptionInt return ""; }; - bool deserialize(std::string str) { + bool deserialize(std::string str, bool append = false) { if (this->keys_map->count(str) == 0) return false; this->value = (*const_cast(this->keys_map))[str]; return true; @@ -596,10 +596,12 @@ class ConfigBase bool equals(ConfigBase &other); t_config_option_keys diff(ConfigBase &other); std::string serialize(const t_config_option_key &opt_key) const; - bool set_deserialize(const t_config_option_key &opt_key, std::string str); + bool set_deserialize(const t_config_option_key &opt_key, std::string str, bool append = false); double get_abs_value(const t_config_option_key &opt_key); double get_abs_value(const t_config_option_key &opt_key, double ratio_over); void setenv_(); + void load(const std::string &file); + void save(const std::string &file) const; }; class DynamicConfig : public virtual ConfigBase diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 90af82fbdf..461981cdaf 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1383,6 +1383,12 @@ CLIConfigDef::CLIConfigDef() def->cli = "info"; def->default_value = new ConfigOptionBool(false); + def = this->add("load", coStrings); + def->label = "Load config file"; + def->tooltip = "Load configuration from the specified file. It can be used more than once to load options from multiple files."; + def->cli = "load"; + def->default_value = new ConfigOptionStrings(); + def = this->add("output", coString); def->label = "Output File"; def->tooltip = "The file where the output will be written (if not specified, it will be based on the input file)."; @@ -1395,6 +1401,12 @@ CLIConfigDef::CLIConfigDef() def->cli = "rotate"; def->default_value = new ConfigOptionFloat(0); + def = this->add("save", coString); + def->label = "Save config file"; + def->tooltip = "Save configuration to the specified file."; + def->cli = "save"; + def->default_value = new ConfigOptionString(); + def = this->add("scale", coFloat); def->label = "Scale"; def->tooltip = "Scaling factor (default: 1)."; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 442af51c23..ccc3b14719 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -506,8 +506,10 @@ class CLIConfig ConfigOptionBool export_pov; ConfigOptionBool export_svg; ConfigOptionBool info; + ConfigOptionStrings load; ConfigOptionString output; ConfigOptionFloat rotate; + ConfigOptionString save; ConfigOptionFloat scale; CLIConfig() : ConfigBase(), StaticConfig() { @@ -520,8 +522,10 @@ class CLIConfig OPT_PTR(export_pov); OPT_PTR(export_svg); OPT_PTR(info); + OPT_PTR(load); OPT_PTR(output); OPT_PTR(rotate); + OPT_PTR(save); OPT_PTR(scale); return NULL; diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index a2db75999c..4bc6081918 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -38,6 +38,8 @@ void normalize(); %name{setenv} void setenv_(); double min_object_distance(); + %name{_load} void load(std::string file); + void save(std::string file); }; %name{Slic3r::Config::Static} class StaticPrintConfig { @@ -84,6 +86,8 @@ %}; %name{setenv} void setenv_(); double min_object_distance(); + %name{_load} void load(std::string file); + void save(std::string file); }; %package{Slic3r::Config};