mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	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
					
				
					 22 changed files with 1131 additions and 530 deletions
				
			
		| 
						 | 
				
			
			@ -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"));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue