Added C++ command line processing, thanks @alexrj and @loh

This commit is contained in:
bubnikv 2018-09-20 16:48:13 +02:00
parent 6ca5a18d05
commit add45a8f6e
11 changed files with 467 additions and 105 deletions

View file

@ -566,6 +566,103 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre
return opt;
}
void DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra)
{
std::vector<char*> args;
// push a bogus executable name (argv[0])
args.emplace_back(const_cast<char*>(""));
for (size_t i = 0; i < tokens.size(); ++ i)
args.emplace_back(const_cast<char *>(tokens[i].c_str()));
this->read_cli(args.size(), &args[0], extra);
}
bool DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra)
{
// cache the CLI option => opt_key mapping
std::map<std::string,std::string> opts;
for (const auto &oit : this->def()->options) {
std::string cli = oit.second.cli;
cli = cli.substr(0, cli.find("="));
boost::trim_right_if(cli, boost::is_any_of("!"));
std::vector<std::string> tokens;
boost::split(tokens, cli, boost::is_any_of("|"));
for (const std::string &t : tokens)
opts[t] = oit.first;
}
bool parse_options = true;
for (int i = 1; i < argc; ++ i) {
std::string token = argv[i];
// Store non-option arguments in the provided vector.
if (! parse_options || ! boost::starts_with(token, "-")) {
extra->push_back(token);
continue;
}
// Stop parsing tokens as options when -- is supplied.
if (token == "--") {
parse_options = false;
continue;
}
// Remove leading dashes
boost::trim_left_if(token, boost::is_any_of("-"));
// Remove the "no-" prefix used to negate boolean options.
bool no = false;
if (boost::starts_with(token, "no-")) {
no = true;
boost::replace_first(token, "no-", "");
}
// Read value when supplied in the --key=value form.
std::string value;
{
size_t equals_pos = token.find("=");
if (equals_pos != std::string::npos) {
value = token.substr(equals_pos+1);
token.erase(equals_pos);
}
}
// Look for the cli -> option mapping.
const auto it = opts.find(token);
if (it == opts.end()) {
printf("Warning: unknown option --%s\n", token.c_str());
// instead of continuing, return false to caller
// to stop execution and print usage
return false;
//continue;
}
const t_config_option_key opt_key = it->second;
const ConfigOptionDef &optdef = this->def()->options.at(opt_key);
// If the option type expects a value and it was not already provided,
// look for it in the next token.
if (optdef.type != coBool && optdef.type != coBools && value.empty()) {
if (i == (argc-1)) {
printf("No value supplied for --%s\n", token.c_str());
continue;
}
value = argv[++ i];
}
// Store the option value.
const bool existing = this->has(opt_key);
if (ConfigOptionBool* opt = this->opt<ConfigOptionBool>(opt_key, true)) {
opt->value = !no;
} else if (ConfigOptionBools* opt = this->opt<ConfigOptionBools>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->values.push_back(!no);
} else if (ConfigOptionStrings* opt = this->opt<ConfigOptionStrings>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->deserialize(value, true);
} else if (ConfigOptionFloats* opt = this->opt<ConfigOptionFloats>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->deserialize(value, true);
} else if (ConfigOptionPoints* opt = this->opt<ConfigOptionPoints>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->deserialize(value, true);
} else {
this->set_deserialize(opt_key, value, true);
}
}
return true;
}
t_config_option_keys DynamicConfig::keys() const
{
t_config_option_keys keys;

View file

@ -976,7 +976,7 @@ public:
// Map from a config option name to its definition.
// The definition does not carry an actual value of the config option, only its constant default value.
// t_config_option_key is std::string
typedef std::map<t_config_option_key,ConfigOptionDef> t_optiondef_map;
typedef std::map<t_config_option_key, ConfigOptionDef> t_optiondef_map;
// Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling.
// The configuration definition is static: It does not carry the actual configuration values,
@ -984,18 +984,27 @@ typedef std::map<t_config_option_key,ConfigOptionDef> t_optiondef_map;
class ConfigDef
{
public:
t_optiondef_map options;
~ConfigDef() { for (auto &opt : this->options) delete opt.second.default_value; }
ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type) {
ConfigOptionDef* opt = &this->options[opt_key];
opt->type = type;
return opt;
}
t_optiondef_map options;
bool has(const t_config_option_key &opt_key) const { return this->options.count(opt_key) > 0; }
const ConfigOptionDef* get(const t_config_option_key &opt_key) const {
t_optiondef_map::iterator it = const_cast<ConfigDef*>(this)->options.find(opt_key);
return (it == this->options.end()) ? nullptr : &it->second;
}
std::vector<std::string> keys() const {
std::vector<std::string> out;
out.reserve(options.size());
for(auto const& kvp : options)
out.push_back(kvp.first);
return out;
}
protected:
ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type) {
ConfigOptionDef* opt = &this->options[opt_key];
opt->type = type;
return opt;
}
};
// An abstract configuration store.
@ -1219,6 +1228,10 @@ public:
bool opt_bool(const t_config_option_key &opt_key) const { return this->option<ConfigOptionBool>(opt_key)->value != 0; }
bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { return this->option<ConfigOptionBools>(opt_key)->get_at(idx) != 0; }
// Command line processing
void read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra);
bool read_cli(int argc, char** argv, t_config_option_keys* extra);
private:
typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
t_options_map options;

