Reworked the command line interface based on the current state
of the upstream. Thanks @alexrj, @lordofhyphens for the original code of slic3r.cpp
This commit is contained in:
parent
75cf1cde92
commit
18025cc669
@ -143,7 +143,7 @@ stl_generate_shared_vertices(stl_file *stl) {
|
||||
}
|
||||
|
||||
void
|
||||
stl_write_off(stl_file *stl, char *file) {
|
||||
stl_write_off(stl_file *stl, const char *file) {
|
||||
int i;
|
||||
FILE *fp;
|
||||
char *error_msg;
|
||||
@ -179,7 +179,7 @@ stl_write_off(stl_file *stl, char *file) {
|
||||
}
|
||||
|
||||
void
|
||||
stl_write_vrml(stl_file *stl, char *file) {
|
||||
stl_write_vrml(stl_file *stl, const char *file) {
|
||||
int i;
|
||||
FILE *fp;
|
||||
char *error_msg;
|
||||
@ -236,7 +236,7 @@ stl_write_vrml(stl_file *stl, char *file) {
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void stl_write_obj (stl_file *stl, char *file) {
|
||||
void stl_write_obj (stl_file *stl, const char *file) {
|
||||
int i;
|
||||
FILE* fp;
|
||||
|
||||
|
@ -177,10 +177,10 @@ extern void stl_transform(stl_file *stl, const Eigen::Transform<double, 3, Eigen
|
||||
extern void stl_open_merge(stl_file *stl, char *file);
|
||||
extern void stl_invalidate_shared_vertices(stl_file *stl);
|
||||
extern void stl_generate_shared_vertices(stl_file *stl);
|
||||
extern void stl_write_obj(stl_file *stl, char *file);
|
||||
extern void stl_write_off(stl_file *stl, char *file);
|
||||
extern void stl_write_dxf(stl_file *stl, char *file, char *label);
|
||||
extern void stl_write_vrml(stl_file *stl, char *file);
|
||||
extern void stl_write_obj(stl_file *stl, const char *file);
|
||||
extern void stl_write_off(stl_file *stl, const char *file);
|
||||
extern void stl_write_dxf(stl_file *stl, const char *file, char *label);
|
||||
extern void stl_write_vrml(stl_file *stl, const char *file);
|
||||
inline void stl_calculate_normal(stl_normal &normal, stl_facet *facet) {
|
||||
normal = (facet->vertex[1] - facet->vertex[0]).cross(facet->vertex[2] - facet->vertex[0]);
|
||||
}
|
||||
|
@ -365,7 +365,7 @@ stl_write_quad_object(stl_file *stl, char *file) {
|
||||
}
|
||||
|
||||
void
|
||||
stl_write_dxf(stl_file *stl, char *file, char *label) {
|
||||
stl_write_dxf(stl_file *stl, const char *file, char *label) {
|
||||
int i;
|
||||
FILE *fp;
|
||||
char *error_msg;
|
||||
|
@ -190,6 +190,110 @@ bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &o
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> ConfigOptionDef::cli_args() const
|
||||
{
|
||||
std::string cli = this->cli.substr(0, this->cli.find("="));
|
||||
boost::trim_right_if(cli, boost::is_any_of("!"));
|
||||
std::vector<std::string> args;
|
||||
boost::split(args, cli, boost::is_any_of("|"));
|
||||
return args;
|
||||
}
|
||||
|
||||
std::ostream& ConfigDef::print_cli_help(std::ostream& out, bool show_defaults) const
|
||||
{
|
||||
// prepare a function for wrapping text
|
||||
auto wrap = [](std::string text, size_t line_length) -> std::string {
|
||||
std::istringstream words(text);
|
||||
std::ostringstream wrapped;
|
||||
std::string word;
|
||||
|
||||
if (words >> word) {
|
||||
wrapped << word;
|
||||
size_t space_left = line_length - word.length();
|
||||
while (words >> word) {
|
||||
if (space_left < word.length() + 1) {
|
||||
wrapped << '\n' << word;
|
||||
space_left = line_length - word.length();
|
||||
} else {
|
||||
wrapped << ' ' << word;
|
||||
space_left -= word.length() + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return wrapped.str();
|
||||
};
|
||||
|
||||
// get the unique categories
|
||||
std::set<std::string> categories;
|
||||
for (const auto& opt : this->options) {
|
||||
const ConfigOptionDef& def = opt.second;
|
||||
categories.insert(def.category);
|
||||
}
|
||||
|
||||
for (auto category : categories) {
|
||||
if (category != "") {
|
||||
out << category << ":" << std::endl;
|
||||
} else if (categories.size() > 1) {
|
||||
out << "Misc options:" << std::endl;
|
||||
}
|
||||
|
||||
for (const auto& opt : this->options) {
|
||||
const ConfigOptionDef& def = opt.second;
|
||||
if (def.category != category) continue;
|
||||
|
||||
if (!def.cli.empty()) {
|
||||
// get all possible variations: --foo, --foobar, -f...
|
||||
auto cli_args = def.cli_args();
|
||||
for (auto& arg : cli_args) {
|
||||
arg.insert(0, (arg.size() == 1) ? "-" : "--");
|
||||
if (def.type == coFloat || def.type == coInt || def.type == coFloatOrPercent
|
||||
|| def.type == coFloats || def.type == coInts) {
|
||||
arg += " N";
|
||||
} else if (def.type == coPoint) {
|
||||
arg += " X,Y";
|
||||
} else if (def.type == coPoint3) {
|
||||
arg += " X,Y,Z";
|
||||
} else if (def.type == coString || def.type == coStrings) {
|
||||
arg += " ABCD";
|
||||
}
|
||||
}
|
||||
|
||||
// left: command line options
|
||||
const std::string cli = boost::algorithm::join(cli_args, ", ");
|
||||
out << " " << std::left << std::setw(20) << cli;
|
||||
|
||||
// right: option description
|
||||
std::string descr = def.tooltip;
|
||||
if (show_defaults && def.default_value != nullptr && def.type != coBool
|
||||
&& (def.type != coString || !def.default_value->serialize().empty())) {
|
||||
descr += " (";
|
||||
if (!def.sidetext.empty()) {
|
||||
descr += def.sidetext + ", ";
|
||||
} else if (!def.enum_values.empty()) {
|
||||
descr += boost::algorithm::join(def.enum_values, ", ") + "; ";
|
||||
}
|
||||
descr += "default: " + def.default_value->serialize() + ")";
|
||||
}
|
||||
|
||||
// wrap lines of description
|
||||
descr = wrap(descr, 80);
|
||||
std::vector<std::string> lines;
|
||||
boost::split(lines, descr, boost::is_any_of("\n"));
|
||||
|
||||
// if command line options are too long, print description in new line
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
if (i == 0 && cli.size() > 19)
|
||||
out << std::endl;
|
||||
if (i > 0 || cli.size() > 19)
|
||||
out << std::string(21, ' ');
|
||||
out << lines[i] << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void ConfigBase::apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent)
|
||||
{
|
||||
// loop through options and apply them
|
||||
@ -508,50 +612,53 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre
|
||||
// Let the parent decide what to do if the opt_key is not defined by this->def().
|
||||
return nullptr;
|
||||
ConfigOption *opt = nullptr;
|
||||
switch (optdef->type) {
|
||||
case coFloat: opt = new ConfigOptionFloat(); break;
|
||||
case coFloats: opt = new ConfigOptionFloats(); break;
|
||||
case coInt: opt = new ConfigOptionInt(); break;
|
||||
case coInts: opt = new ConfigOptionInts(); break;
|
||||
case coString: opt = new ConfigOptionString(); break;
|
||||
case coStrings: opt = new ConfigOptionStrings(); break;
|
||||
case coPercent: opt = new ConfigOptionPercent(); break;
|
||||
case coPercents: opt = new ConfigOptionPercents(); break;
|
||||
case coFloatOrPercent: opt = new ConfigOptionFloatOrPercent(); break;
|
||||
case coPoint: opt = new ConfigOptionPoint(); break;
|
||||
case coPoints: opt = new ConfigOptionPoints(); break;
|
||||
case coBool: opt = new ConfigOptionBool(); break;
|
||||
case coBools: opt = new ConfigOptionBools(); break;
|
||||
case coEnum: opt = new ConfigOptionEnumGeneric(optdef->enum_keys_map); break;
|
||||
default: throw std::runtime_error(std::string("Unknown option type for option ") + opt_key);
|
||||
if (optdef->default_value != nullptr) {
|
||||
opt = (optdef->default_value->type() == coEnum) ?
|
||||
// Special case: For a DynamicConfig, convert a templated enum to a generic enum.
|
||||
new ConfigOptionEnumGeneric(optdef->enum_keys_map, optdef->default_value->getInt()) :
|
||||
optdef->default_value->clone();
|
||||
} else {
|
||||
switch (optdef->type) {
|
||||
case coFloat: opt = new ConfigOptionFloat(); break;
|
||||
case coFloats: opt = new ConfigOptionFloats(); break;
|
||||
case coInt: opt = new ConfigOptionInt(); break;
|
||||
case coInts: opt = new ConfigOptionInts(); break;
|
||||
case coString: opt = new ConfigOptionString(); break;
|
||||
case coStrings: opt = new ConfigOptionStrings(); break;
|
||||
case coPercent: opt = new ConfigOptionPercent(); break;
|
||||
case coPercents: opt = new ConfigOptionPercents(); break;
|
||||
case coFloatOrPercent: opt = new ConfigOptionFloatOrPercent(); break;
|
||||
case coPoint: opt = new ConfigOptionPoint(); break;
|
||||
case coPoints: opt = new ConfigOptionPoints(); break;
|
||||
case coPoint3: opt = new ConfigOptionPoint3(); break;
|
||||
// case coPoint3s: opt = new ConfigOptionPoint3s(); break;
|
||||
case coBool: opt = new ConfigOptionBool(); break;
|
||||
case coBools: opt = new ConfigOptionBools(); break;
|
||||
case coEnum: opt = new ConfigOptionEnumGeneric(optdef->enum_keys_map); break;
|
||||
default: throw std::runtime_error(std::string("Unknown option type for option ") + opt_key);
|
||||
}
|
||||
}
|
||||
this->options[opt_key] = opt;
|
||||
return opt;
|
||||
}
|
||||
|
||||
void DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra)
|
||||
void DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra, t_config_option_keys* keys)
|
||||
{
|
||||
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(int(args.size()), &args[0], extra);
|
||||
this->read_cli(int(args.size()), &args[0], extra, keys);
|
||||
}
|
||||
|
||||
bool DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra)
|
||||
bool DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra, t_config_option_keys* keys)
|
||||
{
|
||||
// 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)
|
||||
for (const auto &oit : this->def()->options)
|
||||
for (auto t : oit.second.cli_args())
|
||||
opts[t] = oit.first;
|
||||
}
|
||||
|
||||
bool parse_options = true;
|
||||
for (int i = 1; i < argc; ++ i) {
|
||||
@ -611,6 +718,10 @@ bool DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra)
|
||||
}
|
||||
// Store the option value.
|
||||
const bool existing = this->has(opt_key);
|
||||
if (keys != nullptr && !existing) {
|
||||
// Save the order of detected keys.
|
||||
keys->push_back(opt_key);
|
||||
}
|
||||
ConfigOption *opt_base = this->option(opt_key, true);
|
||||
ConfigOptionVectorBase *opt_vector = opt_base->is_vector() ? static_cast<ConfigOptionVectorBase*>(opt_base) : nullptr;
|
||||
if (opt_vector) {
|
||||
|
@ -27,6 +27,24 @@ extern std::string escape_strings_cstyle(const std::vector<std::string> &strs);
|
||||
extern bool unescape_string_cstyle(const std::string &str, std::string &out);
|
||||
extern bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &out);
|
||||
|
||||
/// Specialization of std::exception to indicate that an unknown config option has been encountered.
|
||||
class UnknownOptionException : public std::runtime_error {
|
||||
public:
|
||||
UnknownOptionException() :
|
||||
std::runtime_error("Unknown option exception") {}
|
||||
UnknownOptionException(const std::string &opt_key) :
|
||||
std::runtime_error(std::string("Unknown option exception: ") + opt_key) {}
|
||||
};
|
||||
|
||||
/// Indicate that the ConfigBase derived class does not provide config definition (the method def() returns null).
|
||||
class NoDefinitionException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
NoDefinitionException() :
|
||||
std::runtime_error("No definition exception") {}
|
||||
NoDefinitionException(const std::string &opt_key) :
|
||||
std::runtime_error(std::string("No definition exception: ") + opt_key) {}
|
||||
};
|
||||
|
||||
// Type of a configuration value.
|
||||
enum ConfigOptionType {
|
||||
@ -54,12 +72,14 @@ enum ConfigOptionType {
|
||||
coPoint = 6,
|
||||
// vector of 2d points (Point2f). Currently used for the definition of the print bed and for the extruder offsets.
|
||||
coPoints = coPoint + coVectorType,
|
||||
coPoint3 = 7,
|
||||
// coPoint3s = coPoint3 + coVectorType,
|
||||
// single boolean value
|
||||
coBool = 7,
|
||||
coBool = 8,
|
||||
// vector of boolean values
|
||||
coBools = coBool + coVectorType,
|
||||
// a generic enum
|
||||
coEnum = 8,
|
||||
coEnum = 9,
|
||||
};
|
||||
|
||||
enum ConfigOptionMode {
|
||||
@ -718,6 +738,39 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ConfigOptionPoint3 : public ConfigOptionSingle<Vec3d>
|
||||
{
|
||||
public:
|
||||
ConfigOptionPoint3() : ConfigOptionSingle<Vec3d>(Vec3d(0,0,0)) {}
|
||||
explicit ConfigOptionPoint3(const Vec3d &value) : ConfigOptionSingle<Vec3d>(value) {}
|
||||
|
||||
static ConfigOptionType static_type() { return coPoint3; }
|
||||
ConfigOptionType type() const override { return static_type(); }
|
||||
ConfigOption* clone() const override { return new ConfigOptionPoint3(*this); }
|
||||
ConfigOptionPoint3& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
|
||||
bool operator==(const ConfigOptionPoint3 &rhs) const { return this->value == rhs.value; }
|
||||
|
||||
std::string serialize() const override
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << this->value(0);
|
||||
ss << ",";
|
||||
ss << this->value(1);
|
||||
ss << ",";
|
||||
ss << this->value(2);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool deserialize(const std::string &str, bool append = false) override
|
||||
{
|
||||
UNUSED(append);
|
||||
char dummy;
|
||||
return sscanf(str.data(), " %lf , %lf , %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 2 ||
|
||||
sscanf(str.data(), " %lf x %lf x %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 2;
|
||||
}
|
||||
};
|
||||
|
||||
class ConfigOptionBool : public ConfigOptionSingle<bool>
|
||||
{
|
||||
public:
|
||||
@ -893,6 +946,7 @@ class ConfigOptionEnumGeneric : public ConfigOptionInt
|
||||
{
|
||||
public:
|
||||
ConfigOptionEnumGeneric(const t_config_enum_values* keys_map = nullptr) : keys_map(keys_map) {}
|
||||
explicit ConfigOptionEnumGeneric(const t_config_enum_values* keys_map, int value) : ConfigOptionInt(value), keys_map(keys_map) {}
|
||||
|
||||
const t_config_enum_values* keys_map;
|
||||
|
||||
@ -1010,6 +1064,9 @@ public:
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Returns the alternative CLI arguments for the given option.
|
||||
std::vector<std::string> cli_args() const;
|
||||
};
|
||||
|
||||
// Map from a config option name to its definition.
|
||||
@ -1044,6 +1101,9 @@ public:
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Iterate through all of the CLI options and write them to a stream.
|
||||
std::ostream& print_cli_help(std::ostream& out, bool show_defaults) const;
|
||||
|
||||
protected:
|
||||
ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type) {
|
||||
ConfigOptionDef* opt = &this->options[opt_key];
|
||||
@ -1089,12 +1149,24 @@ public:
|
||||
TYPE* option(const t_config_option_key &opt_key, bool create = false)
|
||||
{
|
||||
ConfigOption *opt = this->optptr(opt_key, create);
|
||||
// assert(opt == nullptr || opt->type() == TYPE::static_type());
|
||||
return (opt == nullptr || opt->type() != TYPE::static_type()) ? nullptr : static_cast<TYPE*>(opt);
|
||||
}
|
||||
template<typename TYPE>
|
||||
const TYPE* option(const t_config_option_key &opt_key) const
|
||||
{ return const_cast<ConfigBase*>(this)->option<TYPE>(opt_key, false); }
|
||||
template<typename TYPE>
|
||||
TYPE* option_throw(const t_config_option_key &opt_key, bool create = false)
|
||||
{
|
||||
ConfigOption *opt = this->optptr(opt_key, create);
|
||||
if (opt == nullptr)
|
||||
throw UnknownOptionException(opt_key);
|
||||
if (opt->type() != TYPE::static_type())
|
||||
throw std::runtime_error("Conversion to a wrong type");
|
||||
return static_cast<TYPE*>(opt);
|
||||
}
|
||||
template<typename TYPE>
|
||||
const TYPE* option_throw(const t_config_option_key &opt_key) const
|
||||
{ return const_cast<ConfigBase*>(this)->option_throw<TYPE>(opt_key, false); }
|
||||
// Apply all keys of other ConfigBase defined by this->def() to this ConfigBase.
|
||||
// An UnknownOptionException is thrown in case some option keys of other are not defined by this->def(),
|
||||
// or this ConfigBase is of a StaticConfig type and it does not support some of the keys, and ignore_nonexistent is not set.
|
||||
@ -1276,8 +1348,8 @@ public:
|
||||
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);
|
||||
void read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra, t_config_option_keys* keys = nullptr);
|
||||
bool read_cli(int argc, char** argv, t_config_option_keys* extra, t_config_option_keys* keys = nullptr);
|
||||
|
||||
typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
|
||||
t_options_map::const_iterator cbegin() const { return options.cbegin(); }
|
||||
@ -1303,25 +1375,6 @@ protected:
|
||||
void set_defaults();
|
||||
};
|
||||
|
||||
/// Specialization of std::exception to indicate that an unknown config option has been encountered.
|
||||
class UnknownOptionException : public std::runtime_error {
|
||||
public:
|
||||
UnknownOptionException() :
|
||||
std::runtime_error("Unknown option exception") {}
|
||||
UnknownOptionException(const std::string &opt_key) :
|
||||
std::runtime_error(std::string("Unknown option exception: ") + opt_key) {}
|
||||
};
|
||||
|
||||
/// Indicate that the ConfigBase derived class does not provide config definition (the method def() returns null).
|
||||
class NoDefinitionException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
NoDefinitionException() :
|
||||
std::runtime_error("No definition exception") {}
|
||||
NoDefinitionException(const std::string &opt_key) :
|
||||
std::runtime_error(std::string("No definition exception: ") + opt_key) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -115,4 +115,23 @@ bool load_obj(const char *path, Model *model, const char *object_name_in)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool store_obj(const char *path, TriangleMesh *mesh)
|
||||
{
|
||||
//FIXME returning false even if write failed.
|
||||
mesh->WriteOBJFile(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool store_obj(const char *path, ModelObject *model_object)
|
||||
{
|
||||
TriangleMesh mesh = model_object->mesh();
|
||||
return store_obj(path, &mesh);
|
||||
}
|
||||
|
||||
bool store_obj(const char *path, Model *model)
|
||||
{
|
||||
TriangleMesh mesh = model->mesh();
|
||||
return store_obj(path, &mesh);
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
@ -9,6 +9,10 @@ class Model;
|
||||
// Load an OBJ file into a provided model.
|
||||
extern bool load_obj(const char *path, Model *model, const char *object_name = nullptr);
|
||||
|
||||
extern bool store_obj(const char *path, TriangleMesh *mesh);
|
||||
extern bool store_obj(const char *path, ModelObject *model);
|
||||
extern bool store_obj(const char *path, Model *model);
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Format_OBJ_hpp_ */
|
||||
|
@ -55,4 +55,10 @@ bool store_stl(const char *path, ModelObject *model_object, bool binary)
|
||||
return store_stl(path, &mesh, binary);
|
||||
}
|
||||
|
||||
bool store_stl(const char *path, Model *model, bool binary)
|
||||
{
|
||||
TriangleMesh mesh = model->mesh();
|
||||
return store_stl(path, &mesh, binary);
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
@ -11,6 +11,7 @@ extern bool load_stl(const char *path, Model *model, const char *object_name = n
|
||||
|
||||
extern bool store_stl(const char *path, TriangleMesh *mesh, bool binary);
|
||||
extern bool store_stl(const char *path, ModelObject *model_object, bool binary);
|
||||
extern bool store_stl(const char *path, Model *model, bool binary);
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
|
@ -577,6 +577,11 @@ end:
|
||||
return input_file;
|
||||
}
|
||||
|
||||
std::string Model::propose_export_file_name_and_path(const std::string &new_extension) const
|
||||
{
|
||||
return boost::filesystem::path(this->propose_export_file_name_and_path()).replace_extension(new_extension).string();
|
||||
}
|
||||
|
||||
ModelObject::~ModelObject()
|
||||
{
|
||||
this->clear_volumes();
|
||||
@ -1568,6 +1573,22 @@ void ModelVolume::scale(const Vec3d& scaling_factors)
|
||||
set_scaling_factor(get_scaling_factor().cwiseProduct(scaling_factors));
|
||||
}
|
||||
|
||||
void ModelObject::scale_to_fit(const Vec3d &size)
|
||||
{
|
||||
/*
|
||||
BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const;
|
||||
Vec3d orig_size = this->bounding_box().size();
|
||||
float factor = fminf(
|
||||
size.x / orig_size.x,
|
||||
fminf(
|
||||
size.y / orig_size.y,
|
||||
size.z / orig_size.z
|
||||
)
|
||||
);
|
||||
this->scale(factor);
|
||||
*/
|
||||
}
|
||||
|
||||
void ModelVolume::rotate(double angle, Axis axis)
|
||||
{
|
||||
switch (axis)
|
||||
|
@ -245,6 +245,10 @@ public:
|
||||
void scale(const Vec3d &versor);
|
||||
void scale(const double s) { this->scale(Vec3d(s, s, s)); }
|
||||
void scale(double x, double y, double z) { this->scale(Vec3d(x, y, z)); }
|
||||
/// Scale the current ModelObject to fit by altering the scaling factor of ModelInstances.
|
||||
/// It operates on the total size by duplicating the object according to all the instances.
|
||||
/// \param size Sizef3 the size vector
|
||||
void scale_to_fit(const Vec3d &size);
|
||||
void rotate(double angle, Axis axis);
|
||||
void rotate(double angle, const Vec3d& axis);
|
||||
void mirror(Axis axis);
|
||||
@ -619,6 +623,8 @@ public:
|
||||
|
||||
// Propose an output file name & path based on the first printable object's name and source input file's path.
|
||||
std::string propose_export_file_name_and_path() const;
|
||||
// Propose an output path, replace extension. The new_extension shall contain the initial dot.
|
||||
std::string propose_export_file_name_and_path(const std::string &new_extension) const;
|
||||
|
||||
private:
|
||||
MODELBASE_DERIVED_PRIVATE_COPY_MOVE(Model)
|
||||
|
@ -1509,7 +1509,7 @@ void Print::process()
|
||||
// The export_gcode may die for various reasons (fails to process output_filename_format,
|
||||
// write error into the G-code, cannot execute post-processing scripts).
|
||||
// It is up to the caller to show an error message.
|
||||
void Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data)
|
||||
std::string Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data)
|
||||
{
|
||||
// output everything to a G-code file
|
||||
// The following call may die if the output_filename_format template substitution fails.
|
||||
@ -1525,6 +1525,7 @@ void Print::export_gcode(const std::string &path_template, GCodePreviewData *pre
|
||||
// The following line may die for multiple reasons.
|
||||
GCode gcode;
|
||||
gcode.do_export(this, path.c_str(), preview_data);
|
||||
return path.c_str();
|
||||
}
|
||||
|
||||
void Print::_make_skirt()
|
||||
@ -1693,8 +1694,10 @@ void Print::_make_brim()
|
||||
}
|
||||
polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing())));
|
||||
}
|
||||
|
||||
|
||||
loops = union_pt_chained(loops, false);
|
||||
// The function above produces ordering well suited for concentric infill (from outside to inside).
|
||||
// For Brim, the ordering should be reversed (from inside to outside).
|
||||
std::reverse(loops.begin(), loops.end());
|
||||
extrusion_entities_append_loops(m_brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height()));
|
||||
}
|
||||
|
@ -297,7 +297,9 @@ public:
|
||||
bool apply_config(DynamicPrintConfig config);
|
||||
|
||||
void process() override;
|
||||
void export_gcode(const std::string &path_template, GCodePreviewData *preview_data);
|
||||
// Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file.
|
||||
// If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r).
|
||||
std::string export_gcode(const std::string &path_template, GCodePreviewData *preview_data);
|
||||
|
||||
// methods for handling state
|
||||
bool is_step_done(PrintStep step) const { return Inherited::is_step_done(step); }
|
||||
|
@ -2955,7 +2955,7 @@ std::string FullPrintConfig::validate()
|
||||
return "--use-firmware-retraction is only supported by Marlin, Smoothie, Repetier and Machinekit firmware";
|
||||
|
||||
if (this->use_firmware_retraction.value)
|
||||
for (bool wipe : this->wipe.values)
|
||||
for (unsigned char wipe : this->wipe.values)
|
||||
if (wipe)
|
||||
return "--use-firmware-retraction is not compatible with --wipe";
|
||||
|
||||
@ -2999,7 +2999,7 @@ std::string FullPrintConfig::validate()
|
||||
return "Invalid value for --extruder-clearance-height";
|
||||
|
||||
// --extrusion-multiplier
|
||||
for (float em : this->extrusion_multiplier.values)
|
||||
for (double em : this->extrusion_multiplier.values)
|
||||
if (em <= 0)
|
||||
return "Invalid value for --extrusion-multiplier";
|
||||
|
||||
@ -3100,55 +3100,65 @@ StaticPrintConfig::StaticCache<class Slic3r::SLAPrintObjectConfig> SLAPrintObje
|
||||
StaticPrintConfig::StaticCache<class Slic3r::SLAPrinterConfig> SLAPrinterConfig::s_cache_SLAPrinterConfig;
|
||||
StaticPrintConfig::StaticCache<class Slic3r::SLAFullPrintConfig> SLAFullPrintConfig::s_cache_SLAFullPrintConfig;
|
||||
|
||||
CLIConfigDef::CLIConfigDef()
|
||||
CLIActionsConfigDef::CLIActionsConfigDef()
|
||||
{
|
||||
ConfigOptionDef *def;
|
||||
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("dont_arrange", coBool);
|
||||
def->label = L("Dont arrange");
|
||||
def->tooltip = L("Don't arrange the objects on the build plate. The model coordinates "
|
||||
"define the absolute positions on the build plate. "
|
||||
"The option --center will be ignored.");
|
||||
def->cli = "dont-arrange";
|
||||
// Actions:
|
||||
def = this->add("export_obj", coBool);
|
||||
def->label = L("Export SVG");
|
||||
def->tooltip = L("Export the model(s) as OBJ.");
|
||||
def->cli = "export-obj";
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
/*
|
||||
def = this->add("export_svg", coBool);
|
||||
def->label = L("Export SVG");
|
||||
def->tooltip = L("Slice the model and export solid slices as SVG.");
|
||||
def->cli = "export-svg";
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
*/
|
||||
|
||||
def = this->add("export_sla", coBool);
|
||||
def->label = L("Export SLA");
|
||||
def->tooltip = L("Slice the model and export SLA printing layers as PNG.");
|
||||
def->cli = "export-sla|sla";
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("datadir", coString);
|
||||
def->label = L("User data directory");
|
||||
def->tooltip = L("Load and store settings at the given directory. "
|
||||
"This is useful for maintaining different profiles or including "
|
||||
"configurations from a network storage.");
|
||||
def->cli = "datadir";
|
||||
def->default_value = new ConfigOptionString();
|
||||
|
||||
def = this->add("export_3mf", coBool);
|
||||
def->label = L("Export 3MF");
|
||||
def->tooltip = L("Slice the model and export slices as 3MF.");
|
||||
def->tooltip = L("Export the model(s) 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 = this->add("export_amf", coBool);
|
||||
def->label = L("Export AMF");
|
||||
def->tooltip = L("Export the model(s) as AMF.");
|
||||
def->cli = "export-amf";
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("export_stl", coBool);
|
||||
def->label = L("Export STL");
|
||||
def->tooltip = L("Export the model(s) as STL.");
|
||||
def->cli = "export-stl";
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("export_gcode", coBool);
|
||||
def->label = L("Export G-code");
|
||||
def->tooltip = L("Slice the model and export toolpaths as G-code.");
|
||||
def->cli = "export-gcode|gcode|g";
|
||||
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->cli = "help|h";
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("gui", coBool);
|
||||
def->label = L("Use GUI");
|
||||
def->tooltip = L("Forces the GUI launch instead of command line slicing "
|
||||
"(if you supply a model file, it will be loaded into the plater)");
|
||||
def->cli = "gui";
|
||||
def = this->add("help_options", coBool);
|
||||
def->label = L("Help (options)");
|
||||
def->tooltip = L("Show the full list of print/G-code configuration options.");
|
||||
def->cli = "help-options";
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("info", coBool);
|
||||
@ -3157,107 +3167,169 @@ CLIConfigDef::CLIConfigDef()
|
||||
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("no_gui", coBool);
|
||||
def->label = L("Do not use GUI");
|
||||
def->tooltip = L("Forces the command line slicing instead of gui. This takes precedence over --gui if both are present.");
|
||||
def->cli = "no-gui";
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
CLITransformConfigDef::CLITransformConfigDef()
|
||||
{
|
||||
ConfigOptionDef* def;
|
||||
|
||||
// Transform options:
|
||||
def = this->add("align_xy", coPoint);
|
||||
def->label = L("Align XY");
|
||||
def->tooltip = L("Align the model to the given point.");
|
||||
def->cli = "align-xy";
|
||||
def->default_value = new ConfigOptionPoint(Vec2d(100,100));
|
||||
|
||||
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("cut_grid", coFloat);
|
||||
def->label = L("Cut");
|
||||
def->tooltip = L("Cut model in the XY plane into tiles of the specified max size.");
|
||||
def->cli = "cut-grid";
|
||||
def->default_value = new ConfigOptionPoint();
|
||||
|
||||
def = this->add("cut_x", coFloat);
|
||||
def->label = L("Cut");
|
||||
def->tooltip = L("Cut model at the given X.");
|
||||
def->cli = "cut-x";
|
||||
def->default_value = new ConfigOptionFloat(0);
|
||||
|
||||
def = this->add("cut_y", coFloat);
|
||||
def->label = L("Cut");
|
||||
def->tooltip = L("Cut model at the given Y.");
|
||||
def->cli = "cut-y";
|
||||
def->default_value = new ConfigOptionFloat(0);
|
||||
*/
|
||||
|
||||
def = this->add("center", coPoint);
|
||||
def->label = L("Center");
|
||||
def->tooltip = L("Center the print around the given center.");
|
||||
def->cli = "center";
|
||||
def->default_value = new ConfigOptionPoint(Vec2d(100,100));
|
||||
|
||||
def = this->add("dont_arrange", coBool);
|
||||
def->label = L("Don't arrange");
|
||||
def->tooltip = L("Do not rearrange the given models before merging and keep their original XY coordinates.");
|
||||
def->cli = "dont-arrange";
|
||||
|
||||
def = this->add("duplicate", coInt);
|
||||
def->label = L("Duplicate");
|
||||
def->tooltip =L("Multiply copies by this factor.");
|
||||
def->cli = "duplicate=i";
|
||||
def->min = 1;
|
||||
|
||||
def = this->add("duplicate_grid", coPoint);
|
||||
def->label = L("Duplicate by grid");
|
||||
def->tooltip = L("Multiply copies by creating a grid.");
|
||||
def->cli = "duplicate-grid";
|
||||
|
||||
def = this->add("merge", coBool);
|
||||
def->label = L("Merge");
|
||||
def->tooltip = L("Arrange the supplied models in a plate and merge them in a single model in order to perform actions once.");
|
||||
def->cli = "merge|m";
|
||||
|
||||
def = this->add("repair", coBool);
|
||||
def->label = L("Repair");
|
||||
def->tooltip = L("Try to repair any non-manifold meshes (this option is implicitly added whenever we need to slice the model to perform the requested action).");
|
||||
def->cli = "repair";
|
||||
|
||||
def = this->add("rotate", coFloat);
|
||||
def->label = L("Rotate");
|
||||
def->tooltip = L("Rotation angle around the Z axis in degrees.");
|
||||
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.");
|
||||
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.");
|
||||
def->cli = "rotate-y";
|
||||
def->default_value = new ConfigOptionFloat(0);
|
||||
|
||||
def = this->add("scale", coFloatOrPercent);
|
||||
def->label = L("Scale");
|
||||
def->tooltip = L("Scaling factor or percentage.");
|
||||
def->cli = "scale";
|
||||
def->default_value = new ConfigOptionFloatOrPercent(1, false);
|
||||
|
||||
def = this->add("split", coBool);
|
||||
def->label = L("Split");
|
||||
def->tooltip = L("Detect unconnected parts in the given model(s) and split them into separate objects.");
|
||||
def->cli = "split";
|
||||
|
||||
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("print_center", coPoint);
|
||||
def->label = L("Print center");
|
||||
def->tooltip = L("Center the print around the given center (default: 100, 100).");
|
||||
def->cli = "print-center";
|
||||
def->default_value = new ConfigOptionPoint(Vec2d(100,100));
|
||||
def->default_value = new ConfigOptionPoint3(Vec3d(0,0,0));
|
||||
}
|
||||
|
||||
const CLIConfigDef cli_config_def;
|
||||
CLIMiscConfigDef::CLIMiscConfigDef()
|
||||
{
|
||||
ConfigOptionDef* def;
|
||||
|
||||
def = this->add("ignore_nonexistent_config", coBool);
|
||||
def->label = L("Ignore non-existent config files");
|
||||
def->tooltip = L("Do not fail if a file supplied to --load does not exist.");
|
||||
def->cli = "ignore-nonexistent-config";
|
||||
|
||||
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 = 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|o";
|
||||
|
||||
/*
|
||||
def = this->add("autosave", coString);
|
||||
def->label = L("Autosave");
|
||||
def->tooltip = L("Automatically export current configuration to the specified file.");
|
||||
def->cli = "autosave";
|
||||
*/
|
||||
|
||||
def = this->add("datadir", coString);
|
||||
def->label = L("Data directory");
|
||||
def->tooltip = L("Load and store settings at the given directory. This is useful for maintaining different profiles or including configurations from a network storage.");
|
||||
def->cli = "datadir";
|
||||
|
||||
def = this->add("loglevel", coInt);
|
||||
def->label = L("Logging level");
|
||||
def->tooltip = L("Messages with severity lower or eqal to the loglevel will be printed out. 0:trace, 1:debug, 2:info, 3:warning, 4:error, 5:fatal");
|
||||
def->cli = "loglevel";
|
||||
def->min = 0;
|
||||
}
|
||||
|
||||
const CLIActionsConfigDef cli_actions_config_def;
|
||||
const CLITransformConfigDef cli_transform_config_def;
|
||||
const CLIMiscConfigDef cli_misc_config_def;
|
||||
|
||||
DynamicPrintAndCLIConfig::PrintAndCLIConfigDef DynamicPrintAndCLIConfig::s_def;
|
||||
|
||||
void DynamicPrintAndCLIConfig::handle_legacy(t_config_option_key &opt_key, std::string &value) const
|
||||
{
|
||||
if (cli_config_def.options.find(opt_key) == cli_config_def.options.end()) {
|
||||
PrintConfigDef::handle_legacy(opt_key, value);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if (cli_actions_config_def .options.find(opt_key) == cli_actions_config_def .options.end() &&
|
||||
cli_transform_config_def.options.find(opt_key) == cli_transform_config_def.options.end() &&
|
||||
cli_misc_config_def .options.find(opt_key) == cli_misc_config_def .options.end()) {
|
||||
PrintConfigDef::handle_legacy(opt_key, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ enum PrinterTechnology
|
||||
ptFFF,
|
||||
// Stereolitography
|
||||
ptSLA,
|
||||
// Unknown, useful for command line processing
|
||||
ptUnknown,
|
||||
};
|
||||
|
||||
enum GCodeFlavor {
|
||||
@ -1145,72 +1147,32 @@ protected:
|
||||
#undef STATIC_PRINT_CONFIG_CACHE_DERIVED
|
||||
#undef OPT_PTR
|
||||
|
||||
class CLIConfigDef : public ConfigDef
|
||||
class CLIActionsConfigDef : public ConfigDef
|
||||
{
|
||||
public:
|
||||
CLIConfigDef();
|
||||
CLIActionsConfigDef();
|
||||
};
|
||||
|
||||
extern const CLIConfigDef cli_config_def;
|
||||
|
||||
#define OPT_PTR(KEY) if (opt_key == #KEY) return &this->KEY
|
||||
|
||||
class CLIConfig : public virtual ConfigBase, public StaticConfig
|
||||
class CLITransformConfigDef : public ConfigDef
|
||||
{
|
||||
public:
|
||||
ConfigOptionFloat cut;
|
||||
ConfigOptionString datadir;
|
||||
ConfigOptionBool dont_arrange;
|
||||
ConfigOptionBool export_3mf;
|
||||
ConfigOptionBool gui;
|
||||
ConfigOptionBool info;
|
||||
ConfigOptionBool help;
|
||||
ConfigOptionStrings load;
|
||||
ConfigOptionBool no_gui;
|
||||
ConfigOptionString output;
|
||||
ConfigOptionPoint print_center;
|
||||
ConfigOptionFloat rotate;
|
||||
ConfigOptionFloat rotate_x;
|
||||
ConfigOptionFloat rotate_y;
|
||||
ConfigOptionString save;
|
||||
ConfigOptionFloat scale;
|
||||
// ConfigOptionPoint3 scale_to_fit;
|
||||
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; }
|
||||
t_config_option_keys keys() const override { return cli_config_def.keys(); }
|
||||
|
||||
ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override
|
||||
{
|
||||
OPT_PTR(cut);
|
||||
OPT_PTR(datadir);
|
||||
OPT_PTR(dont_arrange);
|
||||
OPT_PTR(export_3mf);
|
||||
OPT_PTR(gui);
|
||||
OPT_PTR(help);
|
||||
OPT_PTR(info);
|
||||
OPT_PTR(load);
|
||||
OPT_PTR(no_gui);
|
||||
OPT_PTR(output);
|
||||
OPT_PTR(print_center);
|
||||
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;
|
||||
}
|
||||
CLITransformConfigDef();
|
||||
};
|
||||
|
||||
#undef OPT_PTR
|
||||
class CLIMiscConfigDef : public ConfigDef
|
||||
{
|
||||
public:
|
||||
CLIMiscConfigDef();
|
||||
};
|
||||
|
||||
// This class defines the command line options representing actions.
|
||||
extern const CLIActionsConfigDef cli_actions_config_def;
|
||||
|
||||
// This class defines the command line options representing transforms.
|
||||
extern const CLITransformConfigDef cli_transform_config_def;
|
||||
|
||||
// This class defines all command line options that are not actions or transforms.
|
||||
extern const CLIMiscConfigDef cli_misc_config_def;
|
||||
|
||||
class DynamicPrintAndCLIConfig : public DynamicPrintConfig
|
||||
{
|
||||
@ -1233,19 +1195,16 @@ private:
|
||||
public:
|
||||
PrintAndCLIConfigDef() {
|
||||
this->options.insert(print_config_def.options.begin(), print_config_def.options.end());
|
||||
this->options.insert(cli_config_def.options.begin(), cli_config_def.options.end());
|
||||
this->options.insert(cli_actions_config_def.options.begin(), cli_actions_config_def.options.end());
|
||||
this->options.insert(cli_transform_config_def.options.begin(), cli_transform_config_def.options.end());
|
||||
this->options.insert(cli_misc_config_def.options.begin(), cli_misc_config_def.options.end());
|
||||
}
|
||||
// Do not release the default values, they are handled by print_config_def & cli_config_def.
|
||||
// Do not release the default values, they are handled by print_config_def & cli_actions_config_def / cli_transform_config_def / cli_misc_config_def.
|
||||
~PrintAndCLIConfigDef() { this->options.clear(); }
|
||||
};
|
||||
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
|
||||
|
@ -300,81 +300,82 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
|
||||
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
|
||||
assert(it_status != model_object_status.end());
|
||||
assert(it_status->status != ModelObjectStatus::Deleted);
|
||||
if (it_status->status == ModelObjectStatus::New)
|
||||
// PrintObject instances will be added in the next loop.
|
||||
continue;
|
||||
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
|
||||
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
|
||||
const ModelObject &model_object_new = *model.objects[idx_model_object];
|
||||
auto it_print_object_status = print_object_status.lower_bound(PrintObjectStatus(model_object.id()));
|
||||
if (it_print_object_status != print_object_status.end() && it_print_object_status->id != model_object.id())
|
||||
it_print_object_status = print_object_status.end();
|
||||
// Check whether a model part volume was added or removed, their transformations or order changed.
|
||||
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
|
||||
bool sla_trafo_differs = model_object.instances.empty() != model_object_new.instances.empty() ||
|
||||
(! model_object.instances.empty() && ! sla_trafo(model_object).isApprox(sla_trafo(model_object_new)));
|
||||
if (model_parts_differ || sla_trafo_differs) {
|
||||
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
|
||||
if (it_print_object_status != print_object_status.end()) {
|
||||
update_apply_status(it_print_object_status->print_object->invalidate_all_steps());
|
||||
const_cast<PrintObjectStatus&>(*it_print_object_status).status = PrintObjectStatus::Deleted;
|
||||
}
|
||||
// Copy content of the ModelObject including its ID, do not change the parent.
|
||||
model_object.assign_copy(model_object_new);
|
||||
} else {
|
||||
// Synchronize Object's config.
|
||||
bool object_config_changed = model_object.config != model_object_new.config;
|
||||
if (object_config_changed)
|
||||
model_object.config = model_object_new.config;
|
||||
if (! object_diff.empty() || object_config_changed) {
|
||||
SLAPrintObjectConfig new_config = m_default_object_config;
|
||||
normalize_and_apply_config(new_config, model_object.config);
|
||||
if (it_print_object_status != print_object_status.end()) {
|
||||
t_config_option_keys diff = it_print_object_status->print_object->config().diff(new_config);
|
||||
if (! diff.empty()) {
|
||||
update_apply_status(it_print_object_status->print_object->invalidate_state_by_config_options(diff));
|
||||
it_print_object_status->print_object->config_apply_only(new_config, diff, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*if (model_object.sla_support_points != model_object_new.sla_support_points) {
|
||||
model_object.sla_support_points = model_object_new.sla_support_points;
|
||||
if (it_print_object_status != print_object_status.end())
|
||||
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
|
||||
}
|
||||
if (model_object.sla_points_status != model_object_new.sla_points_status) {
|
||||
// Change of this status should invalidate support points. The points themselves are not enough, there are none
|
||||
// in case that nothing was generated OR that points were autogenerated already and not copied to the front-end.
|
||||
// These cases can only be differentiated by checking the status change. However, changing from 'Generating' should NOT
|
||||
// invalidate - that would keep stopping the background processing without a reason.
|
||||
if (model_object.sla_points_status != sla::PointsStatus::Generating)
|
||||
if (it_print_object_status != print_object_status.end())
|
||||
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
|
||||
model_object.sla_points_status = model_object_new.sla_points_status;
|
||||
}*/
|
||||
// PrintObject for this ModelObject, if it exists.
|
||||
auto it_print_object_status = print_object_status.end();
|
||||
if (it_status->status != ModelObjectStatus::New) {
|
||||
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
|
||||
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
|
||||
const ModelObject &model_object_new = *model.objects[idx_model_object];
|
||||
auto it_print_object_status = print_object_status.lower_bound(PrintObjectStatus(model_object.id()));
|
||||
if (it_print_object_status != print_object_status.end() && it_print_object_status->id != model_object.id())
|
||||
it_print_object_status = print_object_status.end();
|
||||
// Check whether a model part volume was added or removed, their transformations or order changed.
|
||||
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
|
||||
bool sla_trafo_differs = model_object.instances.empty() != model_object_new.instances.empty() ||
|
||||
(! model_object.instances.empty() && ! sla_trafo(model_object).isApprox(sla_trafo(model_object_new)));
|
||||
if (model_parts_differ || sla_trafo_differs) {
|
||||
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
|
||||
if (it_print_object_status != print_object_status.end()) {
|
||||
update_apply_status(it_print_object_status->print_object->invalidate_all_steps());
|
||||
const_cast<PrintObjectStatus&>(*it_print_object_status).status = PrintObjectStatus::Deleted;
|
||||
}
|
||||
// Copy content of the ModelObject including its ID, do not change the parent.
|
||||
model_object.assign_copy(model_object_new);
|
||||
} else {
|
||||
// Synchronize Object's config.
|
||||
bool object_config_changed = model_object.config != model_object_new.config;
|
||||
if (object_config_changed)
|
||||
model_object.config = model_object_new.config;
|
||||
if (! object_diff.empty() || object_config_changed) {
|
||||
SLAPrintObjectConfig new_config = m_default_object_config;
|
||||
normalize_and_apply_config(new_config, model_object.config);
|
||||
if (it_print_object_status != print_object_status.end()) {
|
||||
t_config_option_keys diff = it_print_object_status->print_object->config().diff(new_config);
|
||||
if (! diff.empty()) {
|
||||
update_apply_status(it_print_object_status->print_object->invalidate_state_by_config_options(diff));
|
||||
it_print_object_status->print_object->config_apply_only(new_config, diff, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*if (model_object.sla_support_points != model_object_new.sla_support_points) {
|
||||
model_object.sla_support_points = model_object_new.sla_support_points;
|
||||
if (it_print_object_status != print_object_status.end())
|
||||
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
|
||||
}
|
||||
if (model_object.sla_points_status != model_object_new.sla_points_status) {
|
||||
// Change of this status should invalidate support points. The points themselves are not enough, there are none
|
||||
// in case that nothing was generated OR that points were autogenerated already and not copied to the front-end.
|
||||
// These cases can only be differentiated by checking the status change. However, changing from 'Generating' should NOT
|
||||
// invalidate - that would keep stopping the background processing without a reason.
|
||||
if (model_object.sla_points_status != sla::PointsStatus::Generating)
|
||||
if (it_print_object_status != print_object_status.end())
|
||||
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
|
||||
model_object.sla_points_status = model_object_new.sla_points_status;
|
||||
}*/
|
||||
|
||||
bool old_user_modified = model_object.sla_points_status == sla::PointsStatus::UserModified;
|
||||
bool new_user_modified = model_object_new.sla_points_status == sla::PointsStatus::UserModified;
|
||||
if ((old_user_modified && ! new_user_modified) || // switching to automatic supports from manual supports
|
||||
(! old_user_modified && new_user_modified) || // switching to manual supports from automatic supports
|
||||
(new_user_modified && model_object.sla_support_points != model_object_new.sla_support_points)) {
|
||||
if (it_print_object_status != print_object_status.end())
|
||||
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
|
||||
bool old_user_modified = model_object.sla_points_status == sla::PointsStatus::UserModified;
|
||||
bool new_user_modified = model_object_new.sla_points_status == sla::PointsStatus::UserModified;
|
||||
if ((old_user_modified && ! new_user_modified) || // switching to automatic supports from manual supports
|
||||
(! old_user_modified && new_user_modified) || // switching to manual supports from automatic supports
|
||||
(new_user_modified && model_object.sla_support_points != model_object_new.sla_support_points)) {
|
||||
if (it_print_object_status != print_object_status.end())
|
||||
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
|
||||
|
||||
model_object.sla_points_status = model_object_new.sla_points_status;
|
||||
model_object.sla_support_points = model_object_new.sla_support_points;
|
||||
}
|
||||
model_object.sla_points_status = model_object_new.sla_points_status;
|
||||
model_object.sla_support_points = model_object_new.sla_support_points;
|
||||
}
|
||||
|
||||
// Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step.
|
||||
model_object.name = model_object_new.name;
|
||||
model_object.input_file = model_object_new.input_file;
|
||||
model_object.clear_instances();
|
||||
model_object.instances.reserve(model_object_new.instances.size());
|
||||
for (const ModelInstance *model_instance : model_object_new.instances) {
|
||||
model_object.instances.emplace_back(new ModelInstance(*model_instance));
|
||||
model_object.instances.back()->set_model_object(&model_object);
|
||||
}
|
||||
}
|
||||
// Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step.
|
||||
model_object.name = model_object_new.name;
|
||||
model_object.input_file = model_object_new.input_file;
|
||||
model_object.clear_instances();
|
||||
model_object.instances.reserve(model_object_new.instances.size());
|
||||
for (const ModelInstance *model_instance : model_object_new.instances) {
|
||||
model_object.instances.emplace_back(new ModelInstance(*model_instance));
|
||||
model_object.instances.back()->set_model_object(&model_object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<SLAPrintObject::Instance> new_instances = sla_instances(model_object);
|
||||
if (it_print_object_status != print_object_status.end() && it_print_object_status->status != PrintObjectStatus::Deleted) {
|
||||
|
@ -247,7 +247,7 @@ bool TriangleMesh::needed_repair() const
|
||||
|| this->stl.stats.backwards_edges > 0;
|
||||
}
|
||||
|
||||
void TriangleMesh::WriteOBJFile(char* output_file)
|
||||
void TriangleMesh::WriteOBJFile(const char* output_file)
|
||||
{
|
||||
stl_generate_shared_vertices(&stl);
|
||||
stl_write_obj(&stl, output_file);
|
||||
@ -1499,9 +1499,16 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
|
||||
// Try to close gaps.
|
||||
// Do it in two rounds, first try to connect in the same direction only,
|
||||
// then try to connect the open polylines in reversed order as well.
|
||||
#if 0
|
||||
for (double max_gap : { EPSILON, 0.001, 0.1, 1., 2. }) {
|
||||
chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, false);
|
||||
chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, true);
|
||||
}
|
||||
#else
|
||||
const double max_gap = 2.; //mm
|
||||
chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, false);
|
||||
chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, true);
|
||||
#endif
|
||||
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
{
|
||||
|
@ -36,7 +36,7 @@ public:
|
||||
float volume();
|
||||
void check_topology();
|
||||
bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == (int)this->stl.stats.number_of_facets; }
|
||||
void WriteOBJFile(char* output_file);
|
||||
void WriteOBJFile(const char* output_file);
|
||||
void scale(float factor);
|
||||
void scale(const Vec3d &versor);
|
||||
void translate(float x, float y, float z);
|
||||
|
746
src/slic3r.cpp
746
src/slic3r.cpp
@ -31,22 +31,452 @@
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/SLAPrint.hpp"
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
#include "libslic3r/Format/AMF.hpp"
|
||||
#include "libslic3r/Format/3mf.hpp"
|
||||
#include "libslic3r/Format/STL.hpp"
|
||||
#include "libslic3r/Format/OBJ.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
|
||||
#include "slic3r.hpp"
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
/// utility function for displaying CLI usage
|
||||
void printUsage();
|
||||
PrinterTechnology get_printer_technology(const DynamicConfig &config)
|
||||
{
|
||||
const ConfigOptionEnum<PrinterTechnology> *opt = config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology");
|
||||
return (opt == nullptr) ? ptUnknown : opt->value;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
int slic3r_main_(int argc, char **argv)
|
||||
int CLI::run(int argc, char **argv)
|
||||
{
|
||||
if (! this->setup(argc, argv))
|
||||
return 1;
|
||||
|
||||
m_extra_config.apply(m_config, true);
|
||||
m_extra_config.normalize();
|
||||
|
||||
bool start_gui = m_actions.empty() &&
|
||||
// cutting transformations are setting an "export" action.
|
||||
std::find(m_transforms.begin(), m_transforms.end(), "cut") == m_transforms.end() &&
|
||||
std::find(m_transforms.begin(), m_transforms.end(), "cut_x") == m_transforms.end() &&
|
||||
std::find(m_transforms.begin(), m_transforms.end(), "cut_y") == m_transforms.end();
|
||||
PrinterTechnology printer_technology = get_printer_technology(m_extra_config);
|
||||
const std::vector<std::string> &load_configs = m_config.option<ConfigOptionStrings>("load", true)->values;
|
||||
|
||||
// load config files supplied via --load
|
||||
for (auto const &file : load_configs) {
|
||||
if (! boost::filesystem::exists(file)) {
|
||||
if (m_config.opt_bool("ignore_nonexistent_file")) {
|
||||
continue;
|
||||
} else {
|
||||
boost::nowide::cerr << "No such file: " << file << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
DynamicPrintConfig config;
|
||||
try {
|
||||
config.load(file);
|
||||
} catch (std::exception &ex) {
|
||||
boost::nowide::cerr << "Error while reading config file: " << ex.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
config.normalize();
|
||||
PrinterTechnology other_printer_technology = get_printer_technology(config);
|
||||
if (printer_technology == ptUnknown) {
|
||||
printer_technology = other_printer_technology;
|
||||
} else if (printer_technology != other_printer_technology) {
|
||||
boost::nowide::cerr << "Mixing configurations for FFF and SLA technologies" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
m_print_config.apply(config);
|
||||
}
|
||||
|
||||
// Read input file(s) if any.
|
||||
for (const std::string &file : m_input_files) {
|
||||
if (! boost::filesystem::exists(file)) {
|
||||
boost::nowide::cerr << "No such file: " << file << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
Model model;
|
||||
try {
|
||||
// When loading an AMF or 3MF, config is imported as well, including the printer technology.
|
||||
model = Model::read_from_file(file, &m_print_config, true);
|
||||
PrinterTechnology other_printer_technology = get_printer_technology(m_print_config);
|
||||
if (printer_technology == ptUnknown) {
|
||||
printer_technology = other_printer_technology;
|
||||
} else if (printer_technology != other_printer_technology) {
|
||||
boost::nowide::cerr << "Mixing configurations for FFF and SLA technologies" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
} catch (std::exception &e) {
|
||||
boost::nowide::cerr << file << ": " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
if (model.objects.empty()) {
|
||||
boost::nowide::cerr << "Error: file is empty: " << file << std::endl;
|
||||
continue;
|
||||
}
|
||||
m_models.push_back(model);
|
||||
}
|
||||
|
||||
// Apply command line options to a more specific DynamicPrintConfig which provides normalize()
|
||||
// (command line options override --load files)
|
||||
m_print_config.apply(m_extra_config, true);
|
||||
// Normalizing after importing the 3MFs / AMFs
|
||||
m_print_config.normalize();
|
||||
|
||||
if (printer_technology == ptUnknown)
|
||||
printer_technology = std::find(m_actions.begin(), m_actions.end(), "export_sla") == m_actions.end() ? ptFFF : ptSLA;
|
||||
|
||||
// Initialize full print configs for both the FFF and SLA technologies.
|
||||
FullPrintConfig fff_print_config;
|
||||
SLAFullPrintConfig sla_print_config;
|
||||
fff_print_config.apply(m_print_config);
|
||||
sla_print_config.apply(m_print_config);
|
||||
|
||||
// Loop through transform options.
|
||||
for (auto const &opt_key : m_transforms) {
|
||||
if (opt_key == "merge") {
|
||||
Model m;
|
||||
for (auto &model : m_models)
|
||||
for (ModelObject *o : model.objects)
|
||||
m.add_object(*o);
|
||||
// Rearrange instances unless --dont-arrange is supplied
|
||||
if (! m_config.opt_bool("dont_arrange")) {
|
||||
m.add_default_instances();
|
||||
const BoundingBoxf &bb = fff_print_config.bed_shape.values;
|
||||
m.arrange_objects(
|
||||
fff_print_config.min_object_distance(),
|
||||
// If we are going to use the merged model for printing, honor
|
||||
// the configured print bed for arranging, otherwise do it freely.
|
||||
this->has_print_action() ? &bb : nullptr
|
||||
);
|
||||
}
|
||||
m_models.clear();
|
||||
m_models.emplace_back(std::move(m));
|
||||
} else if (opt_key == "duplicate") {
|
||||
const BoundingBoxf &bb = fff_print_config.bed_shape.values;
|
||||
for (auto &model : m_models) {
|
||||
const bool all_objects_have_instances = std::none_of(
|
||||
model.objects.begin(), model.objects.end(),
|
||||
[](ModelObject* o){ return o->instances.empty(); }
|
||||
);
|
||||
if (all_objects_have_instances) {
|
||||
// if all input objects have defined position(s) apply duplication to the whole model
|
||||
model.duplicate(m_config.opt_int("duplicate"), fff_print_config.min_object_distance(), &bb);
|
||||
} else {
|
||||
model.add_default_instances();
|
||||
model.duplicate_objects(m_config.opt_int("duplicate"), fff_print_config.min_object_distance(), &bb);
|
||||
}
|
||||
}
|
||||
} else if (opt_key == "duplicate_grid") {
|
||||
std::vector<int> &ints = m_config.option<ConfigOptionInts>("duplicate_grid")->values;
|
||||
const int x = ints.size() > 0 ? ints.at(0) : 1;
|
||||
const int y = ints.size() > 1 ? ints.at(1) : 1;
|
||||
const double distance = fff_print_config.duplicate_distance.value;
|
||||
for (auto &model : m_models)
|
||||
model.duplicate_objects_grid(x, y, (distance > 0) ? distance : 6); // TODO: this is not the right place for setting a default
|
||||
} else if (opt_key == "center") {
|
||||
for (auto &model : m_models) {
|
||||
model.add_default_instances();
|
||||
// this affects instances:
|
||||
model.center_instances_around_point(m_config.option<ConfigOptionPoint>("center")->value);
|
||||
// this affects volumes:
|
||||
//FIXME Vojtech: Who knows why the complete model should be aligned with Z as a single rigid body?
|
||||
//model.align_to_ground();
|
||||
BoundingBoxf3 bbox;
|
||||
for (ModelObject *model_object : model.objects)
|
||||
// We are interested into the Z span only, therefore it is sufficient to measure the bounding box of the 1st instance only.
|
||||
bbox.merge(model_object->instance_bounding_box(0, false));
|
||||
for (ModelObject *model_object : model.objects)
|
||||
for (ModelInstance *model_instance : model_object->instances)
|
||||
model_instance->set_offset(Z, model_instance->get_offset(Z) - bbox.min.z());
|
||||
}
|
||||
} else if (opt_key == "align_xy") {
|
||||
const Vec2d &p = m_config.option<ConfigOptionPoint>("align_xy")->value;
|
||||
for (auto &model : m_models) {
|
||||
BoundingBoxf3 bb = model.bounding_box();
|
||||
// this affects volumes:
|
||||
model.translate(-(bb.min.x() - p.x()), -(bb.min.y() - p.y()), -bb.min.z());
|
||||
}
|
||||
} else if (opt_key == "dont_arrange") {
|
||||
// do nothing - this option alters other transform options
|
||||
} else if (opt_key == "rotate") {
|
||||
for (auto &model : m_models)
|
||||
for (auto &o : model.objects)
|
||||
// this affects volumes:
|
||||
o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), Z);
|
||||
} else if (opt_key == "rotate_x") {
|
||||
for (auto &model : m_models)
|
||||
for (auto &o : model.objects)
|
||||
// this affects volumes:
|
||||
o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), X);
|
||||
} else if (opt_key == "rotate_y") {
|
||||
for (auto &model : m_models)
|
||||
for (auto &o : model.objects)
|
||||
// this affects volumes:
|
||||
o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), Y);
|
||||
} else if (opt_key == "scale") {
|
||||
for (auto &model : m_models)
|
||||
for (auto &o : model.objects)
|
||||
// this affects volumes:
|
||||
o->scale(m_config.get_abs_value(opt_key, 1));
|
||||
} else if (opt_key == "scale_to_fit") {
|
||||
const Vec3d &opt = m_config.opt<ConfigOptionPoint3>(opt_key)->value;
|
||||
if (opt.x() <= 0 || opt.y() <= 0 || opt.z() <= 0) {
|
||||
boost::nowide::cerr << "--scale-to-fit requires a positive volume" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
for (auto &model : m_models)
|
||||
for (auto &o : model.objects)
|
||||
// this affects volumes:
|
||||
o->scale_to_fit(opt);
|
||||
} else if (opt_key == "cut" || opt_key == "cut_x" || opt_key == "cut_y") {
|
||||
std::vector<Model> new_models;
|
||||
for (auto &model : m_models) {
|
||||
model.repair();
|
||||
model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0
|
||||
size_t num_objects = model.objects.size();
|
||||
for (size_t i = 0; i < num_objects; ++ i) {
|
||||
|
||||
#if 0
|
||||
if (opt_key == "cut_x") {
|
||||
o->cut(X, m_config.opt_float("cut_x"), &out);
|
||||
} else if (opt_key == "cut_y") {
|
||||
o->cut(Y, m_config.opt_float("cut_y"), &out);
|
||||
} else if (opt_key == "cut") {
|
||||
o->cut(Z, m_config.opt_float("cut"), &out);
|
||||
}
|
||||
#else
|
||||
int main(int argc, char **argv)
|
||||
model.objects.front()->cut(0, m_config.opt_float("cut"), true, true, true);
|
||||
#endif
|
||||
model.delete_object(size_t(0));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: copy less stuff around using pointers
|
||||
m_models = new_models;
|
||||
|
||||
if (m_actions.empty())
|
||||
m_actions.push_back("export_stl");
|
||||
}
|
||||
#if 0
|
||||
else if (opt_key == "cut_grid") {
|
||||
std::vector<Model> new_models;
|
||||
for (auto &model : m_models) {
|
||||
TriangleMesh mesh = model.mesh();
|
||||
mesh.repair();
|
||||
|
||||
TriangleMeshPtrs meshes = mesh.cut_by_grid(m_config.option<ConfigOptionPoint>("cut_grid")->value);
|
||||
size_t i = 0;
|
||||
for (TriangleMesh* m : meshes) {
|
||||
Model out;
|
||||
auto o = out.add_object();
|
||||
o->add_volume(*m);
|
||||
o->input_file += "_" + std::to_string(i++);
|
||||
delete m;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: copy less stuff around using pointers
|
||||
m_models = new_models;
|
||||
|
||||
if (m_actions.empty())
|
||||
m_actions.push_back("export_stl");
|
||||
}
|
||||
#endif
|
||||
else if (opt_key == "split") {
|
||||
for (Model &model : m_models) {
|
||||
size_t num_objects = model.objects.size();
|
||||
for (size_t i = 0; i < num_objects; ++ i) {
|
||||
model.objects.front()->split(nullptr);
|
||||
model.delete_object(size_t(0));
|
||||
}
|
||||
}
|
||||
} else if (opt_key == "repair") {
|
||||
for (auto &model : m_models)
|
||||
model.repair();
|
||||
} else {
|
||||
boost::nowide::cerr << "error: option not implemented yet: " << opt_key << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// loop through action options
|
||||
for (auto const &opt_key : m_actions) {
|
||||
if (opt_key == "help") {
|
||||
this->print_help();
|
||||
} else if (opt_key == "help_options") {
|
||||
this->print_help(true);
|
||||
} else if (opt_key == "save") {
|
||||
//FIXME check for mixing the FFF / SLA parameters.
|
||||
// or better save fff_print_config vs. sla_print_config
|
||||
m_print_config.save(m_config.opt_string("save"));
|
||||
} else if (opt_key == "info") {
|
||||
// --info works on unrepaired model
|
||||
for (Model &model : m_models) {
|
||||
model.add_default_instances();
|
||||
model.print_info();
|
||||
}
|
||||
} else if (opt_key == "export_stl") {
|
||||
for (auto &model : m_models)
|
||||
model.add_default_instances();
|
||||
if (! this->export_models(IO::STL))
|
||||
return 1;
|
||||
} else if (opt_key == "export_obj") {
|
||||
for (auto &model : m_models)
|
||||
model.add_default_instances();
|
||||
if (! this->export_models(IO::OBJ))
|
||||
return 1;
|
||||
} else if (opt_key == "export_amf") {
|
||||
if (! this->export_models(IO::AMF))
|
||||
return 1;
|
||||
} else if (opt_key == "export_3mf") {
|
||||
if (! this->export_models(IO::TMF))
|
||||
return 1;
|
||||
} else if (opt_key == "export_gcode" || opt_key == "export_sla" || opt_key == "slice") {
|
||||
if (opt_key == "export_gcode" && printer_technology == ptSLA) {
|
||||
boost::nowide::cerr << "error: cannot export G-code for an FFF configuration" << std::endl;
|
||||
return 1;
|
||||
} else if (opt_key == "export_sla" && printer_technology == ptFFF) {
|
||||
boost::nowide::cerr << "error: cannot export SLA slices for a SLA configuration" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
// Make a copy of the model if the current action is not the last action, as the model may be
|
||||
// modified by the centering and such.
|
||||
Model model_copy;
|
||||
bool make_copy = &opt_key != &m_actions.back();
|
||||
for (Model &model_in : m_models) {
|
||||
if (make_copy)
|
||||
model_copy = model_in;
|
||||
Model &model = make_copy ? model_copy : model_in;
|
||||
// If all objects have defined instances, their relative positions will be
|
||||
// honored when printing (they will be only centered, unless --dont-arrange
|
||||
// is supplied); if any object has no instances, it will get a default one
|
||||
// and all instances will be rearranged (unless --dont-arrange is supplied).
|
||||
std::string outfile = m_config.opt_string("output");
|
||||
Print fff_print;
|
||||
SLAPrint sla_print;
|
||||
PrintBase *print = (printer_technology == ptFFF) ? static_cast<PrintBase*>(&fff_print) : static_cast<PrintBase*>(&sla_print);
|
||||
if (! m_config.opt_bool("dont_arrange")) {
|
||||
//FIXME make the min_object_distance configurable.
|
||||
model.arrange_objects(fff_print.config().min_object_distance());
|
||||
model.center_instances_around_point(m_config.option<ConfigOptionPoint>("center")->value);
|
||||
}
|
||||
if (printer_technology == ptFFF) {
|
||||
for (auto* mo : model.objects)
|
||||
fff_print.auto_assign_extruders(mo);
|
||||
}
|
||||
print->apply(model, m_print_config);
|
||||
std::string err = print->validate();
|
||||
if (err.empty()) {
|
||||
try {
|
||||
std::string outfile_final;
|
||||
print->process();
|
||||
if (printer_technology == ptFFF) {
|
||||
// The outfile is processed by a PlaceholderParser.
|
||||
outfile = fff_print.export_gcode(outfile, nullptr);
|
||||
outfile_final = fff_print.print_statistics().finalize_output_path(outfile);
|
||||
} else {
|
||||
outfile = sla_print.output_filepath(outfile);
|
||||
//FIXME Tamas, please port it to miniz
|
||||
// sla_print.export_raster<SLAZipFmt>(outfile);
|
||||
outfile_final = sla_print.print_statistics().finalize_output_path(outfile);
|
||||
}
|
||||
if (outfile != outfile_final && Slic3r::rename_file(outfile, outfile_final) != 0) {
|
||||
boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
} catch (const std::exception &ex) {
|
||||
boost::nowide::cerr << ex.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
boost::nowide::cerr << err << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
print.center = ! m_config.has("center")
|
||||
&& ! m_config.has("align_xy")
|
||||
&& ! m_config.opt_bool("dont_arrange");
|
||||
print.set_model(model);
|
||||
|
||||
// start chronometer
|
||||
typedef std::chrono::high_resolution_clock clock_;
|
||||
typedef std::chrono::duration<double, std::ratio<1> > second_;
|
||||
std::chrono::time_point<clock_> t0{ clock_::now() };
|
||||
|
||||
const std::string outfile = this->output_filepath(model, IO::Gcode);
|
||||
try {
|
||||
print.export_gcode(outfile);
|
||||
} catch (std::runtime_error &e) {
|
||||
boost::nowide::cerr << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
boost::nowide::cout << "G-code exported to " << outfile << std::endl;
|
||||
|
||||
// output some statistics
|
||||
double duration { std::chrono::duration_cast<second_>(clock_::now() - t0).count() };
|
||||
boost::nowide::cout << std::fixed << std::setprecision(0)
|
||||
<< "Done. Process took " << (duration/60) << " minutes and "
|
||||
<< std::setprecision(3)
|
||||
<< std::fmod(duration, 60.0) << " seconds." << std::endl
|
||||
<< std::setprecision(2)
|
||||
<< "Filament required: " << print.total_used_filament() << "mm"
|
||||
<< " (" << print.total_extruded_volume()/1000 << "cm3)" << std::endl;
|
||||
*/
|
||||
}
|
||||
} else {
|
||||
boost::nowide::cerr << "error: option not supported yet: " << opt_key << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (start_gui) {
|
||||
#if 1
|
||||
// #ifdef USE_WX
|
||||
GUI::GUI_App *gui = new GUI::GUI_App();
|
||||
// gui->autosave = m_config.opt_string("autosave");
|
||||
GUI::GUI_App::SetInstance(gui);
|
||||
gui->CallAfter([gui, this, &load_configs] {
|
||||
if (!gui->initialized()) {
|
||||
return;
|
||||
}
|
||||
#if 0
|
||||
// Load the cummulative config over the currently active profiles.
|
||||
//FIXME if multiple configs are loaded, only the last one will have an effect.
|
||||
// We need to decide what to do about loading of separate presets (just print preset, just filament preset etc).
|
||||
// As of now only the full configs are supported here.
|
||||
if (!m_print_config.empty())
|
||||
gui->mainframe->load_config(m_print_config);
|
||||
#endif
|
||||
if (! load_configs.empty())
|
||||
// Load the last config to give it a name at the UI. The name of the preset may be later
|
||||
// changed by loading an AMF or 3MF.
|
||||
//FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config.
|
||||
gui->mainframe->load_config_file(load_configs.back());
|
||||
// If loading a 3MF file, the config is loaded from the last one.
|
||||
if (! m_input_files.empty())
|
||||
gui->plater()->load_files(m_input_files, true, true);
|
||||
if (! m_extra_config.empty())
|
||||
gui->mainframe->load_config(m_extra_config);
|
||||
});
|
||||
return wxEntry(argc, argv);
|
||||
#else
|
||||
// No GUI support. Just print out a help.
|
||||
this->print_help(false);
|
||||
// If started without a parameter, consider it to be OK, otherwise report an error code (no action etc).
|
||||
return (argc == 0) ? 0 : 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CLI::setup(int argc, char **argv)
|
||||
{
|
||||
{
|
||||
const char *loglevel = boost::nowide::getenv("SLIC3R_LOGLEVEL");
|
||||
@ -58,15 +488,6 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
// parse all command line options into a DynamicConfig
|
||||
DynamicPrintAndCLIConfig all_config;
|
||||
t_config_option_keys input_files;
|
||||
// if any option is unsupported, print usage and abort immediately
|
||||
if (! all_config.read_cli(argc, argv, &input_files)) {
|
||||
printUsage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
boost::filesystem::path path_to_binary = boost::filesystem::system_complete(argv[0]);
|
||||
|
||||
// Path from the Slic3r binary to its resources.
|
||||
@ -94,207 +515,111 @@ int main(int argc, char **argv)
|
||||
set_var_dir((path_resources / "icons").string());
|
||||
set_local_dir((path_resources / "localization").string());
|
||||
|
||||
// apply command line options to a more handy CLIConfig
|
||||
CLIConfig cli_config;
|
||||
#ifdef __APPLE__
|
||||
// Enable the GUI mode by default, to support drag & drop.
|
||||
cli_config.gui.value = true;
|
||||
#endif /* __APPLE__ */
|
||||
// Parse all command line options into a DynamicConfig.
|
||||
// If any option is unsupported, print usage and abort immediately.
|
||||
t_config_option_keys opt_order;
|
||||
if (! m_config.read_cli(argc, argv, &m_input_files, &opt_order)) {
|
||||
this->print_help();
|
||||
return false;
|
||||
}
|
||||
// Parse actions and transform options.
|
||||
for (auto const &opt_key : opt_order) {
|
||||
if (cli_actions_config_def.has(opt_key))
|
||||
m_actions.emplace_back(opt_key);
|
||||
if (cli_transform_config_def.has(opt_key))
|
||||
m_transforms.emplace_back(opt_key);
|
||||
}
|
||||
|
||||
cli_config.apply(all_config, true);
|
||||
set_data_dir(cli_config.datadir.value);
|
||||
|
||||
// Load the extra config values.
|
||||
DynamicPrintConfig extra_config;
|
||||
extra_config.apply(all_config, true);
|
||||
|
||||
// load config files supplied via --load
|
||||
DynamicPrintConfig print_config;
|
||||
for (const std::string &file : cli_config.load.values) {
|
||||
if (! boost::filesystem::exists(file)) {
|
||||
boost::nowide::cout << "No such file: " << file << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
DynamicPrintConfig c;
|
||||
try {
|
||||
c.load(file);
|
||||
} catch (std::exception &e) {
|
||||
boost::nowide::cout << "Error while reading config file: " << e.what() << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
c.normalize();
|
||||
print_config.apply(c);
|
||||
{
|
||||
const ConfigOptionInt *opt_loglevel = m_config.opt<ConfigOptionInt>("loglevel");
|
||||
if (opt_loglevel != 0)
|
||||
set_logging_level(opt_loglevel->value);
|
||||
}
|
||||
|
||||
if ((input_files.empty() || cli_config.gui.value) && ! cli_config.no_gui.value && ! cli_config.help.value && cli_config.save.value.empty()) {
|
||||
#if 1
|
||||
GUI::GUI_App *gui = new GUI::GUI_App();
|
||||
GUI::GUI_App::SetInstance(gui);
|
||||
gui->CallAfter([gui, &input_files, &cli_config, &extra_config, &print_config] {
|
||||
if (! gui->initialized()) {
|
||||
return;
|
||||
}
|
||||
#if 0
|
||||
// Load the cummulative config over the currently active profiles.
|
||||
//FIXME if multiple configs are loaded, only the last one will have an effect.
|
||||
// We need to decide what to do about loading of separate presets (just print preset, just filament preset etc).
|
||||
// As of now only the full configs are supported here.
|
||||
if (! print_config.empty())
|
||||
gui->mainframe->load_config(print_config);
|
||||
#endif
|
||||
if (! cli_config.load.values.empty())
|
||||
// Load the last config to give it a name at the UI. The name of the preset may be later
|
||||
// changed by loading an AMF or 3MF.
|
||||
//FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config.
|
||||
gui->mainframe->load_config_file(cli_config.load.values.back());
|
||||
// If loading a 3MF file, the config is loaded from the last one.
|
||||
gui->plater()->load_files(input_files, true, true);
|
||||
if (! extra_config.empty())
|
||||
gui->mainframe->load_config(extra_config);
|
||||
});
|
||||
return wxEntry(argc, argv);
|
||||
#else
|
||||
std::cout << "GUI support has not been built." << "\n";
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
// Initialize with defaults.
|
||||
for (const t_optiondef_map *options : { &cli_actions_config_def.options, &cli_transform_config_def.options, &cli_misc_config_def.options })
|
||||
for (const std::pair<t_config_option_key, ConfigOptionDef> &optdef : *options)
|
||||
m_config.optptr(optdef.first, true);
|
||||
|
||||
// apply command line options to a more specific DynamicPrintConfig which provides normalize()
|
||||
// (command line options override --load files)
|
||||
print_config.apply(extra_config, true);
|
||||
|
||||
// write config if requested
|
||||
if (! cli_config.save.value.empty()) {
|
||||
print_config.normalize();
|
||||
print_config.save(cli_config.save.value);
|
||||
}
|
||||
set_data_dir(m_config.opt_string("datadir"));
|
||||
|
||||
if (cli_config.help) {
|
||||
printUsage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// read input file(s) if any
|
||||
std::vector<Model> models;
|
||||
for (const t_config_option_key &file : input_files) {
|
||||
if (! boost::filesystem::exists(file)) {
|
||||
boost::nowide::cerr << "No such file: " << file << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
Model model;
|
||||
try {
|
||||
model = Model::read_from_file(file, &print_config, true);
|
||||
} catch (std::exception &e) {
|
||||
boost::nowide::cerr << file << ": " << e.what() << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
if (model.objects.empty()) {
|
||||
boost::nowide::cerr << "Error: file is empty: " << file << std::endl;
|
||||
continue;
|
||||
}
|
||||
model.add_default_instances();
|
||||
// apply command line transform options
|
||||
for (ModelObject* o : model.objects) {
|
||||
/*
|
||||
if (cli_config.scale_to_fit.is_positive_volume())
|
||||
o->scale_to_fit(cli_config.scale_to_fit.value);
|
||||
*/
|
||||
// TODO: honor option order?
|
||||
o->scale(cli_config.scale.value);
|
||||
o->rotate(Geometry::deg2rad(cli_config.rotate_x.value), X);
|
||||
o->rotate(Geometry::deg2rad(cli_config.rotate_y.value), Y);
|
||||
o->rotate(Geometry::deg2rad(cli_config.rotate.value), Z);
|
||||
}
|
||||
// TODO: handle --merge
|
||||
models.push_back(model);
|
||||
}
|
||||
|
||||
for (Model &model : models) {
|
||||
if (cli_config.info) {
|
||||
// --info works on unrepaired model
|
||||
model.print_info();
|
||||
} else if (cli_config.export_3mf) {
|
||||
std::string outfile = cli_config.output.value;
|
||||
if (outfile.empty()) outfile = model.objects.front()->input_file;
|
||||
// Check if the file is already a 3mf.
|
||||
if(outfile.substr(outfile.find_last_of('.'), outfile.length()) == ".3mf")
|
||||
outfile = outfile.substr(0, outfile.find_last_of('.')) + "_2" + ".3mf";
|
||||
else
|
||||
// Remove the previous extension and add .3mf extention.
|
||||
outfile = outfile.substr(0, outfile.find_last_of('.')) + ".3mf";
|
||||
store_3mf(outfile.c_str(), &model, nullptr);
|
||||
boost::nowide::cout << "File file exported to " << outfile << std::endl;
|
||||
} else if (cli_config.cut > 0) {
|
||||
model.repair();
|
||||
model.translate(0, 0, - model.bounding_box().min(2));
|
||||
if (! model.objects.empty()) {
|
||||
// XXX
|
||||
// Model out;
|
||||
// model.objects.front()->cut(cli_config.cut, &out);
|
||||
// ModelObject &upper = *out.objects[0];
|
||||
// ModelObject &lower = *out.objects[1];
|
||||
// // Use the input name and trim off the extension.
|
||||
// std::string outfile = cli_config.output.value;
|
||||
// if (outfile.empty())
|
||||
// outfile = model.objects.front()->input_file;
|
||||
// outfile = outfile.substr(0, outfile.find_last_of('.'));
|
||||
// std::cerr << outfile << "\n";
|
||||
// if (upper.facets_count() > 0)
|
||||
// upper.mesh().write_binary((outfile + "_upper.stl").c_str());
|
||||
// if (lower.facets_count() > 0)
|
||||
// lower.mesh().write_binary((outfile + "_lower.stl").c_str());
|
||||
}
|
||||
} else if (cli_config.slice) {
|
||||
PrinterTechnology printer_technology = print_config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology", true)->value;
|
||||
std::string outfile = cli_config.output.value;
|
||||
Print fff_print;
|
||||
SLAPrint sla_print;
|
||||
PrintBase *print = (printer_technology == ptFFF) ? static_cast<PrintBase*>(&fff_print) : static_cast<PrintBase*>(&sla_print);
|
||||
if (! cli_config.dont_arrange) {
|
||||
//FIXME make the min_object_distance configurable.
|
||||
model.arrange_objects(fff_print.config().min_object_distance());
|
||||
model.center_instances_around_point(cli_config.print_center);
|
||||
}
|
||||
if (printer_technology == ptFFF) {
|
||||
for (auto* mo : model.objects)
|
||||
fff_print.auto_assign_extruders(mo);
|
||||
}
|
||||
print_config.normalize();
|
||||
print->apply(model, print_config);
|
||||
std::string err = print->validate();
|
||||
if (err.empty()) {
|
||||
if (printer_technology == ptFFF) {
|
||||
// The outfile is processed by a PlaceholderParser.
|
||||
fff_print.export_gcode(outfile, nullptr);
|
||||
} else {
|
||||
assert(printer_technology == ptSLA);
|
||||
//FIXME add the output here
|
||||
}
|
||||
} else
|
||||
std::cerr << err << "\n";
|
||||
} else {
|
||||
boost::nowide::cerr << "error: command not supported" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void printUsage()
|
||||
void CLI::print_help(bool include_print_options) const
|
||||
{
|
||||
std::cout << "Slic3r " << SLIC3R_VERSION << " is a STL-to-GCODE translator for RepRap 3D printers" << "\n"
|
||||
<< "written by Alessandro Ranellucci <aar@cpan.org> - http://slic3r.org/ - https://github.com/slic3r/Slic3r" << "\n"
|
||||
// << "Git Version " << BUILD_COMMIT << "\n\n"
|
||||
<< "Usage: ./slic3r [ OPTIONS ] [ file.stl ] [ file2.stl ] ..." << "\n";
|
||||
// CLI Options
|
||||
std::cout << "** CLI OPTIONS **\n";
|
||||
print_cli_options(boost::nowide::cout);
|
||||
std::cout << "****\n";
|
||||
// Print options
|
||||
std::cout << "** PRINT OPTIONS **\n";
|
||||
print_print_options(boost::nowide::cout);
|
||||
std::cout << "****\n";
|
||||
boost::nowide::cout
|
||||
<< "Slic3r Prusa Edition " << SLIC3R_BUILD << std::endl
|
||||
<< "https://github.com/prusa3d/Slic3r" << std::endl << std::endl
|
||||
<< "Usage: slic3r [ ACTIONS ] [ TRANSFORM ] [ OPTIONS ] [ file.stl ... ]" << std::endl
|
||||
<< std::endl
|
||||
<< "Actions:" << std::endl;
|
||||
cli_actions_config_def.print_cli_help(boost::nowide::cout, false);
|
||||
|
||||
boost::nowide::cout
|
||||
<< std::endl
|
||||
<< "Transform options:" << std::endl;
|
||||
cli_transform_config_def.print_cli_help(boost::nowide::cout, false);
|
||||
|
||||
boost::nowide::cout
|
||||
<< std::endl
|
||||
<< "Other options:" << std::endl;
|
||||
cli_misc_config_def.print_cli_help(boost::nowide::cout, false);
|
||||
|
||||
if (include_print_options) {
|
||||
boost::nowide::cout << std::endl;
|
||||
print_config_def.print_cli_help(boost::nowide::cout, true);
|
||||
} else {
|
||||
boost::nowide::cout
|
||||
<< std::endl
|
||||
<< "Run --help-options to see the full listing of print/G-code options." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool CLI::export_models(IO::ExportFormat format)
|
||||
{
|
||||
for (Model &model : m_models) {
|
||||
const std::string path = this->output_filepath(model, format);
|
||||
bool success = false;
|
||||
switch (format) {
|
||||
case IO::AMF: success = Slic3r::store_amf(path.c_str(), &model, nullptr); break;
|
||||
case IO::OBJ: success = Slic3r::store_obj(path.c_str(), &model); break;
|
||||
case IO::STL: success = Slic3r::store_stl(path.c_str(), &model, true); break;
|
||||
case IO::TMF: success = Slic3r::store_3mf(path.c_str(), &model, nullptr); break;
|
||||
default: assert(false); break;
|
||||
}
|
||||
if (success)
|
||||
std::cout << "File exported to " << path << std::endl;
|
||||
else {
|
||||
std::cerr << "File export to " << path << " failed" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string CLI::output_filepath(const Model &model, IO::ExportFormat format) const
|
||||
{
|
||||
std::string ext;
|
||||
switch (format) {
|
||||
case IO::AMF: ext = ".amf"; break;
|
||||
case IO::OBJ: ext = ".obj"; break;
|
||||
case IO::STL: ext = ".stl"; break;
|
||||
case IO::TMF: ext = ".3mf"; break;
|
||||
default: assert(false); break;
|
||||
};
|
||||
auto proposed_path = boost::filesystem::path(model.propose_export_file_name_and_path(ext));
|
||||
// use --output when available
|
||||
std::string cmdline_param = m_config.opt_string("output", false);
|
||||
if (! cmdline_param.empty()) {
|
||||
// if we were supplied a directory, use it and append our automatically generated filename
|
||||
boost::filesystem::path cmdline_path(cmdline_param);
|
||||
if (boost::filesystem::is_directory(cmdline_path))
|
||||
proposed_path = cmdline_path / proposed_path.filename();
|
||||
else
|
||||
proposed_path = cmdline_path;
|
||||
}
|
||||
return proposed_path.string();
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
@ -309,7 +634,12 @@ extern "C" {
|
||||
for (size_t i = 0; i < argc; ++ i)
|
||||
argv_ptrs[i] = const_cast<char*>(argv_narrow[i].data());
|
||||
// Call the UTF8 main.
|
||||
return slic3r_main_(argc, argv_ptrs.data());
|
||||
return CLI().run(argc, argv_ptrs.data());
|
||||
}
|
||||
}
|
||||
#else /* _MSC_VER */
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
return CLI().run(argc, argv);
|
||||
}
|
||||
#endif /* _MSC_VER */
|
||||
|
@ -1289,7 +1289,7 @@ void PresetBundle::update_compatible(bool select_other_if_incompatible)
|
||||
{
|
||||
const Preset &printer_preset = this->printers.get_edited_preset();
|
||||
|
||||
switch (printers.get_edited_preset().printer_technology()) {
|
||||
switch (printer_preset.printer_technology()) {
|
||||
case ptFFF:
|
||||
{
|
||||
assert(printer_preset.config.has("default_print_profile"));
|
||||
|
@ -115,6 +115,8 @@ SV* ConfigOption_to_SV(const ConfigOption &opt, const ConfigOptionDef &def)
|
||||
}
|
||||
case coPoint:
|
||||
return perl_to_SV_clone_ref(static_cast<const ConfigOptionPoint*>(&opt)->value);
|
||||
case coPoint3:
|
||||
return perl_to_SV_clone_ref(static_cast<const ConfigOptionPoint3*>(&opt)->value);
|
||||
case coPoints:
|
||||
{
|
||||
auto optv = static_cast<const ConfigOptionPoints*>(&opt);
|
||||
@ -248,6 +250,8 @@ bool ConfigBase__set(ConfigBase* THIS, const t_config_option_key &opt_key, SV* v
|
||||
}
|
||||
case coPoint:
|
||||
return from_SV_check(value, &static_cast<ConfigOptionPoint*>(opt)->value);
|
||||
case coPoint3:
|
||||
return from_SV_check(value, &static_cast<const ConfigOptionPoint3*>(&opt)->value);
|
||||
case coPoints:
|
||||
{
|
||||
std::vector<Vec2d> &values = static_cast<ConfigOptionPoints*>(opt)->values;
|
||||
|
@ -159,6 +159,8 @@ print_config_def()
|
||||
opt_type = "s@";
|
||||
} else if (optdef->type == coPoint || optdef->type == coPoints) {
|
||||
opt_type = "point";
|
||||
} else if (optdef.type == coPoint3) {
|
||||
opt_type = "point3";
|
||||
} else if (optdef->type == coBool || optdef->type == coBools) {
|
||||
opt_type = "bool";
|
||||
} else if (optdef->type == coEnum) {
|
||||
|
Loading…
Reference in New Issue
Block a user