mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-20 01:29:04 +08:00
Redesigned C++ CLI
This commit is contained in:
parent
e4fd0e3200
commit
bb29661a90
@ -212,6 +212,7 @@ add_library(libslic3r STATIC
|
|||||||
${LIBDIR}/libslic3r/PrintConfig.cpp
|
${LIBDIR}/libslic3r/PrintConfig.cpp
|
||||||
${LIBDIR}/libslic3r/PrintObject.cpp
|
${LIBDIR}/libslic3r/PrintObject.cpp
|
||||||
${LIBDIR}/libslic3r/PrintRegion.cpp
|
${LIBDIR}/libslic3r/PrintRegion.cpp
|
||||||
|
${LIBDIR}/libslic3r/SimplePrint.cpp
|
||||||
${LIBDIR}/libslic3r/SLAPrint.cpp
|
${LIBDIR}/libslic3r/SLAPrint.cpp
|
||||||
${LIBDIR}/libslic3r/SlicingAdaptive.cpp
|
${LIBDIR}/libslic3r/SlicingAdaptive.cpp
|
||||||
${LIBDIR}/libslic3r/Surface.cpp
|
${LIBDIR}/libslic3r/Surface.cpp
|
||||||
|
445
src/slic3r.cpp
445
src/slic3r.cpp
@ -1,102 +1,93 @@
|
|||||||
#include "ConfigBase.hpp"
|
#include "slic3r.hpp"
|
||||||
#include "Geometry.hpp"
|
#include "Geometry.hpp"
|
||||||
#include "IO.hpp"
|
#include "IO.hpp"
|
||||||
#include "Model.hpp"
|
|
||||||
#include "SLAPrint.hpp"
|
#include "SLAPrint.hpp"
|
||||||
#include "Print.hpp"
|
#include "Print.hpp"
|
||||||
|
#include "SimplePrint.hpp"
|
||||||
#include "TriangleMesh.hpp"
|
#include "TriangleMesh.hpp"
|
||||||
#include "libslic3r.h"
|
#include "libslic3r.h"
|
||||||
|
#include <cmath>
|
||||||
|
#include <chrono>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/nowide/args.hpp>
|
#include <boost/nowide/args.hpp>
|
||||||
#include <boost/nowide/iostream.hpp>
|
#include <boost/nowide/iostream.hpp>
|
||||||
|
|
||||||
|
|
||||||
#ifdef USE_WX
|
#ifdef USE_WX
|
||||||
#include "GUI/GUI.hpp"
|
#include "GUI/GUI.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// utility function for displaying CLI usage
|
|
||||||
void printUsage();
|
|
||||||
|
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
|
|
||||||
int
|
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).
|
// Convert arguments to UTF-8 (needed on Windows).
|
||||||
// argv then points to memory owned by a.
|
// argv then points to memory owned by a.
|
||||||
boost::nowide::args a(argc, argv);
|
boost::nowide::args a(argc, argv);
|
||||||
|
|
||||||
// parse all command line options into a DynamicConfig
|
// parse all command line options into a DynamicConfig
|
||||||
ConfigDef config_def;
|
t_config_option_keys opt_order;
|
||||||
config_def.merge(cli_config_def);
|
this->config_def.merge(cli_actions_config_def);
|
||||||
config_def.merge(print_config_def);
|
this->config_def.merge(cli_transform_config_def);
|
||||||
DynamicConfig config(&config_def);
|
this->config_def.merge(cli_misc_config_def);
|
||||||
t_config_option_keys input_files;
|
this->config_def.merge(print_config_def);
|
||||||
|
this->config.def = &this->config_def;
|
||||||
|
|
||||||
// if any option is unsupported, print usage and abort immediately
|
// if any option is unsupported, print usage and abort immediately
|
||||||
if ( !config.read_cli(argc, argv, &input_files) )
|
if (!this->config.read_cli(argc, argv, &this->input_files, &opt_order)) {
|
||||||
{
|
this->print_help();
|
||||||
printUsage();
|
return 1;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply command line options to a more handy CLIConfig
|
// TODO: validate this->config (min, max etc.)
|
||||||
CLIConfig cli_config;
|
|
||||||
cli_config.apply(config, true);
|
|
||||||
|
|
||||||
DynamicPrintConfig print_config;
|
// parse actions and transform options
|
||||||
|
for (auto const &opt_key : opt_order) {
|
||||||
#ifdef USE_WX
|
if (cli_actions_config_def.has(opt_key)) this->actions.push_back(opt_key);
|
||||||
if (cli_config.gui) {
|
if (cli_transform_config_def.has(opt_key)) this->transforms.push_back(opt_key);
|
||||||
GUI::App *gui = new GUI::App();
|
|
||||||
|
|
||||||
GUI::App::SetInstance(gui);
|
|
||||||
wxEntry(argc, argv);
|
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
if (cli_config.gui) {
|
|
||||||
std::cout << "GUI support has not been built." << "\n";
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// load config files supplied via --load
|
// 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)) {
|
if (!boost::filesystem::exists(file)) {
|
||||||
boost::nowide::cout << "No such file: " << file << std::endl;
|
if (config.getBool("ignore_nonexistent_file", false)) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
boost::nowide::cerr << "No such file: " << file << std::endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DynamicPrintConfig c;
|
DynamicPrintConfig c;
|
||||||
try {
|
try {
|
||||||
c.load(file);
|
c.load(file);
|
||||||
} catch (std::exception &e) {
|
} 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);
|
exit(1);
|
||||||
}
|
}
|
||||||
c.normalize();
|
c.normalize();
|
||||||
print_config.apply(c);
|
this->print_config.apply(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply command line options to a more specific DynamicPrintConfig which provides normalize()
|
// apply command line options to a more specific DynamicPrintConfig which provides normalize()
|
||||||
// (command line options override --load files)
|
// (command line options override --load files)
|
||||||
print_config.apply(config, true);
|
this->print_config.apply(config, true);
|
||||||
print_config.normalize();
|
this->print_config.normalize();
|
||||||
|
|
||||||
// write config if requested
|
// create a static (full) print config to be used in our logic
|
||||||
if (!cli_config.save.value.empty()) print_config.save(cli_config.save.value);
|
this->full_print_config.apply(this->print_config);
|
||||||
|
|
||||||
// read input file(s) if any
|
// read input file(s) if any
|
||||||
std::vector<Model> models;
|
for (auto const &file : input_files) {
|
||||||
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;
|
Model model;
|
||||||
try {
|
try {
|
||||||
model = Model::read_from_file(file);
|
model = Model::read_from_file(file);
|
||||||
@ -110,143 +101,224 @@ main(int argc, char **argv)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
model.add_default_instances();
|
this->models.push_back(model);
|
||||||
|
|
||||||
// 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
|
// loop through transform options
|
||||||
models.push_back(model);
|
for (auto const &opt_key : this->transforms) {
|
||||||
}
|
if (opt_key == "merge") {
|
||||||
if (cli_config.help) {
|
Model m;
|
||||||
printUsage();
|
for (auto &model : this->models)
|
||||||
return 0;
|
m.merge(model);
|
||||||
}
|
this->models = {m};
|
||||||
|
} else if (opt_key == "duplicate") {
|
||||||
for (Model &model : models) {
|
const BoundingBoxf bb{ this->full_print_config.bed_shape.values };
|
||||||
if (cli_config.info) {
|
for (auto &model : this->models) {
|
||||||
// --info works on unrepaired model
|
const bool all_objects_have_instances = std::none_of(
|
||||||
model.print_info();
|
model.objects.begin(), model.objects.end(),
|
||||||
} else if (cli_config.export_obj) {
|
[](ModelObject* o){ return o->instances.empty(); }
|
||||||
std::string outfile = cli_config.output.value;
|
);
|
||||||
if (outfile.empty()) outfile = model.objects.front()->input_file + ".obj";
|
if (all_objects_have_instances) {
|
||||||
|
// if all input objects have defined position(s) apply duplication to the whole model
|
||||||
TriangleMesh mesh = model.mesh();
|
model.duplicate(this->config.getInt("duplicate"), this->full_print_config.min_object_distance(), &bb);
|
||||||
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);
|
|
||||||
} else {
|
} 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];
|
// add each resulting object as a distinct model
|
||||||
ModelObject &lower = *out.objects[1];
|
Model upper, lower;
|
||||||
|
auto upper_obj = upper.add_object(*out.objects[0]);
|
||||||
|
auto lower_obj = lower.add_object(*out.objects[1]);
|
||||||
|
|
||||||
// Use the input name and trim off the extension.
|
if (upper_obj->facets_count() > 0) new_models.push_back(upper);
|
||||||
std::string outfile = cli_config.output.value;
|
if (lower_obj->facets_count() > 0) new_models.push_back(lower);
|
||||||
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) {
|
// TODO: copy less stuff around using pointers
|
||||||
TriangleMesh m = upper.mesh();
|
this->models = new_models;
|
||||||
IO::STL::write(m, outfile + "_upper.stl");
|
|
||||||
}
|
if (this->actions.empty())
|
||||||
if (lower.facets_count() > 0) {
|
this->actions.push_back("export_stl");
|
||||||
TriangleMesh m = lower.mesh();
|
} else if (opt_key == "cut_grid") {
|
||||||
IO::STL::write(m, outfile + "_lower.stl");
|
std::vector<Model> new_models;
|
||||||
}
|
for (auto &model : this->models) {
|
||||||
}
|
|
||||||
} else if (cli_config.cut_grid.value.x > 0 && cli_config.cut_grid.value.y > 0) {
|
|
||||||
TriangleMesh mesh = model.mesh();
|
TriangleMesh mesh = model.mesh();
|
||||||
mesh.repair();
|
mesh.repair();
|
||||||
|
|
||||||
TriangleMeshPtrs meshes = mesh.cut_by_grid(cli_config.cut_grid.value);
|
TriangleMeshPtrs meshes = mesh.cut_by_grid(config.opt<ConfigOptionPoint>("cut_grid")->value);
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (TriangleMesh* m : meshes) {
|
for (TriangleMesh* m : meshes) {
|
||||||
std::ostringstream ss;
|
Model out;
|
||||||
ss << model.objects.front()->input_file << "_" << i++ << ".stl";
|
auto o = out.add_object();
|
||||||
IO::STL::write(*m, ss.str());
|
o->add_volume(*m);
|
||||||
|
o->input_file += "_" + std::to_string(i++);
|
||||||
delete m;
|
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 {
|
} else {
|
||||||
boost::nowide::cerr << "error: command not supported" << std::endl;
|
boost::nowide::cerr << "error: option not implemented yet: " << opt_key << std::endl;
|
||||||
return 1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
void printUsage()
|
|
||||||
{
|
void
|
||||||
|
CLI::print_help() const {
|
||||||
std::cout << "Slic3r " << SLIC3R_VERSION << " is a STL-to-GCODE translator for RepRap 3D printers" << "\n"
|
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"
|
<< "written by Alessandro Ranellucci & the Slic3r community - https://slic3r.org/ - https://github.com/slic3r/Slic3r" << "\n"
|
||||||
<< "Git Version " << BUILD_COMMIT << "\n\n"
|
<< "Git Version " << BUILD_COMMIT << "\n\n"
|
||||||
<< "Usage (C++ only): ./slic3r [ OPTIONS ] [ file.stl ] [ file2.stl ] ..." << "\n";
|
<< "Usage (C++ only): ./slic3r [ OPTIONS ] [ file.stl ] [ file2.stl ] ..." << "\n";
|
||||||
// CLI Options
|
// CLI Options
|
||||||
@ -258,3 +330,56 @@ void printUsage()
|
|||||||
print_print_options(boost::nowide::cout);
|
print_print_options(boost::nowide::cout);
|
||||||
std::cout << "****\n";
|
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
29
src/slic3r.hpp
Normal 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
|
@ -64,7 +64,6 @@ public:
|
|||||||
if (print_config_def.options.count(opt_key) == 0) throw InvalidOptionType(opt_key + std::string(" is an invalid option."));
|
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();
|
return (dynamic_cast<ConfigOption*>(this->_config.optptr(opt_key, create)))->getFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
int getInt(const t_config_option_key& opt_key, bool create=true) {
|
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."));
|
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();
|
return (dynamic_cast<ConfigOption*>(this->_config.optptr(opt_key, create)))->getInt();
|
||||||
|
@ -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);
|
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
|
void
|
||||||
ConfigBase::setenv_()
|
ConfigBase::setenv_()
|
||||||
{
|
{
|
||||||
@ -451,6 +481,7 @@ DynamicConfig& DynamicConfig::operator= (DynamicConfig other)
|
|||||||
void
|
void
|
||||||
DynamicConfig::swap(DynamicConfig &other)
|
DynamicConfig::swap(DynamicConfig &other)
|
||||||
{
|
{
|
||||||
|
std::swap(this->def, other.def);
|
||||||
std::swap(this->options, other.options);
|
std::swap(this->options, other.options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -541,7 +572,7 @@ DynamicConfig::empty() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
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;
|
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)
|
for (size_t i = 0; i < tokens.size(); ++i)
|
||||||
_argv.push_back(const_cast<char *>(tokens[i].c_str()));
|
_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
|
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
|
// cache the CLI option => opt_key mapping
|
||||||
std::map<std::string,std::string> opts;
|
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.
|
// Store the option value.
|
||||||
const bool existing = this->has(opt_key);
|
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)) {
|
if (ConfigOptionBool* opt = this->opt<ConfigOptionBool>(opt_key, true)) {
|
||||||
opt->value = !no;
|
opt->value = !no;
|
||||||
} else if (ConfigOptionBools* opt = this->opt<ConfigOptionBools>(opt_key, true)) {
|
} else if (ConfigOptionBools* opt = this->opt<ConfigOptionBools>(opt_key, true)) {
|
||||||
|
@ -45,6 +45,7 @@ class ConfigOption {
|
|||||||
virtual void setFloat(double val) {};
|
virtual void setFloat(double val) {};
|
||||||
virtual void setString(std::string val) {};
|
virtual void setString(std::string val) {};
|
||||||
virtual std::string getString() const { return ""; };
|
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);
|
||||||
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(const std::vector<std::string> _values) : ConfigOptionVector<std::string>(_values) {};
|
||||||
ConfigOptionStrings* clone() const { return new ConfigOptionStrings(this->values); };
|
ConfigOptionStrings* clone() const { return new ConfigOptionStrings(this->values); };
|
||||||
|
|
||||||
|
std::vector<std::string> getStrings() const { return this->values; };
|
||||||
|
|
||||||
std::string serialize() const {
|
std::string serialize() const {
|
||||||
return escape_strings_cstyle(this->values);
|
return escape_strings_cstyle(this->values);
|
||||||
};
|
};
|
||||||
@ -387,7 +390,7 @@ class ConfigOptionPoint3 : public ConfigOptionSingle<Pointf3>
|
|||||||
|
|
||||||
bool deserialize(std::string str, bool append = false);
|
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;
|
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);
|
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) const;
|
||||||
double get_abs_value(const t_config_option_key &opt_key, double ratio_over) 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 setenv_();
|
||||||
void load(const std::string &file);
|
void load(const std::string &file);
|
||||||
void save(const std::string &file) const;
|
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 erase(const t_config_option_key &opt_key);
|
||||||
void clear();
|
void clear();
|
||||||
bool empty() const;
|
bool empty() const;
|
||||||
void read_cli(const std::vector<std::string> &tokens, 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);
|
bool read_cli(int argc, char** argv, t_config_option_keys* extra, t_config_option_keys* keys = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
|
typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "IO.hpp"
|
#include "IO.hpp"
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/nowide/fstream.hpp>
|
#include <boost/nowide/fstream.hpp>
|
||||||
@ -10,6 +11,24 @@
|
|||||||
|
|
||||||
namespace Slic3r { namespace IO {
|
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
|
bool
|
||||||
STL::read(std::string input_file, TriangleMesh* mesh)
|
STL::read(std::string input_file, TriangleMesh* mesh)
|
||||||
{
|
{
|
||||||
@ -44,14 +63,14 @@ STL::read(std::string input_file, Model* model)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
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();
|
TriangleMesh mesh = model.mesh();
|
||||||
return STL::write(mesh, output_file, binary);
|
return STL::write(mesh, output_file, binary);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
STL::write(TriangleMesh& mesh, std::string output_file, bool binary)
|
STL::write(const TriangleMesh &mesh, std::string output_file, bool binary)
|
||||||
{
|
{
|
||||||
if (binary) {
|
if (binary) {
|
||||||
mesh.write_binary(output_file);
|
mesh.write_binary(output_file);
|
||||||
@ -134,21 +153,28 @@ OBJ::read(std::string input_file, Model* model)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
OBJ::write(Model& model, std::string output_file)
|
OBJ::write(const Model& model, std::string output_file)
|
||||||
{
|
{
|
||||||
TriangleMesh mesh = model.mesh();
|
TriangleMesh mesh = model.mesh();
|
||||||
return OBJ::write(mesh, output_file);
|
return OBJ::write(mesh, output_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
OBJ::write(TriangleMesh& mesh, std::string output_file)
|
OBJ::write(const TriangleMesh& mesh, std::string output_file)
|
||||||
{
|
{
|
||||||
mesh.WriteOBJFile(output_file);
|
mesh.WriteOBJFile(output_file);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
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;
|
TriangleMesh mesh2 = mesh;
|
||||||
mesh2.center_around_origin();
|
mesh2.center_around_origin();
|
||||||
|
@ -4,17 +4,26 @@
|
|||||||
#include "libslic3r.h"
|
#include "libslic3r.h"
|
||||||
#include "Model.hpp"
|
#include "Model.hpp"
|
||||||
#include "TriangleMesh.hpp"
|
#include "TriangleMesh.hpp"
|
||||||
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace Slic3r { namespace IO {
|
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
|
class STL
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static bool read(std::string input_file, TriangleMesh* mesh);
|
static bool read(std::string input_file, TriangleMesh* mesh);
|
||||||
static bool read(std::string input_file, Model* model);
|
static bool read(std::string input_file, Model* model);
|
||||||
static bool write(Model& model, std::string output_file, bool binary = true);
|
static bool write(const Model &model, std::string output_file) {
|
||||||
static bool write(TriangleMesh& mesh, std::string output_file, bool binary = true);
|
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
|
class OBJ
|
||||||
@ -22,28 +31,29 @@ class OBJ
|
|||||||
public:
|
public:
|
||||||
static bool read(std::string input_file, TriangleMesh* mesh);
|
static bool read(std::string input_file, TriangleMesh* mesh);
|
||||||
static bool read(std::string input_file, Model* model);
|
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);
|
||||||
static bool write(TriangleMesh& mesh, std::string output_file);
|
static bool write(const TriangleMesh& mesh, std::string output_file);
|
||||||
};
|
};
|
||||||
|
|
||||||
class AMF
|
class AMF
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static bool read(std::string input_file, Model* model);
|
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
|
class POV
|
||||||
{
|
{
|
||||||
public:
|
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
|
class TMF
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static bool read(std::string input_file, Model* model);
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
} }
|
} }
|
||||||
|
@ -496,7 +496,7 @@ AMF::read(std::string input_file, Model* model)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
AMF::write(Model& model, std::string output_file)
|
AMF::write(const Model& model, std::string output_file)
|
||||||
{
|
{
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@ -388,9 +388,10 @@ TMFEditor::~TMFEditor(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
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();
|
return tmf_writer.produce_TMF();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,8 +48,11 @@ Model::~Model()
|
|||||||
Model
|
Model
|
||||||
Model::read_from_file(std::string input_file)
|
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")) {
|
if (boost::algorithm::iends_with(input_file, ".stl")) {
|
||||||
IO::STL::read(input_file, &model);
|
IO::STL::read(input_file, &model);
|
||||||
} else if (boost::algorithm::iends_with(input_file, ".obj")) {
|
} else if (boost::algorithm::iends_with(input_file, ".obj")) {
|
||||||
@ -72,6 +75,13 @@ Model::read_from_file(std::string input_file)
|
|||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Model::merge(const Model &other)
|
||||||
|
{
|
||||||
|
for (ModelObject* o : other.objects)
|
||||||
|
this->add_object(*o, true);
|
||||||
|
}
|
||||||
|
|
||||||
ModelObject*
|
ModelObject*
|
||||||
Model::add_object()
|
Model::add_object()
|
||||||
{
|
{
|
||||||
@ -201,6 +211,18 @@ Model::repair()
|
|||||||
(*o)->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
|
void
|
||||||
Model::center_instances_around_point(const Pointf &point)
|
Model::center_instances_around_point(const Pointf &point)
|
||||||
{
|
{
|
||||||
@ -281,11 +303,9 @@ Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
|
|||||||
// get the (transformed) size of each instance so that we take
|
// get the (transformed) size of each instance so that we take
|
||||||
// into account their different transformations when packing
|
// into account their different transformations when packing
|
||||||
Pointfs instance_sizes;
|
Pointfs instance_sizes;
|
||||||
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) {
|
for (const ModelObject* o : this->objects)
|
||||||
for (size_t i = 0; i < (*o)->instances.size(); ++i) {
|
for (size_t i = 0; i < o->instances.size(); ++i)
|
||||||
instance_sizes.push_back((*o)->instance_bounding_box(i).size());
|
instance_sizes.push_back(o->instance_bounding_box(i).size());
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Pointfs positions;
|
Pointfs positions;
|
||||||
if (! this->_arrange(instance_sizes, dist, bb, positions))
|
if (! this->_arrange(instance_sizes, dist, bb, positions))
|
||||||
@ -330,13 +350,12 @@ Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb)
|
|||||||
void
|
void
|
||||||
Model::duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb)
|
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
|
// make a copy of the pointers in order to avoid recursion when appending their copies
|
||||||
ModelInstancePtrs instances = (*o)->instances;
|
const auto instances = o->instances;
|
||||||
for (ModelInstancePtrs::const_iterator i = instances.begin(); i != instances.end(); ++i) {
|
for (const ModelInstance* i : instances)
|
||||||
for (size_t k = 2; k <= copies_num; ++k)
|
for (size_t k = 2; k <= copies_num; ++k)
|
||||||
(*o)->add_instance(**i);
|
o->add_instance(*i);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->arrange_objects(dist, bb);
|
this->arrange_objects(dist, bb);
|
||||||
@ -643,6 +662,14 @@ Model::align_instances_to_origin()
|
|||||||
this->center_instances_around_point(new_center);
|
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
|
void
|
||||||
ModelObject::align_to_ground()
|
ModelObject::align_to_ground()
|
||||||
{
|
{
|
||||||
@ -819,8 +846,16 @@ ModelObject::cut(Axis axis, coordf_t z, Model* model) const
|
|||||||
ModelObject* lower = model->add_object(*this);
|
ModelObject* lower = model->add_object(*this);
|
||||||
upper->clear_volumes();
|
upper->clear_volumes();
|
||||||
lower->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) {
|
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) {
|
||||||
ModelVolume* volume = *v;
|
ModelVolume* volume = *v;
|
||||||
|
@ -74,6 +74,8 @@ class Model
|
|||||||
/// \return Model the read Model
|
/// \return Model the read Model
|
||||||
static Model read_from_file(std::string input_file);
|
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.
|
/// Create a new object and add it to the current Model.
|
||||||
/// \return ModelObject* a pointer to the new Model
|
/// \return ModelObject* a pointer to the new Model
|
||||||
ModelObject* add_object();
|
ModelObject* add_object();
|
||||||
@ -134,12 +136,16 @@ class Model
|
|||||||
/// This function calls repair function on each TriangleMesh of each model object volume
|
/// This function calls repair function on each TriangleMesh of each model object volume
|
||||||
void repair();
|
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.
|
/// 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.
|
/// 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
|
/// \param point pointf object to center the model instances of model objects around
|
||||||
void center_instances_around_point(const Pointf &point);
|
void center_instances_around_point(const Pointf &point);
|
||||||
|
|
||||||
void align_instances_to_origin();
|
void align_instances_to_origin();
|
||||||
|
void align_to_ground();
|
||||||
|
|
||||||
/// Translate each ModelObject with x, y, z units.
|
/// Translate each ModelObject with x, y, z units.
|
||||||
/// \param x coordf_t units in the x direction
|
/// \param x coordf_t units in the x direction
|
||||||
|
@ -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();
|
t_config_option_keys opt_keys = config.keys();
|
||||||
for (t_config_option_keys::const_iterator i = opt_keys.begin(); i != opt_keys.end(); ++i) {
|
for (t_config_option_keys::const_iterator i = opt_keys.begin(); i != opt_keys.end(); ++i) {
|
||||||
|
@ -21,7 +21,7 @@ class PlaceholderParser
|
|||||||
|
|
||||||
PlaceholderParser();
|
PlaceholderParser();
|
||||||
void update_timestamp();
|
void update_timestamp();
|
||||||
void apply_config(const DynamicPrintConfig &config);
|
void apply_config(const DynamicConfig &config);
|
||||||
void apply_env_variables();
|
void apply_env_variables();
|
||||||
void set(const std::string &key, const std::string &value);
|
void set(const std::string &key, const std::string &value);
|
||||||
void set(const std::string &key, int value);
|
void set(const std::string &key, int value);
|
||||||
|
@ -705,10 +705,13 @@ Print::add_model_object(ModelObject* model_object, int idx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifndef SLIC3RXS
|
#ifndef SLIC3RXS
|
||||||
|
|
||||||
void
|
void
|
||||||
Print::export_gcode(std::ostream& output, bool quiet)
|
Print::export_gcode(std::ostream& output, bool quiet)
|
||||||
{
|
{
|
||||||
|
// prerequisites
|
||||||
this->process();
|
this->process();
|
||||||
|
|
||||||
if (this->status_cb != nullptr)
|
if (this->status_cb != nullptr)
|
||||||
this->status_cb(90, "Exporting G-Code...");
|
this->status_cb(90, "Exporting G-Code...");
|
||||||
|
|
||||||
@ -718,10 +721,15 @@ Print::export_gcode(std::ostream& output, bool quiet)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
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);
|
std::ofstream outstream(outfile);
|
||||||
this->export_gcode(outstream);
|
this->export_gcode(outstream);
|
||||||
|
|
||||||
|
// TODO: export_gcode() is not ported completely from Perl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -233,7 +233,6 @@ class Print
|
|||||||
PrintObjectPtrs objects;
|
PrintObjectPtrs objects;
|
||||||
PrintRegionPtrs regions;
|
PrintRegionPtrs regions;
|
||||||
PlaceholderParser placeholder_parser;
|
PlaceholderParser placeholder_parser;
|
||||||
// TODO: status_cb
|
|
||||||
#ifndef SLIC3RXS
|
#ifndef SLIC3RXS
|
||||||
std::function<void(int, const std::string&)> status_cb {nullptr};
|
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);
|
void export_gcode(std::ostream& output, bool quiet = false);
|
||||||
|
|
||||||
/// Performs a gcode export and then runs post-processing scripts (if any)
|
/// 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
|
/// commands a gcode export to a temporary file and return its name
|
||||||
std::string export_gcode(bool quiet = false);
|
std::string export_gcode(bool quiet = false);
|
||||||
|
@ -1892,10 +1892,89 @@ PrintConfigBase::_handle_legacy(t_config_option_key &opt_key, std::string &value
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CLIConfigDef::CLIConfigDef()
|
CLIActionsConfigDef::CLIActionsConfigDef()
|
||||||
{
|
{
|
||||||
ConfigOptionDef* def;
|
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 = this->add("cut", coFloat);
|
||||||
def->label = __TRANS("Cut");
|
def->label = __TRANS("Cut");
|
||||||
def->tooltip = __TRANS("Cut model at the given Z.");
|
def->tooltip = __TRANS("Cut model at the given Z.");
|
||||||
@ -1920,113 +1999,102 @@ CLIConfigDef::CLIConfigDef()
|
|||||||
def->cli = "cut-y";
|
def->cli = "cut-y";
|
||||||
def->default_value = new ConfigOptionFloat(0);
|
def->default_value = new ConfigOptionFloat(0);
|
||||||
|
|
||||||
def = this->add("export_obj", coBool);
|
def = this->add("center", coPoint);
|
||||||
def->label = __TRANS("Export SVG");
|
def->label = __TRANS("Center");
|
||||||
def->tooltip = __TRANS("Export the model as OBJ.");
|
def->tooltip = __TRANS("Center the print around the given center.");
|
||||||
def->cli = "export-obj";
|
def->cli = "center";
|
||||||
def->default_value = new ConfigOptionBool(false);
|
def->default_value = new ConfigOptionPoint(Pointf(100,100));
|
||||||
|
|
||||||
def = this->add("export_pov", coBool);
|
def = this->add("dont_arrange", coBool);
|
||||||
def->label = __TRANS("Export POV");
|
def->label = __TRANS("Don't arrange");
|
||||||
def->tooltip = __TRANS("Export the model as POV-Ray definition.");
|
def->tooltip = __TRANS("Do not rearrange the given models before merging and keep their original XY coordinates.");
|
||||||
def->cli = "export-pov";
|
def->cli = "dont-arrange";
|
||||||
def->default_value = new ConfigOptionBool(false);
|
|
||||||
|
|
||||||
def = this->add("export_svg", coBool);
|
def = this->add("duplicate", coInt);
|
||||||
def->label = __TRANS("Export SVG");
|
def->label = __TRANS("Duplicate");
|
||||||
def->tooltip = __TRANS("Slice the model and export slices as SVG.");
|
def->tooltip =__TRANS("Multiply copies by this factor.");
|
||||||
def->cli = "export-svg";
|
def->cli = "duplicate=i";
|
||||||
def->default_value = new ConfigOptionBool(false);
|
def->min = 1;
|
||||||
|
|
||||||
def = this->add("export_3mf", coBool);
|
def = this->add("duplicate_grid", coInts);
|
||||||
def->label = __TRANS("Export 3MF");
|
def->label = __TRANS("Duplicate by grid");
|
||||||
def->tooltip = __TRANS("Slice the model and export slices as 3MF.");
|
def->tooltip = __TRANS("Multiply copies by creating a grid.");
|
||||||
def->cli = "export-3mf";
|
def->cli = "duplicate-grid=i@";
|
||||||
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("load", coStrings);
|
def = this->add("load", coStrings);
|
||||||
def->label = __TRANS("Load config file");
|
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->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->cli = "load";
|
||||||
def->default_value = new ConfigOptionStrings();
|
|
||||||
|
|
||||||
def = this->add("output", coString);
|
def = this->add("merge", coBool);
|
||||||
def->label = __TRANS("Output File");
|
def->label = __TRANS("Merge");
|
||||||
def->tooltip = __TRANS("The file where the output will be written (if not specified, it will be based on the input file).");
|
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 = "output";
|
def->cli = "merge|m";
|
||||||
def->default_value = new ConfigOptionString("");
|
|
||||||
|
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 = this->add("rotate", coFloat);
|
||||||
def->label = __TRANS("Rotate");
|
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->cli = "rotate";
|
||||||
def->default_value = new ConfigOptionFloat(0);
|
def->default_value = new ConfigOptionFloat(0);
|
||||||
|
|
||||||
def = this->add("rotate_x", coFloat);
|
def = this->add("rotate_x", coFloat);
|
||||||
def->label = __TRANS("Rotate around X");
|
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->cli = "rotate-x";
|
||||||
def->default_value = new ConfigOptionFloat(0);
|
def->default_value = new ConfigOptionFloat(0);
|
||||||
|
|
||||||
def = this->add("rotate_y", coFloat);
|
def = this->add("rotate_y", coFloat);
|
||||||
def->label = __TRANS("Rotate around Y");
|
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->cli = "rotate-y";
|
||||||
def->default_value = new ConfigOptionFloat(0);
|
def->default_value = new ConfigOptionFloat(0);
|
||||||
|
|
||||||
def = this->add("save", coString);
|
def = this->add("scale", coFloatOrPercent);
|
||||||
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->label = __TRANS("Scale");
|
def->label = __TRANS("Scale");
|
||||||
def->tooltip = __TRANS("Scaling factor (default: 1).");
|
def->tooltip = __TRANS("Scaling factor or percentage.");
|
||||||
def->cli = "scale";
|
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 = this->add("scale_to_fit", coPoint3);
|
||||||
def->label = __TRANS("Scale to Fit");
|
def->label = __TRANS("Scale to Fit");
|
||||||
def->tooltip = __TRANS("Scale to fit the given volume.");
|
def->tooltip = __TRANS("Scale to fit the given volume.");
|
||||||
def->cli = "scale-to-fit";
|
def->cli = "scale-to-fit";
|
||||||
def->default_value = new ConfigOptionPoint3(Pointf3(0,0,0));
|
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&
|
std::ostream&
|
||||||
print_cli_options(std::ostream& out) {
|
print_cli_options(std::ostream& out) {
|
||||||
|
/*
|
||||||
for (const auto& opt : cli_config_def.options) {
|
for (const auto& opt : cli_config_def.options) {
|
||||||
if (opt.second.cli.size() != 0) {
|
if (opt.second.cli.size() != 0) {
|
||||||
out << "\t" << std::left << std::setw(40) << std::string("--") + opt.second.cli;
|
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;
|
std::cerr << std::endl;
|
||||||
|
*/
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,78 +639,33 @@ class SLAPrintConfig
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
class CLIConfigDef : public ConfigDef
|
class CLIActionsConfigDef : public ConfigDef
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CLIConfigDef();
|
CLIActionsConfigDef();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const CLIConfigDef cli_config_def;
|
class CLITransformConfigDef : public ConfigDef
|
||||||
|
|
||||||
class CLIConfig
|
|
||||||
: public virtual ConfigBase, public StaticConfig
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ConfigOptionFloat cut;
|
CLITransformConfigDef();
|
||||||
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;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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.
|
/// Iterate through all of the print options and write them to a stream.
|
||||||
std::ostream& print_print_options(std::ostream& out);
|
std::ostream& print_print_options(std::ostream& out);
|
||||||
/// Iterate through all of the CLI options and write them to a stream.
|
/// Iterate through all of the CLI options and write them to a stream.
|
||||||
std::ostream&
|
std::ostream& print_cli_options(std::ostream& out);
|
||||||
print_cli_options(std::ostream& out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -38,12 +38,12 @@ class SLAPrint
|
|||||||
};
|
};
|
||||||
std::vector<SupportPillar> sm_pillars;
|
std::vector<SupportPillar> sm_pillars;
|
||||||
|
|
||||||
SLAPrint(Model* _model) : model(_model) {};
|
SLAPrint(const Model* _model) : model(_model) {};
|
||||||
void slice();
|
void slice();
|
||||||
void write_svg(const std::string &outputfile) const;
|
void write_svg(const std::string &outputfile) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Model* model;
|
const Model* model;
|
||||||
BoundingBoxf3 bb;
|
BoundingBoxf3 bb;
|
||||||
|
|
||||||
void _infill_layer(size_t i, const Fill* fill);
|
void _infill_layer(size_t i, const Fill* fill);
|
||||||
|
49
xs/src/libslic3r/SimplePrint.cpp
Normal file
49
xs/src/libslic3r/SimplePrint.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
29
xs/src/libslic3r/SimplePrint.hpp
Normal file
29
xs/src/libslic3r/SimplePrint.hpp
Normal 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
|
@ -147,22 +147,22 @@ TriangleMesh::ReadSTLFile(const std::string &input_file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TriangleMesh::write_ascii(const std::string &output_file)
|
TriangleMesh::write_ascii(const std::string &output_file) const
|
||||||
{
|
{
|
||||||
#ifdef BOOST_WINDOWS
|
#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
|
#else
|
||||||
stl_write_ascii(&this->stl, output_file.c_str(), "");
|
stl_write_ascii(const_cast<stl_file*>(&this->stl), output_file.c_str(), "");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TriangleMesh::write_binary(const std::string &output_file)
|
TriangleMesh::write_binary(const std::string &output_file) const
|
||||||
{
|
{
|
||||||
#ifdef BOOST_WINDOWS
|
#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
|
#else
|
||||||
stl_write_binary(&this->stl, output_file.c_str(), "");
|
stl_write_binary(const_cast<stl_file*>(&this->stl), output_file.c_str(), "");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,13 +272,13 @@ TriangleMesh::facets_count() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TriangleMesh::WriteOBJFile(const std::string &output_file) {
|
TriangleMesh::WriteOBJFile(const std::string &output_file) const {
|
||||||
stl_generate_shared_vertices(&stl);
|
stl_generate_shared_vertices(const_cast<stl_file*>(&this->stl));
|
||||||
|
|
||||||
#ifdef BOOST_WINDOWS
|
#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
|
#else
|
||||||
stl_write_obj(&stl, output_file.c_str());
|
stl_write_obj(const_cast<stl_file*>(&this->stl), output_file.c_str());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,13 +55,13 @@ class TriangleMesh
|
|||||||
void swap(TriangleMesh &other);
|
void swap(TriangleMesh &other);
|
||||||
~TriangleMesh();
|
~TriangleMesh();
|
||||||
void ReadSTLFile(const std::string &input_file);
|
void ReadSTLFile(const std::string &input_file);
|
||||||
void write_ascii(const std::string &output_file);
|
void write_ascii(const std::string &output_file) const;
|
||||||
void write_binary(const std::string &output_file);
|
void write_binary(const std::string &output_file) const;
|
||||||
void repair();
|
void repair();
|
||||||
void check_topology();
|
void check_topology();
|
||||||
float volume();
|
float volume();
|
||||||
bool is_manifold() const;
|
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(float factor);
|
||||||
void scale(const Pointf3 &versor);
|
void scale(const Pointf3 &versor);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user