PrusaSlicer.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. #ifdef WIN32
  2. // Why?
  3. #define _WIN32_WINNT 0x0502
  4. // The standard Windows includes.
  5. #define WIN32_LEAN_AND_MEAN
  6. #define NOMINMAX
  7. #include <Windows.h>
  8. #include <wchar.h>
  9. #ifdef SLIC3R_GUI
  10. // Let the NVIDIA and AMD know we want to use their graphics card
  11. // on a dual graphics card system.
  12. __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
  13. __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
  14. #endif /* SLIC3R_GUI */
  15. #endif /* WIN32 */
  16. #include <cstdio>
  17. #include <string>
  18. #include <cstring>
  19. #include <iostream>
  20. #include <math.h>
  21. #include <boost/filesystem.hpp>
  22. #include <boost/nowide/args.hpp>
  23. #include <boost/nowide/cenv.hpp>
  24. #include <boost/nowide/iostream.hpp>
  25. #include "unix/fhs.hpp" // Generated by CMake from ../platform/unix/fhs.hpp.in
  26. #include "libslic3r/libslic3r.h"
  27. #include "libslic3r/Config.hpp"
  28. #include "libslic3r/Geometry.hpp"
  29. #include "libslic3r/Model.hpp"
  30. #include "libslic3r/Print.hpp"
  31. #include "libslic3r/SLAPrint.hpp"
  32. #include "libslic3r/TriangleMesh.hpp"
  33. #include "libslic3r/Format/AMF.hpp"
  34. #include "libslic3r/Format/3mf.hpp"
  35. #include "libslic3r/Format/STL.hpp"
  36. #include "libslic3r/Format/OBJ.hpp"
  37. #include "libslic3r/Utils.hpp"
  38. #include "slic3r.hpp"
  39. #ifdef SLIC3R_GUI
  40. #include "slic3r/GUI/GUI.hpp"
  41. #include "slic3r/GUI/GUI_App.hpp"
  42. #endif /* SLIC3R_GUI */
  43. using namespace Slic3r;
  44. PrinterTechnology get_printer_technology(const DynamicConfig &config)
  45. {
  46. const ConfigOptionEnum<PrinterTechnology> *opt = config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology");
  47. return (opt == nullptr) ? ptUnknown : opt->value;
  48. }
  49. int CLI::run(int argc, char **argv)
  50. {
  51. if (! this->setup(argc, argv))
  52. return 1;
  53. m_extra_config.apply(m_config, true);
  54. m_extra_config.normalize();
  55. bool start_gui = m_actions.empty() &&
  56. // cutting transformations are setting an "export" action.
  57. std::find(m_transforms.begin(), m_transforms.end(), "cut") == m_transforms.end() &&
  58. std::find(m_transforms.begin(), m_transforms.end(), "cut_x") == m_transforms.end() &&
  59. std::find(m_transforms.begin(), m_transforms.end(), "cut_y") == m_transforms.end();
  60. PrinterTechnology printer_technology = get_printer_technology(m_extra_config);
  61. const std::vector<std::string> &load_configs = m_config.option<ConfigOptionStrings>("load", true)->values;
  62. // load config files supplied via --load
  63. for (auto const &file : load_configs) {
  64. if (! boost::filesystem::exists(file)) {
  65. if (m_config.opt_bool("ignore_nonexistent_config")) {
  66. continue;
  67. } else {
  68. boost::nowide::cerr << "No such file: " << file << std::endl;
  69. return 1;
  70. }
  71. }
  72. DynamicPrintConfig config;
  73. try {
  74. config.load(file);
  75. } catch (std::exception &ex) {
  76. boost::nowide::cerr << "Error while reading config file: " << ex.what() << std::endl;
  77. return 1;
  78. }
  79. config.normalize();
  80. PrinterTechnology other_printer_technology = get_printer_technology(config);
  81. if (printer_technology == ptUnknown) {
  82. printer_technology = other_printer_technology;
  83. } else if (printer_technology != other_printer_technology) {
  84. boost::nowide::cerr << "Mixing configurations for FFF and SLA technologies" << std::endl;
  85. return 1;
  86. }
  87. m_print_config.apply(config);
  88. }
  89. // Read input file(s) if any.
  90. for (const std::string &file : m_input_files) {
  91. if (! boost::filesystem::exists(file)) {
  92. boost::nowide::cerr << "No such file: " << file << std::endl;
  93. exit(1);
  94. }
  95. Model model;
  96. try {
  97. // When loading an AMF or 3MF, config is imported as well, including the printer technology.
  98. model = Model::read_from_file(file, &m_print_config, true);
  99. PrinterTechnology other_printer_technology = get_printer_technology(m_print_config);
  100. if (printer_technology == ptUnknown) {
  101. printer_technology = other_printer_technology;
  102. } else if (printer_technology != other_printer_technology) {
  103. boost::nowide::cerr << "Mixing configurations for FFF and SLA technologies" << std::endl;
  104. return 1;
  105. }
  106. } catch (std::exception &e) {
  107. boost::nowide::cerr << file << ": " << e.what() << std::endl;
  108. return 1;
  109. }
  110. if (model.objects.empty()) {
  111. boost::nowide::cerr << "Error: file is empty: " << file << std::endl;
  112. continue;
  113. }
  114. m_models.push_back(model);
  115. }
  116. // Apply command line options to a more specific DynamicPrintConfig which provides normalize()
  117. // (command line options override --load files)
  118. m_print_config.apply(m_extra_config, true);
  119. // Normalizing after importing the 3MFs / AMFs
  120. m_print_config.normalize();
  121. if (printer_technology == ptUnknown)
  122. printer_technology = std::find(m_actions.begin(), m_actions.end(), "export_sla") == m_actions.end() ? ptFFF : ptSLA;
  123. // Initialize full print configs for both the FFF and SLA technologies.
  124. FullPrintConfig fff_print_config;
  125. // SLAFullPrintConfig sla_print_config;
  126. fff_print_config.apply(m_print_config, true);
  127. // sla_print_config.apply(m_print_config, true);
  128. // Loop through transform options.
  129. for (auto const &opt_key : m_transforms) {
  130. if (opt_key == "merge") {
  131. Model m;
  132. for (auto &model : m_models)
  133. for (ModelObject *o : model.objects)
  134. m.add_object(*o);
  135. // Rearrange instances unless --dont-arrange is supplied
  136. if (! m_config.opt_bool("dont_arrange")) {
  137. m.add_default_instances();
  138. const BoundingBoxf &bb = fff_print_config.bed_shape.values;
  139. m.arrange_objects(
  140. fff_print_config.min_object_distance(),
  141. // If we are going to use the merged model for printing, honor
  142. // the configured print bed for arranging, otherwise do it freely.
  143. this->has_print_action() ? &bb : nullptr
  144. );
  145. }
  146. m_models.clear();
  147. m_models.emplace_back(std::move(m));
  148. } else if (opt_key == "duplicate") {
  149. const BoundingBoxf &bb = fff_print_config.bed_shape.values;
  150. for (auto &model : m_models) {
  151. const bool all_objects_have_instances = std::none_of(
  152. model.objects.begin(), model.objects.end(),
  153. [](ModelObject* o){ return o->instances.empty(); }
  154. );
  155. if (all_objects_have_instances) {
  156. // if all input objects have defined position(s) apply duplication to the whole model
  157. model.duplicate(m_config.opt_int("duplicate"), fff_print_config.min_object_distance(), &bb);
  158. } else {
  159. model.add_default_instances();
  160. model.duplicate_objects(m_config.opt_int("duplicate"), fff_print_config.min_object_distance(), &bb);
  161. }
  162. }
  163. } else if (opt_key == "duplicate_grid") {
  164. std::vector<int> &ints = m_config.option<ConfigOptionInts>("duplicate_grid")->values;
  165. const int x = ints.size() > 0 ? ints.at(0) : 1;
  166. const int y = ints.size() > 1 ? ints.at(1) : 1;
  167. const double distance = fff_print_config.duplicate_distance.value;
  168. for (auto &model : m_models)
  169. model.duplicate_objects_grid(x, y, (distance > 0) ? distance : 6); // TODO: this is not the right place for setting a default
  170. } else if (opt_key == "center") {
  171. for (auto &model : m_models) {
  172. model.add_default_instances();
  173. // this affects instances:
  174. model.center_instances_around_point(m_config.option<ConfigOptionPoint>("center")->value);
  175. // this affects volumes:
  176. //FIXME Vojtech: Who knows why the complete model should be aligned with Z as a single rigid body?
  177. //model.align_to_ground();
  178. BoundingBoxf3 bbox;
  179. for (ModelObject *model_object : model.objects)
  180. // We are interested into the Z span only, therefore it is sufficient to measure the bounding box of the 1st instance only.
  181. bbox.merge(model_object->instance_bounding_box(0, false));
  182. for (ModelObject *model_object : model.objects)
  183. for (ModelInstance *model_instance : model_object->instances)
  184. model_instance->set_offset(Z, model_instance->get_offset(Z) - bbox.min.z());
  185. }
  186. } else if (opt_key == "align_xy") {
  187. const Vec2d &p = m_config.option<ConfigOptionPoint>("align_xy")->value;
  188. for (auto &model : m_models) {
  189. BoundingBoxf3 bb = model.bounding_box();
  190. // this affects volumes:
  191. model.translate(-(bb.min.x() - p.x()), -(bb.min.y() - p.y()), -bb.min.z());
  192. }
  193. } else if (opt_key == "dont_arrange") {
  194. // do nothing - this option alters other transform options
  195. } else if (opt_key == "rotate") {
  196. for (auto &model : m_models)
  197. for (auto &o : model.objects)
  198. // this affects volumes:
  199. o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), Z);
  200. } else if (opt_key == "rotate_x") {
  201. for (auto &model : m_models)
  202. for (auto &o : model.objects)
  203. // this affects volumes:
  204. o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), X);
  205. } else if (opt_key == "rotate_y") {
  206. for (auto &model : m_models)
  207. for (auto &o : model.objects)
  208. // this affects volumes:
  209. o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), Y);
  210. } else if (opt_key == "scale") {
  211. for (auto &model : m_models)
  212. for (auto &o : model.objects)
  213. // this affects volumes:
  214. o->scale(m_config.get_abs_value(opt_key, 1));
  215. } else if (opt_key == "scale_to_fit") {
  216. const Vec3d &opt = m_config.opt<ConfigOptionPoint3>(opt_key)->value;
  217. if (opt.x() <= 0 || opt.y() <= 0 || opt.z() <= 0) {
  218. boost::nowide::cerr << "--scale-to-fit requires a positive volume" << std::endl;
  219. return 1;
  220. }
  221. for (auto &model : m_models)
  222. for (auto &o : model.objects)
  223. // this affects volumes:
  224. o->scale_to_fit(opt);
  225. } else if (opt_key == "cut" || opt_key == "cut_x" || opt_key == "cut_y") {
  226. std::vector<Model> new_models;
  227. for (auto &model : m_models) {
  228. model.repair();
  229. model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0
  230. size_t num_objects = model.objects.size();
  231. for (size_t i = 0; i < num_objects; ++ i) {
  232. #if 0
  233. if (opt_key == "cut_x") {
  234. o->cut(X, m_config.opt_float("cut_x"), &out);
  235. } else if (opt_key == "cut_y") {
  236. o->cut(Y, m_config.opt_float("cut_y"), &out);
  237. } else if (opt_key == "cut") {
  238. o->cut(Z, m_config.opt_float("cut"), &out);
  239. }
  240. #else
  241. model.objects.front()->cut(0, m_config.opt_float("cut"), true, true, true);
  242. #endif
  243. model.delete_object(size_t(0));
  244. }
  245. }
  246. // TODO: copy less stuff around using pointers
  247. m_models = new_models;
  248. if (m_actions.empty())
  249. m_actions.push_back("export_stl");
  250. }
  251. #if 0
  252. else if (opt_key == "cut_grid") {
  253. std::vector<Model> new_models;
  254. for (auto &model : m_models) {
  255. TriangleMesh mesh = model.mesh();
  256. mesh.repair();
  257. TriangleMeshPtrs meshes = mesh.cut_by_grid(m_config.option<ConfigOptionPoint>("cut_grid")->value);
  258. size_t i = 0;
  259. for (TriangleMesh* m : meshes) {
  260. Model out;
  261. auto o = out.add_object();
  262. o->add_volume(*m);
  263. o->input_file += "_" + std::to_string(i++);
  264. delete m;
  265. }
  266. }
  267. // TODO: copy less stuff around using pointers
  268. m_models = new_models;
  269. if (m_actions.empty())
  270. m_actions.push_back("export_stl");
  271. }
  272. #endif
  273. else if (opt_key == "split") {
  274. for (Model &model : m_models) {
  275. size_t num_objects = model.objects.size();
  276. for (size_t i = 0; i < num_objects; ++ i) {
  277. model.objects.front()->split(nullptr);
  278. model.delete_object(size_t(0));
  279. }
  280. }
  281. } else if (opt_key == "repair") {
  282. for (auto &model : m_models)
  283. model.repair();
  284. } else {
  285. boost::nowide::cerr << "error: option not implemented yet: " << opt_key << std::endl;
  286. return 1;
  287. }
  288. }
  289. // loop through action options
  290. for (auto const &opt_key : m_actions) {
  291. if (opt_key == "help") {
  292. this->print_help();
  293. } else if (opt_key == "help_fff") {
  294. this->print_help(true, ptFFF);
  295. } else if (opt_key == "help_sla") {
  296. this->print_help(true, ptSLA);
  297. } else if (opt_key == "save") {
  298. //FIXME check for mixing the FFF / SLA parameters.
  299. // or better save fff_print_config vs. sla_print_config
  300. m_print_config.save(m_config.opt_string("save"));
  301. } else if (opt_key == "info") {
  302. // --info works on unrepaired model
  303. for (Model &model : m_models) {
  304. model.add_default_instances();
  305. model.print_info();
  306. }
  307. } else if (opt_key == "export_stl") {
  308. for (auto &model : m_models)
  309. model.add_default_instances();
  310. if (! this->export_models(IO::STL))
  311. return 1;
  312. } else if (opt_key == "export_obj") {
  313. for (auto &model : m_models)
  314. model.add_default_instances();
  315. if (! this->export_models(IO::OBJ))
  316. return 1;
  317. } else if (opt_key == "export_amf") {
  318. if (! this->export_models(IO::AMF))
  319. return 1;
  320. } else if (opt_key == "export_3mf") {
  321. if (! this->export_models(IO::TMF))
  322. return 1;
  323. } else if (opt_key == "export_gcode" || opt_key == "export_sla" || opt_key == "slice") {
  324. if (opt_key == "export_gcode" && printer_technology == ptSLA) {
  325. boost::nowide::cerr << "error: cannot export G-code for an FFF configuration" << std::endl;
  326. return 1;
  327. } else if (opt_key == "export_sla" && printer_technology == ptFFF) {
  328. boost::nowide::cerr << "error: cannot export SLA slices for a SLA configuration" << std::endl;
  329. return 1;
  330. }
  331. // Make a copy of the model if the current action is not the last action, as the model may be
  332. // modified by the centering and such.
  333. Model model_copy;
  334. bool make_copy = &opt_key != &m_actions.back();
  335. for (Model &model_in : m_models) {
  336. if (make_copy)
  337. model_copy = model_in;
  338. Model &model = make_copy ? model_copy : model_in;
  339. // If all objects have defined instances, their relative positions will be
  340. // honored when printing (they will be only centered, unless --dont-arrange
  341. // is supplied); if any object has no instances, it will get a default one
  342. // and all instances will be rearranged (unless --dont-arrange is supplied).
  343. std::string outfile = m_config.opt_string("output");
  344. Print fff_print;
  345. SLAPrint sla_print;
  346. sla_print.set_status_callback(
  347. [](const PrintBase::SlicingStatus& s)
  348. {
  349. if(s.percent >= 0) // FIXME: is this sufficient?
  350. printf("%3d%s %s\n", s.percent, "% =>", s.text.c_str());
  351. });
  352. PrintBase *print = (printer_technology == ptFFF) ? static_cast<PrintBase*>(&fff_print) : static_cast<PrintBase*>(&sla_print);
  353. if (! m_config.opt_bool("dont_arrange")) {
  354. //FIXME make the min_object_distance configurable.
  355. model.arrange_objects(fff_print.config().min_object_distance());
  356. model.center_instances_around_point(m_config.option<ConfigOptionPoint>("center")->value);
  357. }
  358. if (printer_technology == ptFFF) {
  359. for (auto* mo : model.objects)
  360. fff_print.auto_assign_extruders(mo);
  361. }
  362. print->apply(model, m_print_config);
  363. std::string err = print->validate();
  364. if (! err.empty()) {
  365. boost::nowide::cerr << err << std::endl;
  366. return 1;
  367. }
  368. if (print->empty())
  369. boost::nowide::cout << "Nothing to print for " << outfile << " . Either the print is empty or no object is fully inside the print volume." << std::endl;
  370. else
  371. try {
  372. std::string outfile_final;
  373. print->process();
  374. if (printer_technology == ptFFF) {
  375. // The outfile is processed by a PlaceholderParser.
  376. outfile = fff_print.export_gcode(outfile, nullptr);
  377. outfile_final = fff_print.print_statistics().finalize_output_path(outfile);
  378. } else {
  379. outfile = sla_print.output_filepath(outfile);
  380. // We need to finalize the filename beforehand because the export function sets the filename inside the zip metadata
  381. outfile_final = sla_print.print_statistics().finalize_output_path(outfile);
  382. sla_print.export_raster(outfile_final);
  383. }
  384. if (outfile != outfile_final && Slic3r::rename_file(outfile, outfile_final) != 0) {
  385. boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl;
  386. return 1;
  387. }
  388. boost::nowide::cout << "Slicing result exported to " << outfile << std::endl;
  389. } catch (const std::exception &ex) {
  390. boost::nowide::cerr << ex.what() << std::endl;
  391. return 1;
  392. }
  393. /*
  394. print.center = ! m_config.has("center")
  395. && ! m_config.has("align_xy")
  396. && ! m_config.opt_bool("dont_arrange");
  397. print.set_model(model);
  398. // start chronometer
  399. typedef std::chrono::high_resolution_clock clock_;
  400. typedef std::chrono::duration<double, std::ratio<1> > second_;
  401. std::chrono::time_point<clock_> t0{ clock_::now() };
  402. const std::string outfile = this->output_filepath(model, IO::Gcode);
  403. try {
  404. print.export_gcode(outfile);
  405. } catch (std::runtime_error &e) {
  406. boost::nowide::cerr << e.what() << std::endl;
  407. return 1;
  408. }
  409. boost::nowide::cout << "G-code exported to " << outfile << std::endl;
  410. // output some statistics
  411. double duration { std::chrono::duration_cast<second_>(clock_::now() - t0).count() };
  412. boost::nowide::cout << std::fixed << std::setprecision(0)
  413. << "Done. Process took " << (duration/60) << " minutes and "
  414. << std::setprecision(3)
  415. << std::fmod(duration, 60.0) << " seconds." << std::endl
  416. << std::setprecision(2)
  417. << "Filament required: " << print.total_used_filament() << "mm"
  418. << " (" << print.total_extruded_volume()/1000 << "cm3)" << std::endl;
  419. */
  420. }
  421. } else {
  422. boost::nowide::cerr << "error: option not supported yet: " << opt_key << std::endl;
  423. return 1;
  424. }
  425. }
  426. if (start_gui) {
  427. #ifdef SLIC3R_GUI
  428. // #ifdef USE_WX
  429. GUI::GUI_App *gui = new GUI::GUI_App();
  430. // gui->autosave = m_config.opt_string("autosave");
  431. GUI::GUI_App::SetInstance(gui);
  432. gui->CallAfter([gui, this, &load_configs] {
  433. if (!gui->initialized()) {
  434. return;
  435. }
  436. #if 0
  437. // Load the cummulative config over the currently active profiles.
  438. //FIXME if multiple configs are loaded, only the last one will have an effect.
  439. // We need to decide what to do about loading of separate presets (just print preset, just filament preset etc).
  440. // As of now only the full configs are supported here.
  441. if (!m_print_config.empty())
  442. gui->mainframe->load_config(m_print_config);
  443. #endif
  444. if (! load_configs.empty())
  445. // Load the last config to give it a name at the UI. The name of the preset may be later
  446. // changed by loading an AMF or 3MF.
  447. //FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config.
  448. gui->mainframe->load_config_file(load_configs.back());
  449. // If loading a 3MF file, the config is loaded from the last one.
  450. if (! m_input_files.empty())
  451. gui->plater()->load_files(m_input_files, true, true);
  452. if (! m_extra_config.empty())
  453. gui->mainframe->load_config(m_extra_config);
  454. });
  455. return wxEntry(argc, argv);
  456. #else /* SLIC3R_GUI */
  457. // No GUI support. Just print out a help.
  458. this->print_help(false);
  459. // If started without a parameter, consider it to be OK, otherwise report an error code (no action etc).
  460. return (argc == 0) ? 0 : 1;
  461. #endif /* SLIC3R_GUI */
  462. }
  463. return 0;
  464. }
  465. bool CLI::setup(int argc, char **argv)
  466. {
  467. {
  468. const char *loglevel = boost::nowide::getenv("SLIC3R_LOGLEVEL");
  469. if (loglevel != nullptr) {
  470. if (loglevel[0] >= '0' && loglevel[0] <= '9' && loglevel[1] == 0)
  471. set_logging_level(loglevel[0] - '0');
  472. else
  473. boost::nowide::cerr << "Invalid SLIC3R_LOGLEVEL environment variable: " << loglevel << std::endl;
  474. }
  475. }
  476. boost::filesystem::path path_to_binary = boost::filesystem::system_complete(argv[0]);
  477. // Path from the Slic3r binary to its resources.
  478. #ifdef __APPLE__
  479. // The application is packed in the .dmg archive as 'Slic3r.app/Contents/MacOS/Slic3r'
  480. // The resources are packed to 'Slic3r.app/Contents/Resources'
  481. boost::filesystem::path path_resources = path_to_binary.parent_path() / "../Resources";
  482. #elif defined _WIN32
  483. // The application is packed in the .zip archive in the root,
  484. // The resources are packed to 'resources'
  485. // Path from Slic3r binary to resources:
  486. boost::filesystem::path path_resources = path_to_binary.parent_path() / "resources";
  487. #elif defined SLIC3R_FHS
  488. // The application is packaged according to the Linux Filesystem Hierarchy Standard
  489. // Resources are set to the 'Architecture-independent (shared) data', typically /usr/share or /usr/local/share
  490. boost::filesystem::path path_resources = SLIC3R_FHS_RESOURCES;
  491. #else
  492. // The application is packed in the .tar.bz archive (or in AppImage) as 'bin/slic3r',
  493. // The resources are packed to 'resources'
  494. // Path from Slic3r binary to resources:
  495. boost::filesystem::path path_resources = path_to_binary.parent_path() / "../resources";
  496. #endif
  497. set_resources_dir(path_resources.string());
  498. set_var_dir((path_resources / "icons").string());
  499. set_local_dir((path_resources / "localization").string());
  500. // Parse all command line options into a DynamicConfig.
  501. // If any option is unsupported, print usage and abort immediately.
  502. t_config_option_keys opt_order;
  503. if (! m_config.read_cli(argc, argv, &m_input_files, &opt_order)) {
  504. // Separate error message reported by the CLI parser from the help.
  505. boost::nowide::cerr << std::endl;
  506. this->print_help();
  507. return false;
  508. }
  509. // Parse actions and transform options.
  510. for (auto const &opt_key : opt_order) {
  511. if (cli_actions_config_def.has(opt_key))
  512. m_actions.emplace_back(opt_key);
  513. else if (cli_transform_config_def.has(opt_key))
  514. m_transforms.emplace_back(opt_key);
  515. }
  516. {
  517. const ConfigOptionInt *opt_loglevel = m_config.opt<ConfigOptionInt>("loglevel");
  518. if (opt_loglevel != 0)
  519. set_logging_level(opt_loglevel->value);
  520. }
  521. // Initialize with defaults.
  522. for (const t_optiondef_map *options : { &cli_actions_config_def.options, &cli_transform_config_def.options, &cli_misc_config_def.options })
  523. for (const std::pair<t_config_option_key, ConfigOptionDef> &optdef : *options)
  524. m_config.optptr(optdef.first, true);
  525. set_data_dir(m_config.opt_string("datadir"));
  526. return true;
  527. }
  528. void CLI::print_help(bool include_print_options, PrinterTechnology printer_technology) const
  529. {
  530. boost::nowide::cout
  531. << SLIC3R_APP_NAME << " " << SLIC3R_BUILD << " " << "based on Slic3r"
  532. #ifdef SLIC3R_GUI
  533. << " (with GUI support)"
  534. #else /* SLIC3R_GUI */
  535. << " (without GUI support)"
  536. #endif /* SLIC3R_GUI */
  537. << std::endl
  538. << "https://github.com/prusa3d/Slic3r" << std::endl << std::endl
  539. << "Usage: slic3r [ ACTIONS ] [ TRANSFORM ] [ OPTIONS ] [ file.stl ... ]" << std::endl
  540. << std::endl
  541. << "Actions:" << std::endl;
  542. cli_actions_config_def.print_cli_help(boost::nowide::cout, false);
  543. boost::nowide::cout
  544. << std::endl
  545. << "Transform options:" << std::endl;
  546. cli_transform_config_def.print_cli_help(boost::nowide::cout, false);
  547. boost::nowide::cout
  548. << std::endl
  549. << "Other options:" << std::endl;
  550. cli_misc_config_def.print_cli_help(boost::nowide::cout, false);
  551. if (include_print_options) {
  552. boost::nowide::cout << std::endl;
  553. print_config_def.print_cli_help(boost::nowide::cout, true, [printer_technology](const ConfigOptionDef &def)
  554. { return printer_technology == ptAny || def.printer_technology == ptAny || printer_technology == def.printer_technology; });
  555. } else {
  556. boost::nowide::cout
  557. << std::endl
  558. << "Run --help-fff / --help-sla to see the full listing of print options." << std::endl;
  559. }
  560. }
  561. bool CLI::export_models(IO::ExportFormat format)
  562. {
  563. for (Model &model : m_models) {
  564. const std::string path = this->output_filepath(model, format);
  565. bool success = false;
  566. switch (format) {
  567. case IO::AMF: success = Slic3r::store_amf(path.c_str(), &model, nullptr); break;
  568. case IO::OBJ: success = Slic3r::store_obj(path.c_str(), &model); break;
  569. case IO::STL: success = Slic3r::store_stl(path.c_str(), &model, true); break;
  570. case IO::TMF: success = Slic3r::store_3mf(path.c_str(), &model, nullptr); break;
  571. default: assert(false); break;
  572. }
  573. if (success)
  574. std::cout << "File exported to " << path << std::endl;
  575. else {
  576. std::cerr << "File export to " << path << " failed" << std::endl;
  577. return false;
  578. }
  579. }
  580. return true;
  581. }
  582. std::string CLI::output_filepath(const Model &model, IO::ExportFormat format) const
  583. {
  584. std::string ext;
  585. switch (format) {
  586. case IO::AMF: ext = ".zip.amf"; break;
  587. case IO::OBJ: ext = ".obj"; break;
  588. case IO::STL: ext = ".stl"; break;
  589. case IO::TMF: ext = ".3mf"; break;
  590. default: assert(false); break;
  591. };
  592. auto proposed_path = boost::filesystem::path(model.propose_export_file_name_and_path(ext));
  593. // use --output when available
  594. std::string cmdline_param = m_config.opt_string("output");
  595. if (! cmdline_param.empty()) {
  596. // if we were supplied a directory, use it and append our automatically generated filename
  597. boost::filesystem::path cmdline_path(cmdline_param);
  598. if (boost::filesystem::is_directory(cmdline_path))
  599. proposed_path = cmdline_path / proposed_path.filename();
  600. else
  601. proposed_path = cmdline_path;
  602. }
  603. return proposed_path.string();
  604. }
  605. #ifdef _MSC_VER
  606. extern "C" {
  607. __declspec(dllexport) int __stdcall slic3r_main(int argc, wchar_t **argv)
  608. {
  609. // Convert wchar_t arguments to UTF8.
  610. std::vector<std::string> argv_narrow;
  611. std::vector<char*> argv_ptrs(argc + 1, nullptr);
  612. for (size_t i = 0; i < argc; ++ i)
  613. argv_narrow.emplace_back(boost::nowide::narrow(argv[i]));
  614. for (size_t i = 0; i < argc; ++ i)
  615. argv_ptrs[i] = const_cast<char*>(argv_narrow[i].data());
  616. // Call the UTF8 main.
  617. return CLI().run(argc, argv_ptrs.data());
  618. }
  619. }
  620. #else /* _MSC_VER */
  621. int main(int argc, char **argv)
  622. {
  623. return CLI().run(argc, argv);
  624. }
  625. #endif /* _MSC_VER */