mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-15 19:15:54 +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/PrintObject.cpp
|
||||
${LIBDIR}/libslic3r/PrintRegion.cpp
|
||||
${LIBDIR}/libslic3r/SimplePrint.cpp
|
||||
${LIBDIR}/libslic3r/SLAPrint.cpp
|
||||
${LIBDIR}/libslic3r/SlicingAdaptive.cpp
|
||||
${LIBDIR}/libslic3r/Surface.cpp
|
||||
|
485
src/slic3r.cpp
485
src/slic3r.cpp
@ -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
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."));
|
||||
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();
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
} }
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
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
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user