View file

@ -113,6 +113,12 @@ Model Model::read_from_archive(const std::string &input_file, PresetBundle* bund
return model;
}
void Model::repair()
{
for (ModelObject *o : this->objects)
o->repair();
}
ModelObject* Model::add_object()
{
this->objects.emplace_back(new ModelObject(this));
@ -886,6 +892,12 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
return;
}
void ModelObject::repair()
{
for (ModelVolume *v : this->volumes)
v->mesh.repair();
}
// Called by Print::validate() from the UI thread.
void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume)
{

View file

@ -120,6 +120,7 @@ public:
void translate(const Vec3d &vector) { this->translate(vector(0), vector(1), vector(2)); }
void translate(coordf_t x, coordf_t y, coordf_t z);
void scale(const Vec3d &versor);
void scale(const double s) { this->scale(Vec3d(s, s, s)); }
void rotate(float angle, const Axis &axis);
void rotate(float angle, const Vec3d& axis);
void mirror(const Axis &axis);
@ -128,6 +129,7 @@ public:
bool needed_repair() const;
void cut(coordf_t z, Model* model) const;
void split(ModelObjectPtrs* new_objects);
void repair();
// Called by Print::validate() from the UI thread.
void check_instances_print_volume_state(const BoundingBoxf3& print_volume);
@ -190,11 +192,11 @@ public:
// Split this volume, append the result to the object owning this volume.
// Return the number of volumes created from this one.
// This is useful to assign different materials to different volumes of an object.
size_t split(unsigned int max_extruders);
size_t split(unsigned int max_extruders);
ModelMaterial* assign_unique_material();
ModelMaterial* assign_unique_material();
void calculate_convex_hull();
void calculate_convex_hull();
const TriangleMesh& get_convex_hull() const;
// Helpers for loading / storing into AMF / 3MF files.
@ -325,6 +327,10 @@ public:
static Model read_from_file(const std::string &input_file, bool add_default_instances = true);
static Model read_from_archive(const std::string &input_file, PresetBundle* bundle, bool add_default_instances = true);
/// Repair the ModelObjects of the current Model.
/// This function calls repair function on each TriangleMesh of each model object volume
void repair();
ModelObject* add_object();
ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh);
ModelObject* add_object(const char *name, const char *path, TriangleMesh &&mesh);

View file

