Redesigned C++ CLI

This commit is contained in:
Alessandro Ranellucci 2018-11-17 16:43:29 +01:00 committed by Joseph Lenox
parent e4fd0e3200
commit bb29661a90
23 changed files with 755 additions and 371 deletions

View File

@ -212,6 +212,7 @@ add_library(libslic3r STATIC
${LIBDIR}/libslic3r/PrintConfig.cpp
${LIBDIR}/libslic3r/PrintObject.cpp
${LIBDIR}/libslic3r/PrintRegion.cpp
${LIBDIR}/libslic3r/SimplePrint.cpp
${LIBDIR}/libslic3r/SLAPrint.cpp
${LIBDIR}/libslic3r/SlicingAdaptive.cpp
${LIBDIR}/libslic3r/Surface.cpp

View File

@ -1,102 +1,93 @@
#include "ConfigBase.hpp"
#include "slic3r.hpp"
#include "Geometry.hpp"
#include "IO.hpp"
#include "Model.hpp"
#include "SLAPrint.hpp"
#include "Print.hpp"
#include "SimplePrint.hpp"
#include "TriangleMesh.hpp"
#include "libslic3r.h"
#include <cmath>
#include <chrono>
#include <cstdio>
#include <string>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <math.h>
#include <boost/filesystem.hpp>
#include <boost/nowide/args.hpp>
#include <boost/nowide/iostream.hpp>
#ifdef USE_WX
#include "GUI/GUI.hpp"
#endif
/// utility function for displaying CLI usage
void printUsage();
using namespace Slic3r;
int
main(int argc, char **argv)
{
main(int argc, char **argv) {
return CLI().run(argc, argv);
}
int CLI::run(int argc, char **argv) {
// Convert arguments to UTF-8 (needed on Windows).
// argv then points to memory owned by a.
boost::nowide::args a(argc, argv);
// parse all command line options into a DynamicConfig
ConfigDef config_def;
config_def.merge(cli_config_def);
config_def.merge(print_config_def);
DynamicConfig config(&config_def);
t_config_option_keys input_files;
t_config_option_keys opt_order;
this->config_def.merge(cli_actions_config_def);
this->config_def.merge(cli_transform_config_def);
this->config_def.merge(cli_misc_config_def);
this->config_def.merge(print_config_def);
this->config.def = &this->config_def;
// if any option is unsupported, print usage and abort immediately
if ( !config.read_cli(argc, argv, &input_files) )
{
printUsage();
return 0;
if (!this->config.read_cli(argc, argv, &this->input_files, &opt_order)) {
this->print_help();
return 1;
}
// apply command line options to a more handy CLIConfig
CLIConfig cli_config;
cli_config.apply(config, true);
// TODO: validate this->config (min, max etc.)
DynamicPrintConfig print_config;
#ifdef USE_WX
if (cli_config.gui) {
GUI::App *gui = new GUI::App();
GUI::App::SetInstance(gui);
wxEntry(argc, argv);
// parse actions and transform options
for (auto const &opt_key : opt_order) {
if (cli_actions_config_def.has(opt_key)) this->actions.push_back(opt_key);
if (cli_transform_config_def.has(opt_key)) this->transforms.push_back(opt_key);
}
#else
if (cli_config.gui) {
std::cout << "GUI support has not been built." << "\n";
}
#endif
// load config files supplied via --load
for (const std::string &file : cli_config.load.values) {
for (auto const &file : config.getStrings("load")) {
if (!boost::filesystem::exists(file)) {
boost::nowide::cout << "No such file: " << file << std::endl;
exit(1);
if (config.getBool("ignore_nonexistent_file", false)) {
continue;
} else {
boost::nowide::cerr << "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;
boost::nowide::cerr << "Error while reading config file: " << e.what() << std::endl;
exit(1);
}
c.normalize();
print_config.apply(c);
this->print_config.apply(c);
}
// apply command line options to a more specific DynamicPrintConfig which provides normalize()
// (command line options override --load files)
print_config.apply(config, true);
print_config.normalize();
this->print_config.apply(config, true);
this->print_config.normalize();
// write config if requested
if (!cli_config.save.value.empty()) print_config.save(cli_config.save.value);
// create a static (full) print config to be used in our logic
this->full_print_config.apply(this->print_config);
// 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);
}
for (auto const &file : input_files) {
Model model;
try {
model = Model::read_from_file(file);
@ -110,151 +101,285 @@ main(int argc, char **argv)
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);
}
if (cli_config.help) {
printUsage();
return 0;
this->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_obj) {
std::string outfile = cli_config.output.value;
if (outfile.empty()) outfile = model.objects.front()->input_file + ".obj";
TriangleMesh mesh = model.mesh();
mesh.repair();
IO::OBJ::write(mesh, outfile);
boost::nowide::cout << "File exported to " << outfile << std::endl;
} else if (cli_config.export_pov) {
std::string outfile = cli_config.output.value;
if (outfile.empty()) outfile = model.objects.front()->input_file + ".pov";
TriangleMesh mesh = model.mesh();
mesh.repair();
IO::POV::write(mesh, outfile);
boost::nowide::cout << "File exported to " << outfile << std::endl;
} else if (cli_config.export_svg) {
std::string outfile = cli_config.output.value;
if (outfile.empty())
outfile = model.objects.front()->input_file + ".svg";
SLAPrint print(&model); // initialize print with model
print.config.apply(print_config, true); // apply configuration
print.slice(); // slice file
print.write_svg(outfile); // write SVG
boost::nowide::cout << "SVG file exported to " << outfile << std::endl;
} 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";
IO::TMF::write(model, outfile);
boost::nowide::cout << "File file exported to " << outfile << std::endl;
} else if (cli_config.cut_x > 0 || cli_config.cut_y > 0 || cli_config.cut > 0) {
model.repair();
model.translate(0, 0, -model.bounding_box().min.z);
if (!model.objects.empty()) {
// FIXME: cut all objects
Model out;
if (cli_config.cut_x > 0) {
model.objects.front()->cut(X, cli_config.cut_x, &out);
} else if (cli_config.cut_y > 0) {
model.objects.front()->cut(Y, cli_config.cut_y, &out);
// loop through transform options
for (auto const &opt_key : this->transforms) {
if (opt_key == "merge") {
Model m;
for (auto &model : this->models)
m.merge(model);
this->models = {m};
} else if (opt_key == "duplicate") {
const BoundingBoxf bb{ this->full_print_config.bed_shape.values };
for (auto &model : this->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(this->config.getInt("duplicate"), this->full_print_config.min_object_distance(), &bb);
} else {
model.objects.front()->cut(Z, cli_config.cut, &out);
model.add_default_instances();
model.duplicate_objects(this->config.getInt("duplicate"), this->full_print_config.min_object_distance(), &bb);
}
}
} else if (opt_key == "duplicate_grid") {
auto &ints = this->config.opt<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 = this->full_print_config.duplicate_distance.value;
for (auto &model : this->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 : this->models) {
model.center_instances_around_point(config.opt<ConfigOptionPoint>("center")->value);
model.align_to_ground();
}
} else if (opt_key == "align_xy") {
const Pointf p{ this->config.opt<ConfigOptionPoint>("align_xy")->value };
for (auto &model : this->models) {
BoundingBoxf3 bb{ model.bounding_box() };
model.translate(-(bb.min.x - p.x), -(bb.min.y - p.y), -bb.min.z);
}
} else if (opt_key == "rotate") {
for (auto &model : this->models)
for (auto &o : model.objects)
o->rotate(Geometry::deg2rad(config.getFloat(opt_key)), Z);
} else if (opt_key == "rotate_x") {
for (auto &model : this->models)
for (auto &o : model.objects)
o->rotate(Geometry::deg2rad(config.getFloat(opt_key)), X);
} else if (opt_key == "rotate_y") {
for (auto &model : this->models)
for (auto &o : model.objects)
o->rotate(Geometry::deg2rad(config.getFloat(opt_key)), Y);
} else if (opt_key == "scale") {
for (auto &model : this->models)
for (auto &o : model.objects)
o->scale(config.get_abs_value(opt_key, 1));
} else if (opt_key == "scale_to_fit") {
const auto opt = config.opt<ConfigOptionPoint3>(opt_key);
if (!opt->is_positive_volume()) {
boost::nowide::cerr << "--scale-to-fit requires a positive volume" << std::endl;
return 1;
}
for (auto &model : this->models)
for (auto &o : model.objects)
o->scale_to_fit(opt->value);
} else if (opt_key == "cut" || opt_key == "cut_x" || opt_key == "cut_y") {
std::vector<Model> new_models;
for (auto &model : this->models) {
model.repair();
model.translate(0, 0, -model.bounding_box().min.z); // align to z = 0
Model out;
for (auto &o : model.objects) {
if (opt_key == "cut_x") {
o->cut(X, config.getFloat("cut_x"), &out);
} else if (opt_key == "cut_y") {
o->cut(Y, config.getFloat("cut_y"), &out);
} else if (opt_key == "cut") {
o->cut(Z, config.getFloat("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";
// add each resulting object as a distinct model
Model upper, lower;
auto upper_obj = upper.add_object(*out.objects[0]);
auto lower_obj = lower.add_object(*out.objects[1]);
if (upper_obj->facets_count() > 0) new_models.push_back(upper);
if (lower_obj->facets_count() > 0) new_models.push_back(lower);
}
if (upper.facets_count() > 0) {
TriangleMesh m = upper.mesh();
IO::STL::write(m, outfile + "_upper.stl");
}
if (lower.facets_count() > 0) {
TriangleMesh m = lower.mesh();
IO::STL::write(m, outfile + "_lower.stl");
// TODO: copy less stuff around using pointers
this->models = new_models;
if (this->actions.empty())
this->actions.push_back("export_stl");
} else if (opt_key == "cut_grid") {
std::vector<Model> new_models;
for (auto &model : this->models) {
TriangleMesh mesh = model.mesh();
mesh.repair();
TriangleMeshPtrs meshes = mesh.cut_by_grid(config.opt<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;
}
}
} else if (cli_config.cut_grid.value.x > 0 && cli_config.cut_grid.value.y > 0) {
TriangleMesh mesh = model.mesh();
mesh.repair();
TriangleMeshPtrs meshes = mesh.cut_by_grid(cli_config.cut_grid.value);
size_t i = 0;
for (TriangleMesh* m : meshes) {
std::ostringstream ss;
ss << model.objects.front()->input_file << "_" << i++ << ".stl";
IO::STL::write(*m, ss.str());
delete m;
}
} else if (cli_config.slice) {
std::string outfile = cli_config.output.value;
Print print;
model.arrange_objects(print.config.min_object_distance());
model.center_instances_around_point(cli_config.center);
if (outfile.empty()) outfile = model.objects.front()->input_file + ".gcode";
print.apply_config(print_config);
for (auto* mo : model.objects) {
print.auto_assign_extruders(mo);
print.add_model_object(mo);
}
print.validate();
print.export_gcode(outfile);
// TODO: copy less stuff around using pointers
this->models = new_models;
if (this->actions.empty())
this->actions.push_back("export_stl");
} else if (opt_key == "split") {
for (auto &model : this->models)
model.split();
} else if (opt_key == "repair") {
for (auto &model : this->models)
model.repair();
} else {
boost::nowide::cerr << "error: command not supported" << std::endl;
boost::nowide::cerr << "error: option not implemented yet: " << opt_key << std::endl;
return 1;
}
}
// loop through action options
for (auto const &opt_key : this->actions) {
if (opt_key == "help") {
this->print_help();
} else if (opt_key == "save") {
this->print_config.save(config.getString("save"));
} else if (opt_key == "info") {
// --info works on unrepaired model
for (const Model &model : this->models)
model.print_info();
} else if (opt_key == "export_stl") {
this->export_models(IO::STL);
} else if (opt_key == "export_obj") {
this->export_models(IO::OBJ);
} else if (opt_key == "export_pov") {
this->export_models(IO::POV);
} else if (opt_key == "export_amf") {
this->export_models(IO::AMF);
} else if (opt_key == "export_3mf") {
this->export_models(IO::TMF);
} else if (opt_key == "export_sla") {
boost::nowide::cerr << "--export-sla is not implemented yet" << std::endl;
} else if (opt_key == "export_sla_svg") {
for (const Model &model : this->models) {
SLAPrint print(&model); // initialize print with model
print.config.apply(this->print_config, true); // apply configuration
print.slice(); // slice file
const std::string outfile = this->output_filepath(model, IO::SVG);
print.write_svg(outfile); // write SVG
boost::nowide::cout << "SVG file exported to " << outfile << std::endl;
}
} else if (opt_key == "export_gcode") {
for (const Model &model : this->models) {
SimplePrint print;
print.status_cb = [](int ln, const std::string& msg) {
boost::nowide::cout << msg << std::endl;
};
print.center = !this->config.has("center")
&& !this->config.has("align_xy")
&& !this->config.getBool("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);
print.export_gcode(outfile);
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 (actions.empty()) {
#ifdef USE_WX
GUI::App *gui = new GUI::App();
GUI::App::SetInstance(gui);
wxEntry(argc, argv);
#else
std::cout << "GUI support has not been built." << "\n";
#endif
}
return 0;
}
void printUsage()
{
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 (C++ only): ./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";
void
CLI::print_help() const {
std::cout << "Slic3r " << SLIC3R_VERSION << " is a STL-to-GCODE translator for RepRap 3D printers" << "\n"
<< "written by Alessandro Ranellucci & the Slic3r community - https://slic3r.org/ - https://github.com/slic3r/Slic3r" << "\n"
<< "Git Version " << BUILD_COMMIT << "\n\n"
<< "Usage (C++ only): ./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";
}
void
CLI::export_models(IO::ExportFormat format) {
for (size_t i = 0; i < this->models.size(); ++i) {
Model &model = this->models[i];
const std::string outfile = this->output_filepath(model, format);
IO::write_model.at(format)(model, outfile);
std::cout << "File exported to " << outfile << std::endl;
}
}
std::string
CLI::output_filepath(const Model &model, IO::ExportFormat format) const {
// get the --output-filename-format option
std::string filename_format = this->print_config.getString("output_filename_format", "[input_filename_base]");
// strip the file extension and add the correct one
filename_format = filename_format.substr(0, filename_format.find_last_of("."));
filename_format += "." + IO::extensions.at(format);
// this is the same logic used in Print::output_filepath()
// TODO: factor it out to a single place?
// find the first input_file of the model
boost::filesystem::path input_file;
for (auto o : model.objects) {
if (!o->input_file.empty()) {
input_file = o->input_file;
break;
}
}
// compute the automatic filename
PlaceholderParser pp;
pp.set("input_filename", input_file.filename().string());
pp.set("input_filename_base", input_file.stem().string());
pp.apply_config(this->config);
const std::string filename = pp.process(filename_format);
// use --output when available
std::string outfile{ this->config.getString("output") };
if (!outfile.empty()) {
// if we were supplied a directory, use it and append our automatically generated filename
const boost::filesystem::path out(outfile);
if (boost::filesystem::is_directory(out))
outfile = (out / filename).string();
} else {
outfile = (input_file.parent_path() / filename).string();
}
return outfile;
}

29
src/slic3r.hpp Normal file
View File

@ -0,0 +1,29 @@
#ifndef SLIC3R_HPP
#define SLIC3R_HPP
#include "ConfigBase.hpp"
#include "IO.hpp"
#include "Model.hpp"
namespace Slic3r {
class CLI {
public:
int run(int argc, char **argv);
private:
ConfigDef config_def;
DynamicConfig config;
DynamicPrintConfig print_config;
FullPrintConfig full_print_config;
t_config_option_keys input_files, actions, transforms;
std::vector<Model> models;
void print_help() const;
void export_models(IO::ExportFormat format);
std::string output_filepath(const Model &model, IO::ExportFormat format) const;
};
}
#endif

View File

@ -64,7 +64,6 @@ public:
if (print_config_def.options.count(opt_key) == 0) throw InvalidOptionType(opt_key + std::string(" is an invalid option."));
return (dynamic_cast<ConfigOption*>(this->_config.optptr(opt_key, create)))->getFloat();
}
int getInt(const t_config_option_key& opt_key, bool create=true) {
if (print_config_def.options.count(opt_key) == 0) throw InvalidOptionType(opt_key + std::string(" is an invalid option."));
return (dynamic_cast<ConfigOption*>(this->_config.optptr(opt_key, create)))->getInt();

View File

@ -374,6 +374,36 @@ ConfigBase::get_abs_value(const t_config_option_key &opt_key, double ratio_over)
return opt->get_abs_value(ratio_over);
}
bool
ConfigBase::getBool(const t_config_option_key &opt_key, bool default_value) const {
auto opt = this->opt<ConfigOptionBool>(opt_key);
return opt == nullptr ? default_value : opt->value;
}
double
ConfigBase::getFloat(const t_config_option_key &opt_key, double default_value) const {
auto opt = this->opt<ConfigOptionFloat>(opt_key);
return opt == nullptr ? default_value : opt->value;
}
int
ConfigBase::getInt(const t_config_option_key &opt_key, double default_value) const {
auto opt = this->opt<ConfigOptionInt>(opt_key);
return opt == nullptr ? default_value : opt->value;
}
std::string
ConfigBase::getString(const t_config_option_key &opt_key, std::string default_value) const {
auto opt = this->opt<ConfigOptionString>(opt_key);
return opt == nullptr ? default_value : opt->value;
}
std::vector<std::string>
ConfigBase::getStrings(const t_config_option_key &opt_key, std::vector<std::string> default_value) const {
auto opt = this->opt<ConfigOptionStrings>(opt_key);
return opt == nullptr ? default_value : opt->values;
}
void
ConfigBase::setenv_()
{
@ -451,6 +481,7 @@ DynamicConfig& DynamicConfig::operator= (DynamicConfig other)
void
DynamicConfig::swap(DynamicConfig &other)
{
std::swap(this->def, other.def);
std::swap(this->options, other.options);
}
@ -541,7 +572,7 @@ DynamicConfig::empty() const {
}
void
DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra)
DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra, t_config_option_keys* keys)
{
std::vector<char*> _argv;
@ -551,11 +582,11 @@ DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_
for (size_t i = 0; i < tokens.size(); ++i)
_argv.push_back(const_cast<char *>(tokens[i].c_str()));
this->read_cli(_argv.size(), &_argv[0], extra);
this->read_cli(_argv.size(), &_argv[0], extra, keys);
}
bool
DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra)
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;
@ -630,6 +661,10 @@ 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);
}
if (ConfigOptionBool* opt = this->opt<ConfigOptionBool>(opt_key, true)) {
opt->value = !no;
} else if (ConfigOptionBools* opt = this->opt<ConfigOptionBools>(opt_key, true)) {

View File

@ -45,6 +45,7 @@ class ConfigOption {
virtual void setFloat(double val) {};
virtual void setString(std::string val) {};
virtual std::string getString() const { return ""; };
virtual std::vector<std::string> getStrings() const { return std::vector<std::string>(); };
friend bool operator== (const ConfigOption &a, const ConfigOption &b);
friend bool operator!= (const ConfigOption &a, const ConfigOption &b);
};
@ -259,6 +260,8 @@ class ConfigOptionStrings : public ConfigOptionVector<std::string>
ConfigOptionStrings(const std::vector<std::string> _values) : ConfigOptionVector<std::string>(_values) {};
ConfigOptionStrings* clone() const { return new ConfigOptionStrings(this->values); };
std::vector<std::string> getStrings() const { return this->values; };
std::string serialize() const {
return escape_strings_cstyle(this->values);
};
@ -387,7 +390,7 @@ class ConfigOptionPoint3 : public ConfigOptionSingle<Pointf3>
bool deserialize(std::string str, bool append = false);
bool is_positive_volume () {
bool is_positive_volume() const {
return this->value.x > 0 && this->value.y > 0 && this->value.z > 0;
};
};
@ -721,6 +724,11 @@ class ConfigBase
virtual bool set_deserialize(t_config_option_key opt_key, std::string str, bool append = false);
double get_abs_value(const t_config_option_key &opt_key) const;
double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const;
bool getBool(const t_config_option_key &opt_key, bool default_value = false) const;
double getFloat(const t_config_option_key &opt_key, double default_value = 0.0) const;
int getInt(const t_config_option_key &opt_key, double default_value = 0) const;
std::string getString(const t_config_option_key &opt_key, std::string default_value = "") const;
std::vector<std::string> getStrings(const t_config_option_key &opt_key, std::vector<std::string> default_value = std::vector<std::string>()) const;
void setenv_();
void load(const std::string &file);
void save(const std::string &file) const;
@ -742,8 +750,8 @@ class DynamicConfig : public virtual ConfigBase
void erase(const t_config_option_key &opt_key);
void clear();
bool empty() const;
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);
private:
typedef std::map<t_config_option_key,ConfigOption*> t_options_map;

View File

@ -1,6 +1,7 @@
#include "IO.hpp"
#include <stdexcept>
#include <fstream>
#include <functional>
#include <iostream>
#include <boost/filesystem.hpp>
#include <boost/nowide/fstream.hpp>
@ -10,6 +11,24 @@
namespace Slic3r { namespace IO {
const std::map<ExportFormat,std::string> extensions{
{STL, "stl"},
{OBJ, "obj"},
{POV, "pov"},
{AMF, "amf"},
{TMF, "3mf"},
{SVG, "svg"},
{Gcode, "gcode"},
};
const std::map<ExportFormat,bool(*)(const Model&,std::string)> write_model{
{STL, &STL::write},
{OBJ, &OBJ::write},
{POV, &POV::write},
{AMF, &AMF::write},
{TMF, &TMF::write},
};
bool
STL::read(std::string input_file, TriangleMesh* mesh)
{
@ -44,14 +63,14 @@ STL::read(std::string input_file, Model* model)
}
bool
STL::write(Model& model, std::string output_file, bool binary)
STL::write(const Model &model, std::string output_file, bool binary)
{
TriangleMesh mesh = model.mesh();
return STL::write(mesh, output_file, binary);
}
bool
STL::write(TriangleMesh& mesh, std::string output_file, bool binary)
STL::write(const TriangleMesh &mesh, std::string output_file, bool binary)
{
if (binary) {
mesh.write_binary(output_file);
@ -134,21 +153,28 @@ OBJ::read(std::string input_file, Model* model)
}
bool
OBJ::write(Model& model, std::string output_file)
OBJ::write(const Model& model, std::string output_file)
{
TriangleMesh mesh = model.mesh();
return OBJ::write(mesh, output_file);
}
bool
OBJ::write(TriangleMesh& mesh, std::string output_file)
OBJ::write(const TriangleMesh& mesh, std::string output_file)
{
mesh.WriteOBJFile(output_file);
return true;
}
bool
POV::write(TriangleMesh& mesh, std::string output_file)
POV::write(const Model &model, std::string output_file)
{
TriangleMesh mesh{ model.mesh() };
return STL::write(mesh, output_file);
}
bool
POV::write(const TriangleMesh& mesh, std::string output_file)
{
TriangleMesh mesh2 = mesh;
mesh2.center_around_origin();

View File

@ -4,17 +4,26 @@
#include "libslic3r.h"
#include "Model.hpp"
#include "TriangleMesh.hpp"
#include <map>
#include <string>
namespace Slic3r { namespace IO {
enum ExportFormat { AMF, OBJ, POV, STL, SVG, TMF, Gcode };
extern const std::map<ExportFormat,std::string> extensions;
extern const std::map<ExportFormat,bool(*)(const Model&,std::string)> write_model;
class STL
{
public:
static bool read(std::string input_file, TriangleMesh* mesh);
static bool read(std::string input_file, Model* model);
static bool write(Model& model, std::string output_file, bool binary = true);
static bool write(TriangleMesh& mesh, std::string output_file, bool binary = true);
static bool write(const Model &model, std::string output_file) {
return STL::write(model, output_file, true);
};
static bool write(const Model &model, std::string output_file, bool binary);
static bool write(const TriangleMesh &mesh, std::string output_file, bool binary = true);
};
class OBJ
@ -22,28 +31,29 @@ class OBJ
public:
static bool read(std::string input_file, TriangleMesh* mesh);
static bool read(std::string input_file, Model* model);
static bool write(Model& model, std::string output_file);
static bool write(TriangleMesh& mesh, std::string output_file);
static bool write(const Model& model, std::string output_file);
static bool write(const TriangleMesh& mesh, std::string output_file);
};
class AMF
{
public:
static bool read(std::string input_file, Model* model);
static bool write(Model& model, std::string output_file);
static bool write(const Model& model, std::string output_file);
};
class POV
{
public:
static bool write(TriangleMesh& mesh, std::string output_file);
static bool write(const Model& model, std::string output_file);
static bool write(const TriangleMesh& mesh, std::string output_file);
};
class TMF
{
public:
static bool read(std::string input_file, Model* model);
static bool write(Model& model, std::string output_file);
static bool write(const Model& model, std::string output_file);
};
} }

View File

@ -496,7 +496,7 @@ AMF::read(std::string input_file, Model* model)
}
bool
AMF::write(Model& model, std::string output_file)
AMF::write(const Model& model, std::string output_file)
{
using namespace std;

View File

@ -388,9 +388,10 @@ TMFEditor::~TMFEditor(){
}
bool
TMF::write(Model& model, std::string output_file)
TMF::write(const Model& model, std::string output_file)
{
TMFEditor tmf_writer(std::move(output_file), &model);
Model m2{model};
TMFEditor tmf_writer(std::move(output_file), &m2);
return tmf_writer.produce_TMF();
}

View File

@ -48,8 +48,11 @@ Model::~Model()
Model
Model::read_from_file(std::string input_file)
{
Model model;
if (!boost::filesystem::exists(input_file)) {
throw std::runtime_error("No such file");
}
Model model;
if (boost::algorithm::iends_with(input_file, ".stl")) {
IO::STL::read(input_file, &model);
} else if (boost::algorithm::iends_with(input_file, ".obj")) {
@ -72,6 +75,13 @@ Model::read_from_file(std::string input_file)
return model;
}
void
Model::merge(const Model &other)
{
for (ModelObject* o : other.objects)
this->add_object(*o, true);
}
ModelObject*
Model::add_object()
{
@ -201,6 +211,18 @@ Model::repair()
(*o)->repair();
}
void
Model::split()
{
Model new_model;
for (ModelObject* o : this->objects)
o->split(&new_model.objects);
this->clear_objects();
for (ModelObject* o : new_model.objects)
this->add_object(*o);
}
void
Model::center_instances_around_point(const Pointf &point)
{
@ -281,16 +303,14 @@ Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
// get the (transformed) size of each instance so that we take
// into account their different transformations when packing
Pointfs instance_sizes;
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) {
for (size_t i = 0; i < (*o)->instances.size(); ++i) {
instance_sizes.push_back((*o)->instance_bounding_box(i).size());
}
}
for (const ModelObject* o : this->objects)
for (size_t i = 0; i < o->instances.size(); ++i)
instance_sizes.push_back(o->instance_bounding_box(i).size());
Pointfs positions;
if (! this->_arrange(instance_sizes, dist, bb, positions))
return false;
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) {
for (ModelInstancePtrs::const_iterator i = (*o)->instances.begin(); i != (*o)->instances.end(); ++i) {
(*i)->offset = positions.back();
@ -330,13 +350,12 @@ Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb)
void
Model::duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb)
{
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) {
for (ModelObject* o : this->objects) {
// make a copy of the pointers in order to avoid recursion when appending their copies
ModelInstancePtrs instances = (*o)->instances;
for (ModelInstancePtrs::const_iterator i = instances.begin(); i != instances.end(); ++i) {
const auto instances = o->instances;
for (const ModelInstance* i : instances)
for (size_t k = 2; k <= copies_num; ++k)
(*o)->add_instance(**i);
}
o->add_instance(*i);
}
this->arrange_objects(dist, bb);
@ -642,6 +661,14 @@ Model::align_instances_to_origin()
new_center.translate(-new_center.x/2, -new_center.y/2);
this->center_instances_around_point(new_center);
}
void
Model::align_to_ground()
{
BoundingBoxf3 bb = this->bounding_box();
for (ModelObject* o : this->objects)
o->translate(0, 0, -bb.min.z);
}
void
ModelObject::align_to_ground()
@ -819,8 +846,16 @@ ModelObject::cut(Axis axis, coordf_t z, Model* model) const
ModelObject* lower = model->add_object(*this);
upper->clear_volumes();
lower->clear_volumes();
upper->input_file = "";
lower->input_file = "";
// remove extension from filename and add suffix
if (this->input_file.empty()) {
upper->input_file = "upper";
lower->input_file = "lower";
} else {
const boost::filesystem::path p{this->input_file};
upper->input_file = (p.parent_path() / p.stem()).string() + "_upper";
lower->input_file = (p.parent_path() / p.stem()).string() + "_lower";
}
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) {
ModelVolume* volume = *v;

View File

@ -73,7 +73,9 @@ class Model
/// \param input_file std::string the file path expressed in UTF-8
/// \return Model the read Model
static Model read_from_file(std::string input_file);
void merge(const Model &other);
/// Create a new object and add it to the current Model.
/// \return ModelObject* a pointer to the new Model
ModelObject* add_object();
@ -133,13 +135,17 @@ class Model
/// Repair the ModelObjects of the current Model.
/// This function calls repair function on each TriangleMesh of each model object volume
void repair();
/// Split the meshes of the ModelObjects into several distinct ModelObjects.
void split();
/// Center the total bounding box of the instances around a point.
/// This transformation works in the XY plane only and no transformation in Z is performed.
/// \param point pointf object to center the model instances of model objects around
void center_instances_around_point(const Pointf &point);
void align_instances_to_origin();
void align_to_ground();
/// Translate each ModelObject with x, y, z units.
/// \param x coordf_t units in the x direction

View File

@ -76,7 +76,7 @@ PlaceholderParser::update_timestamp()
}
}
void PlaceholderParser::apply_config(const DynamicPrintConfig &config)
void PlaceholderParser::apply_config(const DynamicConfig &config)
{
t_config_option_keys opt_keys = config.keys();
for (t_config_option_keys::const_iterator i = opt_keys.begin(); i != opt_keys.end(); ++i) {

View File

@ -21,7 +21,7 @@ class PlaceholderParser
PlaceholderParser();
void update_timestamp();
void apply_config(const DynamicPrintConfig &config);
void apply_config(const DynamicConfig &config);
void apply_env_variables();
void set(const std::string &key, const std::string &value);
void set(const std::string &key, int value);

View File

@ -705,10 +705,13 @@ Print::add_model_object(ModelObject* model_object, int idx)
}
#ifndef SLIC3RXS
void
Print::export_gcode(std::ostream& output, bool quiet)
{
// prerequisites
this->process();
if (this->status_cb != nullptr)
this->status_cb(90, "Exporting G-Code...");
@ -718,10 +721,15 @@ Print::export_gcode(std::ostream& output, bool quiet)
}
void
Print::export_gcode(const std::string& outfile, bool quiet)
Print::export_gcode(std::string outfile, bool quiet)
{
// compute the actual output filepath
outfile = this->output_filepath(outfile);
std::ofstream outstream(outfile);
this->export_gcode(outstream);
// TODO: export_gcode() is not ported completely from Perl
}

View File

@ -233,7 +233,6 @@ class Print
PrintObjectPtrs objects;
PrintRegionPtrs regions;
PlaceholderParser placeholder_parser;
// TODO: status_cb
#ifndef SLIC3RXS
std::function<void(int, const std::string&)> status_cb {nullptr};
@ -272,7 +271,7 @@ class Print
void export_gcode(std::ostream& output, bool quiet = false);
/// Performs a gcode export and then runs post-processing scripts (if any)
void export_gcode(const std::string& filename, bool quiet = false);
void export_gcode(std::string filename, bool quiet = false);
/// commands a gcode export to a temporary file and return its name
std::string export_gcode(bool quiet = false);

View File

@ -1892,10 +1892,89 @@ PrintConfigBase::_handle_legacy(t_config_option_key &opt_key, std::string &value
}
}
CLIConfigDef::CLIConfigDef()
CLIActionsConfigDef::CLIActionsConfigDef()
{
ConfigOptionDef* def;
// Actions:
def = this->add("export_obj", coBool);
def->label = __TRANS("Export SVG");
def->tooltip = __TRANS("Export the model(s) as OBJ.");
def->cli = "export-obj";
def->default_value = new ConfigOptionBool(false);
def = this->add("export_pov", coBool);
def->label = __TRANS("Export POV");
def->tooltip = __TRANS("Export the model as POV-Ray definition.");
def->cli = "export-pov";
def->default_value = new ConfigOptionBool(false);
def = this->add("export_svg", coBool);
def->label = __TRANS("Export SVG");
def->tooltip = __TRANS("Slice the model and export solid slices as SVG.");
def->cli = "export-svg";
def->default_value = new ConfigOptionBool(false);
def = this->add("export_sla_svg", coBool);
def->label = __TRANS("Export SVG for SLA");
def->tooltip = __TRANS("Slice the model and export SLA printing layers as SVG.");
def->cli = "export-sla-svg|sla";
def->default_value = new ConfigOptionBool(false);
def = this->add("export_3mf", coBool);
def->label = __TRANS("Export 3MF");
def->tooltip = __TRANS("Export the model(s) as 3MF.");
def->cli = "export-3mf";
def->default_value = new ConfigOptionBool(false);
def = this->add("export_stl", coBool);
def->label = __TRANS("Export STL");
def->tooltip = __TRANS("Export the model(s) as STL.");
def->cli = "export-stl";
def->default_value = new ConfigOptionBool(false);
def = this->add("export_gcode", coBool);
def->label = __TRANS("Export G-code");
def->tooltip = __TRANS("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 = __TRANS("Help");
def->tooltip = __TRANS("Show this help.");
def->cli = "help";
def->default_value = new ConfigOptionBool(false);
def = this->add("help_options", coBool);
def->label = __TRANS("Help (options)");
def->tooltip = __TRANS("Show the list of configuration options.");
def->cli = "help-options";
def->default_value = new ConfigOptionBool(false);
def = this->add("info", coBool);
def->label = __TRANS("Output Model Info");
def->tooltip = __TRANS("Write information about the model to the console.");
def->cli = "info";
def->default_value = new ConfigOptionBool(false);
def = this->add("save", coString);
def->label = __TRANS("Save config file");
def->tooltip = __TRANS("Save configuration to the specified file.");
def->cli = "save";
def->default_value = new ConfigOptionString();
}
CLITransformConfigDef::CLITransformConfigDef()
{
ConfigOptionDef* def;
// Transform options:
def = this->add("align_xy", coPoint);
def->label = __TRANS("Align XY");
def->tooltip = __TRANS("Align the model to the given point.");
def->cli = "align-xy";
def->default_value = new ConfigOptionPoint(Pointf(100,100));
def = this->add("cut", coFloat);
def->label = __TRANS("Cut");
def->tooltip = __TRANS("Cut model at the given Z.");
@ -1920,113 +1999,102 @@ CLIConfigDef::CLIConfigDef()
def->cli = "cut-y";
def->default_value = new ConfigOptionFloat(0);
def = this->add("export_obj", coBool);
def->label = __TRANS("Export SVG");
def->tooltip = __TRANS("Export the model as OBJ.");
def->cli = "export-obj";
def->default_value = new ConfigOptionBool(false);
def = this->add("center", coPoint);
def->label = __TRANS("Center");
def->tooltip = __TRANS("Center the print around the given center.");
def->cli = "center";
def->default_value = new ConfigOptionPoint(Pointf(100,100));
def = this->add("dont_arrange", coBool);
def->label = __TRANS("Don't arrange");
def->tooltip = __TRANS("Do not rearrange the given models before merging and keep their original XY coordinates.");
def->cli = "dont-arrange";
def = this->add("export_pov", coBool);
def->label = __TRANS("Export POV");
def->tooltip = __TRANS("Export the model as POV-Ray definition.");
def->cli = "export-pov";
def->default_value = new ConfigOptionBool(false);
def = this->add("duplicate", coInt);
def->label = __TRANS("Duplicate");
def->tooltip =__TRANS("Multiply copies by this factor.");
def->cli = "duplicate=i";
def->min = 1;
def = this->add("export_svg", coBool);
def->label = __TRANS("Export SVG");
def->tooltip = __TRANS("Slice the model and export slices as SVG.");
def->cli = "export-svg";
def->default_value = new ConfigOptionBool(false);
def = this->add("export_3mf", coBool);
def->label = __TRANS("Export 3MF");
def->tooltip = __TRANS("Slice the model and export slices as 3MF.");
def->cli = "export-3mf";
def->default_value = new ConfigOptionBool(false);
def = this->add("slice", coBool);
def->label = __TRANS("Slice");
def->tooltip = __TRANS("Slice the model and export gcode.");
def->cli = "slice";
def->default_value = new ConfigOptionBool(false);
def = this->add("help", coBool);
def->label = __TRANS("Help");
def->tooltip = __TRANS("Show this help.");
def->cli = "help";
def->default_value = new ConfigOptionBool(false);
def = this->add("gui", coBool);
def->label = __TRANS("Use GUI");
def->tooltip = __TRANS("Start the Slic3r GUI.");
def->cli = "gui";
def->default_value = new ConfigOptionBool(false);
def = this->add("info", coBool);
def->label = __TRANS("Output Model Info");
def->tooltip = __TRANS("Write information about the model to the console.");
def->cli = "info";
def->default_value = new ConfigOptionBool(false);
def = this->add("duplicate_grid", coInts);
def->label = __TRANS("Duplicate by grid");
def->tooltip = __TRANS("Multiply copies by creating a grid.");
def->cli = "duplicate-grid=i@";
def = this->add("load", coStrings);
def->label = __TRANS("Load config file");
def->tooltip = __TRANS("Load configuration from the specified file. It can be used more than once to load options from multiple files.");
def->cli = "load";
def->default_value = new ConfigOptionStrings();
def = this->add("output", coString);
def->label = __TRANS("Output File");
def->tooltip = __TRANS("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("merge", coBool);
def->label = __TRANS("Merge");
def->tooltip = __TRANS("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 = __TRANS("Repair");
def->tooltip = __TRANS("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 = __TRANS("Rotate");
def->tooltip = __TRANS("Rotation angle around the Z axis in degrees (0-360, default: 0).");
def->tooltip = __TRANS("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 = __TRANS("Rotate around X");
def->tooltip = __TRANS("Rotation angle around the X axis in degrees (0-360, default: 0).");
def->tooltip = __TRANS("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 = __TRANS("Rotate around Y");
def->tooltip = __TRANS("Rotation angle around the Y axis in degrees (0-360, default: 0).");
def->tooltip = __TRANS("Rotation angle around the Y axis in degrees.");
def->cli = "rotate-y";
def->default_value = new ConfigOptionFloat(0);
def = this->add("save", coString);
def->label = __TRANS("Save config file");
def->tooltip = __TRANS("Save configuration to the specified file.");
def->cli = "save";
def->default_value = new ConfigOptionString();
def = this->add("scale", coFloat);
def = this->add("scale", coFloatOrPercent);
def->label = __TRANS("Scale");
def->tooltip = __TRANS("Scaling factor (default: 1).");
def->tooltip = __TRANS("Scaling factor or percentage.");
def->cli = "scale";
def->default_value = new ConfigOptionFloat(1);
def->default_value = new ConfigOptionFloatOrPercent(1, false);
def = this->add("split", coBool);
def->label = __TRANS("Split");
def->tooltip = __TRANS("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 = __TRANS("Scale to Fit");
def->tooltip = __TRANS("Scale to fit the given volume.");
def->cli = "scale-to-fit";
def->default_value = new ConfigOptionPoint3(Pointf3(0,0,0));
def = this->add("center", coPoint3);
def->label = __TRANS("Center");
def->tooltip = __TRANS("Center the print around the given center (default: 100, 100).");
def->cli = "center";
def->default_value = new ConfigOptionPoint(Pointf(100,100));
}
const CLIConfigDef cli_config_def;
CLIMiscConfigDef::CLIMiscConfigDef()
{
ConfigOptionDef* def;
def = this->add("ignore_nonexistent_config", coBool);
def->label = __TRANS("Ignore non-existent config files");
def->tooltip = __TRANS("Do not fail if a file supplied to --load does not exist.");
def->cli = "ignore-nonexistent-config";
def = this->add("output", coString);
def->label = __TRANS("Output File");
def->tooltip = __TRANS("The file where the output will be written (if not specified, it will be based on the input file).");
def->cli = "output";
}
const CLIActionsConfigDef cli_actions_config_def;
const CLITransformConfigDef cli_transform_config_def;
const CLIMiscConfigDef cli_misc_config_def;
std::ostream&
print_cli_options(std::ostream& out) {
/*
for (const auto& opt : cli_config_def.options) {
if (opt.second.cli.size() != 0) {
out << "\t" << std::left << std::setw(40) << std::string("--") + opt.second.cli;
@ -2037,6 +2105,7 @@ print_cli_options(std::ostream& out) {
}
}
std::cerr << std::endl;
*/
return out;
}

View File

@ -639,78 +639,33 @@ class SLAPrintConfig
};
};
class CLIConfigDef : public ConfigDef
class CLIActionsConfigDef : public ConfigDef
{
public:
CLIConfigDef();
CLIActionsConfigDef();
};
extern const CLIConfigDef cli_config_def;
class CLIConfig
: public virtual ConfigBase, public StaticConfig
class CLITransformConfigDef : public ConfigDef
{
public:
ConfigOptionFloat cut;
ConfigOptionPoint cut_grid;
ConfigOptionFloat cut_x;
ConfigOptionFloat cut_y;
ConfigOptionBool export_obj;
ConfigOptionBool export_pov;
ConfigOptionBool export_svg;
ConfigOptionBool export_3mf;
ConfigOptionBool gui;
ConfigOptionBool info;
ConfigOptionBool help;
ConfigOptionStrings load;
ConfigOptionString output;
ConfigOptionFloat rotate;
ConfigOptionFloat rotate_x;
ConfigOptionFloat rotate_y;
ConfigOptionString save;
ConfigOptionFloat scale;
ConfigOptionPoint3 scale_to_fit;
ConfigOptionPoint center;
ConfigOptionBool slice;
ConfigOptionBool threads;
CLIConfig() : ConfigBase(), StaticConfig() {
this->def = &cli_config_def;
this->set_defaults();
};
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) {
OPT_PTR(cut);
OPT_PTR(cut_grid);
OPT_PTR(cut_x);
OPT_PTR(cut_y);
OPT_PTR(export_obj);
OPT_PTR(export_pov);
OPT_PTR(export_svg);
OPT_PTR(export_3mf);
OPT_PTR(gui);
OPT_PTR(help);
OPT_PTR(info);
OPT_PTR(load);
OPT_PTR(output);
OPT_PTR(rotate);
OPT_PTR(rotate_x);
OPT_PTR(rotate_y);
OPT_PTR(save);
OPT_PTR(scale);
OPT_PTR(scale_to_fit);
OPT_PTR(slice);
OPT_PTR(threads);
return NULL;
};
CLITransformConfigDef();
};
class CLIMiscConfigDef : public ConfigDef
{
public:
CLIMiscConfigDef();
};
extern const CLIActionsConfigDef cli_actions_config_def;
extern const CLITransformConfigDef cli_transform_config_def;
extern const CLIMiscConfigDef cli_misc_config_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);
std::ostream& print_cli_options(std::ostream& out);
}
#endif

View File

@ -38,12 +38,12 @@ class SLAPrint
};
std::vector<SupportPillar> sm_pillars;
SLAPrint(Model* _model) : model(_model) {};
SLAPrint(const Model* _model) : model(_model) {};
void slice();
void write_svg(const std::string &outputfile) const;
private:
Model* model;
const Model* model;
BoundingBoxf3 bb;
void _infill_layer(size_t i, const Fill* fill);

View File

@ -0,0 +1,49 @@
#include "SimplePrint.hpp"
#include "ClipperUtils.hpp"
#include <iostream>
namespace Slic3r {
void
SimplePrint::set_model(const Model &model) {
this->_model = model;
// make method idempotent so that the object is reusable
this->_print.clear_objects();
// make sure all objects have at least one defined instance
this->_model.add_default_instances();
// align to z = 0
for (ModelObject* o : this->_model.objects)
o->translate(0, 0, -o->bounding_box().min.z);
if (this->center) {
Polygon bed_polygon{ scale(this->_print.config.bed_shape.values) };
this->_model.center_instances_around_point(Slic3r::Pointf::new_unscale(bed_polygon.centroid()));
}
for (ModelObject* o : this->_model.objects) {
this->_print.auto_assign_extruders(o);
this->_print.add_model_object(o);
}
}
void
SimplePrint::export_gcode(std::string outfile) {
this->_print.status_cb = this->status_cb;
this->_print.validate();
this->_print.export_gcode(outfile);
// check that all parts fit in bed shape, and warn if they don't
// TODO: use actual toolpaths instead of total bounding box
Polygon bed_polygon{ scale(this->_print.config.bed_shape.values) };
if (!diff(this->_print.bounding_box().polygon(), bed_polygon).empty()) {
std::cout << "Warning: the supplied parts might not fit in the configured bed shape. "
<< "You might want to review the result before printing." << std::endl;
}
this->_print.status_cb = nullptr;
}
}

View File

@ -0,0 +1,29 @@
#ifndef slic3r_SimplePrint_hpp_
#define slic3r_SimplePrint_hpp_
#include "libslic3r.h"
#include "Point.hpp"
#include "Print.hpp"
namespace Slic3r {
class SimplePrint {
public:
bool center{true};
std::function<void(int, const std::string&)> status_cb {nullptr};
bool apply_config(config_ptr config) { return this->_print.apply_config(config); }
bool apply_config(DynamicPrintConfig config) { return this->_print.apply_config(config); }
double total_used_filament() const { return this->_print.total_used_filament; }
double total_extruded_volume() const { return this->_print.total_extruded_volume; }
void set_model(const Model &model);
void export_gcode(std::string outfile);
private:
Model _model;
Print _print;
};
}
#endif

View File

@ -147,22 +147,22 @@ TriangleMesh::ReadSTLFile(const std::string &input_file) {
}
void
TriangleMesh::write_ascii(const std::string &output_file)
TriangleMesh::write_ascii(const std::string &output_file) const
{
#ifdef BOOST_WINDOWS
stl_write_ascii(&this->stl, boost::nowide::widen(output_file).c_str(), "");
stl_write_ascii(const_cast<stl_file*>(&this->stl), boost::nowide::widen(output_file).c_str(), "");
#else
stl_write_ascii(&this->stl, output_file.c_str(), "");
stl_write_ascii(const_cast<stl_file*>(&this->stl), output_file.c_str(), "");
#endif
}
void
TriangleMesh::write_binary(const std::string &output_file)
TriangleMesh::write_binary(const std::string &output_file) const
{
#ifdef BOOST_WINDOWS
stl_write_binary(&this->stl, boost::nowide::widen(output_file).c_str(), "");
stl_write_binary(const_cast<stl_file*>(&this->stl), boost::nowide::widen(output_file).c_str(), "");
#else
stl_write_binary(&this->stl, output_file.c_str(), "");
stl_write_binary(const_cast<stl_file*>(&this->stl), output_file.c_str(), "");
#endif
}
@ -272,13 +272,13 @@ TriangleMesh::facets_count() const
}
void
TriangleMesh::WriteOBJFile(const std::string &output_file) {
stl_generate_shared_vertices(&stl);
TriangleMesh::WriteOBJFile(const std::string &output_file) const {
stl_generate_shared_vertices(const_cast<stl_file*>(&this->stl));
#ifdef BOOST_WINDOWS
stl_write_obj(&stl, boost::nowide::widen(output_file).c_str());
stl_write_obj(const_cast<stl_file*>(&this->stl), boost::nowide::widen(output_file).c_str());
#else
stl_write_obj(&stl, output_file.c_str());
stl_write_obj(const_cast<stl_file*>(&this->stl), output_file.c_str());
#endif
}

View File

@ -55,13 +55,13 @@ class TriangleMesh
void swap(TriangleMesh &other);
~TriangleMesh();
void ReadSTLFile(const std::string &input_file);
void write_ascii(const std::string &output_file);
void write_binary(const std::string &output_file);
void write_ascii(const std::string &output_file) const;
void write_binary(const std::string &output_file) const;
void repair();
void check_topology();
float volume();
bool is_manifold() const;
void WriteOBJFile(const std::string &output_file);
void WriteOBJFile(const std::string &output_file) const;
void scale(float factor);
void scale(const Pointf3 &versor);