@ -2345,7 +2345,7 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
}
}
PrintConfigDef print_config_def;
const PrintConfigDef print_config_def;
DynamicPrintConfig* DynamicPrintConfig::new_from_defaults()
{
@ -2601,4 +2601,135 @@ StaticPrintConfig::StaticCache<class Slic3r::SLAMaterialConfig> SLAMaterialConf
StaticPrintConfig::StaticCache<class Slic3r::SLAPrinterConfig> SLAPrinterConfig::s_cache_SLAPrinterConfig;
StaticPrintConfig::StaticCache<class Slic3r::SLAFullPrintConfig> SLAFullPrintConfig::s_cache_SLAFullPrintConfig;
CLIConfigDef::CLIConfigDef()
{
ConfigOptionDef *def;
def = this->add("cut", coFloat);
def->label = L("Cut");
def->tooltip = L("Cut model at the given Z.");
def->cli = "cut";
def->default_value = new ConfigOptionFloat(0);
def = this->add("export_3mf", coBool);
def->label = L("Export 3MF");
def->tooltip = L("Slice the model and export slices as 3MF.");
def->cli = "export-3mf";
def->default_value = new ConfigOptionBool(false);
def = this->add("slice", coBool);
def->label = L("Slice");
def->tooltip = L("Slice the model and export gcode.");
def->cli = "slice";
def->default_value = new ConfigOptionBool(false);
def = this->add("help", coBool);
def->label = L("Help");
def->tooltip = L("Show this help.");
def->cli = "help";
def->default_value = new ConfigOptionBool(false);
def = this->add("gui", coBool);
def->label = L("Use GUI");
def->tooltip = L("Start the Slic3r GUI.");
def->cli = "gui";
def->default_value = new ConfigOptionBool(false);
def = this->add("info", coBool);
def->label = L("Output Model Info");
def->tooltip = L("Write information about the model to the console.");
def->cli = "info";
def->default_value = new ConfigOptionBool(false);
def = this->add("load", coStrings);
def->label = L("Load config file");
def->tooltip = L("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 = L("Output File");
def->tooltip = L("The file where the output will be written (if not specified, it will be based on the input file).");
def->cli = "output";
def->default_value = new ConfigOptionString("");
def = this->add("rotate", coFloat);
def->label = L("Rotate");
def->tooltip = L("Rotation angle around the Z axis in degrees (0-360, default: 0).");
def->cli = "rotate";
def->default_value = new ConfigOptionFloat(0);
def = this->add("rotate_x", coFloat);
def->label = L("Rotate around X");
def->tooltip = L("Rotation angle around the X axis in degrees (0-360, default: 0).");
def->cli = "rotate-x";
def->default_value = new ConfigOptionFloat(0);
def = this->add("rotate_y", coFloat);
def->label = L("Rotate around Y");
def->tooltip = L("Rotation angle around the Y axis in degrees (0-360, default: 0).");
def->cli = "rotate-y";
def->default_value = new ConfigOptionFloat(0);
def = this->add("save", coString);
def->label = L("Save config file");
def->tooltip = L("Save configuration to the specified file.");
def->cli = "save";
def->default_value = new ConfigOptionString();
def = this->add("scale", coFloat);
def->label = L("Scale");
def->tooltip = L("Scaling factor (default: 1).");
def->cli = "scale";
def->default_value = new ConfigOptionFloat(1);
/*
def = this->add("scale_to_fit", coPoint3);
def->label = L("Scale to Fit");
def->tooltip = L("Scale to fit the given volume.");
def->cli = "scale-to-fit";
def->default_value = new ConfigOptionPoint3(Pointf3(0,0,0));
*/
def = this->add("center", coPoint);
def->label = L("Center");
def->tooltip = L("Center the print around the given center (default: 100, 100).");
def->cli = "center";
def->default_value = new ConfigOptionPoint(Vec2d(100,100));
}
const CLIConfigDef cli_config_def;
DynamicPrintAndCLIConfig::PrintAndCLIConfigDef DynamicPrintAndCLIConfig::s_def;
std::ostream& print_cli_options(std::ostream& out)
{
for (const auto& opt : cli_config_def.options) {
if (opt.second.cli.size() != 0) {
out << "\t" << std::left << std::setw(40) << std::string("--") + opt.second.cli;
out << "\t" << opt.second.tooltip << "\n";
if (opt.second.default_value != nullptr)
out << "\t" << std::setw(40) << " " << "\t" << " (default: " << opt.second.default_value->serialize() << ")";
out << "\n";
}
}
std::cerr << std::endl;
return out;
}
std::ostream& print_print_options(std::ostream& out)
{
for (const auto& opt : print_config_def.options) {
if (opt.second.cli.size() != 0) {
out << "\t" << std::left << std::setw(40) << std::string("--") + opt.second.cli;
out << "\t" << opt.second.tooltip << "\n";
if (opt.second.default_value != nullptr)
out << "\t" << std::setw(40) << " " << "\t" << " (default: " << opt.second.default_value->serialize() << ")";
out << "\n";
}
}
std::cerr << std::endl;
return out;
}
}

View file

@ -165,7 +165,7 @@ private:
// The one and only global definition of SLic3r configuration options.
// This definition is constant.
extern PrintConfigDef print_config_def;
extern const PrintConfigDef print_config_def;
// Slic3r dynamic configuration, used to override the configuration
// per object, per modification volume or per printing material.
@ -968,6 +968,88 @@ protected:
#undef STATIC_PRINT_CONFIG_CACHE_DERIVED
#undef OPT_PTR
}
class CLIConfigDef : public ConfigDef
{
public:
CLIConfigDef();
};
extern const CLIConfigDef cli_config_def;
#define OPT_PTR(KEY) if (opt_key == #KEY) return &this->KEY
class CLIConfig : public virtual ConfigBase, public StaticConfig
{
public:
ConfigOptionFloat cut;
ConfigOptionBool export_3mf;
ConfigOptionBool gui;
ConfigOptionBool info;
ConfigOptionBool help;
ConfigOptionStrings load;
ConfigOptionString output;
ConfigOptionFloat rotate;
ConfigOptionFloat rotate_x;
ConfigOptionFloat rotate_y;
ConfigOptionString save;
ConfigOptionFloat scale;
// ConfigOptionPoint3 scale_to_fit;
ConfigOptionPoint center;
ConfigOptionBool slice;
CLIConfig() : ConfigBase(), StaticConfig()
{
this->set_defaults();
};
// Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here.
const ConfigDef* def() const override { return &cli_config_def; }
ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override
{
OPT_PTR(cut);
OPT_PTR(export_3mf);
OPT_PTR(gui);
OPT_PTR(help);
OPT_PTR(info);
OPT_PTR(load);
OPT_PTR(output);
OPT_PTR(rotate);
OPT_PTR(rotate_x);
OPT_PTR(rotate_y);
OPT_PTR(save);
OPT_PTR(scale);
// OPT_PTR(scale_to_fit);
OPT_PTR(slice);
return NULL;
}
};
#undef OPT_PTR
class DynamicPrintAndCLIConfig : public DynamicPrintConfig
{
public:
DynamicPrintAndCLIConfig() { this->apply(FullPrintConfig::defaults()); this->apply(CLIConfig()); }
DynamicPrintAndCLIConfig(const DynamicPrintAndCLIConfig &other) : DynamicPrintConfig(other) {}
// Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here.
const ConfigDef* def() const override { return &s_def; }
private:
class PrintAndCLIConfigDef : public PrintConfigDef
{
public:
PrintAndCLIConfigDef() { this->options.insert(cli_config_def.options.begin(), cli_config_def.options.end()); }
};
static PrintAndCLIConfigDef s_def;
};
/// Iterate through all of the print options and write them to a stream.
std::ostream& print_print_options(std::ostream& out);
/// Iterate through all of the CLI options and write them to a stream.
std::ostream& print_cli_options(std::ostream& out);
} // namespace Slic3r
#endif

View file

@ -1591,7 +1591,7 @@ std::vector<ExPolygons> PrintObject::slice_support_enforcers() const
std::vector<float> zs;
zs.reserve(this->layers().size());
for (const Layer *l : this->layers())
zs.emplace_back(l->slice_z);
zs.emplace_back((float)l->slice_z);
return this->_slice_volumes(zs, volumes);
}
@ -1604,7 +1604,7 @@ std::vector<ExPolygons> PrintObject::slice_support_blockers() const
std::vector<float> zs;
zs.reserve(this->layers().size());
for (const Layer *l : this->layers())
zs.emplace_back(l->slice_z);
zs.emplace_back((float)l->slice_z);
return this->_slice_volumes(zs, volumes);
}