mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	 027ab4fb02
			
		
	
	
		027ab4fb02
		
	
	
	
	
		
			
			Implemented try/catch blocks for Slic3r exceptions and std::exceptions with GUI error reporting. This is extremely important to report corruption of PrusaSlicer.ini.
		
			
				
	
	
		
			785 lines
		
	
	
	
		
			33 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			785 lines
		
	
	
	
		
			33 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #ifdef WIN32
 | |
|     // Why?
 | |
|     #define _WIN32_WINNT 0x0502
 | |
|     // The standard Windows includes.
 | |
|     #define WIN32_LEAN_AND_MEAN
 | |
|     #define NOMINMAX
 | |
|     #include <Windows.h>
 | |
|     #include <wchar.h>
 | |
|     #ifdef SLIC3R_GUI
 | |
|     extern "C"
 | |
|     {
 | |
|         // Let the NVIDIA and AMD know we want to use their graphics card
 | |
|         // on a dual graphics card system.
 | |
|         __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
 | |
|         __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
 | |
|     }
 | |
|     #endif /* SLIC3R_GUI */
 | |
| #endif /* WIN32 */
 | |
| 
 | |
| #include <cstdio>
 | |
| #include <string>
 | |
| #include <cstring>
 | |
| #include <iostream>
 | |
| #include <math.h>
 | |
| #include <boost/algorithm/string/predicate.hpp>
 | |
| #include <boost/filesystem.hpp>
 | |
| #include <boost/nowide/args.hpp>
 | |
| #include <boost/nowide/cenv.hpp>
 | |
| #include <boost/nowide/iostream.hpp>
 | |
| #include <boost/nowide/integration/filesystem.hpp>
 | |
| 
 | |
| #include "unix/fhs.hpp"  // Generated by CMake from ../platform/unix/fhs.hpp.in
 | |
| 
 | |
| #include "libslic3r/libslic3r.h"
 | |
| #include "libslic3r/Config.hpp"
 | |
| #include "libslic3r/Geometry.hpp"
 | |
| #include "libslic3r/Model.hpp"
 | |
| #include "libslic3r/ModelArrange.hpp"
 | |
| #include "libslic3r/Print.hpp"
 | |
| #include "libslic3r/SLAPrint.hpp"
 | |
| #include "libslic3r/TriangleMesh.hpp"
 | |
| #include "libslic3r/Format/AMF.hpp"
 | |
| #include "libslic3r/Format/3mf.hpp"
 | |
| #include "libslic3r/Format/STL.hpp"
 | |
| #include "libslic3r/Format/OBJ.hpp"
 | |
| #include "libslic3r/Format/SL1.hpp"
 | |
| #include "libslic3r/Utils.hpp"
 | |
| #include "libslic3r/Thread.hpp"
 | |
| 
 | |
| #include "PrusaSlicer.hpp"
 | |
| 
 | |
| #ifdef SLIC3R_GUI
 | |
|     #include "slic3r/GUI/GUI_Init.hpp"
 | |
| #endif /* SLIC3R_GUI */
 | |
| 
 | |
| using namespace Slic3r;
 | |
| 
 | |
| int CLI::run(int argc, char **argv)
 | |
| {
 | |
|     // Mark the main thread for the debugger and for runtime checks.
 | |
|     set_current_thread_name("slic3r_main");
 | |
| 
 | |
| #ifdef __WXGTK__
 | |
|     // On Linux, wxGTK has no support for Wayland, and the app crashes on
 | |
|     // startup if gtk3 is used. This env var has to be set explicitly to
 | |
|     // instruct the window manager to fall back to X server mode.
 | |
|     ::setenv("GDK_BACKEND", "x11", /* replace */ true);
 | |
| #endif
 | |
| 
 | |
| 	// Switch boost::filesystem to utf8.
 | |
|     try {
 | |
|         boost::nowide::nowide_filesystem();
 | |
|     } catch (const std::runtime_error& ex) {
 | |
|         std::string caption = std::string(SLIC3R_APP_NAME) + " Error";
 | |
|         std::string text = std::string("An error occured while setting up locale.\n") + (
 | |
| #if !defined(_WIN32) && !defined(__APPLE__)
 | |
|         	// likely some linux system
 | |
|         	"You may need to reconfigure the missing locales, likely by running the \"locale-gen\" and \"dpkg-reconfigure locales\" commands.\n"
 | |
| #endif
 | |
|         	SLIC3R_APP_NAME " will now terminate.\n\n") + ex.what();
 | |
|     #if defined(_WIN32) && defined(SLIC3R_GUI)
 | |
|         if (m_actions.empty())
 | |
|         	// Empty actions means Slicer is executed in the GUI mode. Show a GUI message.
 | |
|             MessageBoxA(NULL, text.c_str(), caption.c_str(), MB_OK | MB_ICONERROR);
 | |
|     #endif
 | |
|         boost::nowide::cerr << text.c_str() << std::endl;
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
| 	if (! this->setup(argc, argv))
 | |
| 		return 1;
 | |
| 
 | |
|     m_extra_config.apply(m_config, true);
 | |
|     m_extra_config.normalize_fdm();
 | |
|     
 | |
|     PrinterTechnology printer_technology = Slic3r::printer_technology(m_config);
 | |
| 
 | |
|     bool							start_gui			= m_actions.empty() &&
 | |
|         // cutting transformations are setting an "export" action.
 | |
|         std::find(m_transforms.begin(), m_transforms.end(), "cut") == m_transforms.end() &&
 | |
|         std::find(m_transforms.begin(), m_transforms.end(), "cut_x") == m_transforms.end() &&
 | |
|         std::find(m_transforms.begin(), m_transforms.end(), "cut_y") == m_transforms.end();
 | |
|     bool 							start_as_gcodeviewer =
 | |
| #ifdef _WIN32
 | |
|             false;
 | |
| #else
 | |
|             // On Unix systems, the prusa-slicer binary may be symlinked to give the application a different meaning.
 | |
|             boost::algorithm::iends_with(boost::filesystem::path(argv[0]).filename().string(), "gcodeviewer");
 | |
| #endif // _WIN32
 | |
| 
 | |
|     const std::vector<std::string> &load_configs		= m_config.option<ConfigOptionStrings>("load", true)->values;
 | |
| 
 | |
|     // load config files supplied via --load
 | |
|     for (auto const &file : load_configs) {
 | |
|         if (! boost::filesystem::exists(file)) {
 | |
|             if (m_config.opt_bool("ignore_nonexistent_config")) {
 | |
|                 continue;
 | |
|             } else {
 | |
|                 boost::nowide::cerr << "No such file: " << file << std::endl;
 | |
|                 return 1;
 | |
|             }
 | |
|         }
 | |
|         DynamicPrintConfig config;
 | |
|         try {
 | |
|             config.load(file);
 | |
|         } catch (std::exception &ex) {
 | |
|             boost::nowide::cerr << "Error while reading config file: " << ex.what() << std::endl;
 | |
|             return 1;
 | |
|         }
 | |
|         config.normalize_fdm();
 | |
|         PrinterTechnology other_printer_technology = Slic3r::printer_technology(config);
 | |
|         if (printer_technology == ptUnknown) {
 | |
|             printer_technology = other_printer_technology;
 | |
|         } else if (printer_technology != other_printer_technology && other_printer_technology != ptUnknown) {
 | |
|             boost::nowide::cerr << "Mixing configurations for FFF and SLA technologies" << std::endl;
 | |
|             return 1;
 | |
|         }
 | |
|         m_print_config.apply(config);
 | |
|     }
 | |
| 
 | |
| #if ENABLE_GCODE_VIEWER
 | |
|     // are we starting as gcodeviewer ?
 | |
|     for (auto it = m_actions.begin(); it != m_actions.end(); ++it) {
 | |
|         if (*it == "gcodeviewer") {
 | |
|             start_gui = true;
 | |
|             start_as_gcodeviewer = true;
 | |
|             m_actions.erase(it);
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| #endif // ENABLE_GCODE_VIEWER
 | |
| 
 | |
|     // Read input file(s) if any.
 | |
| #if ENABLE_GCODE_VIEWER
 | |
|     for (const std::string& file : m_input_files) {
 | |
|         std::string ext = boost::filesystem::path(file).extension().string();
 | |
|         if (ext == ".gcode" || ext == ".g") {
 | |
|             if (boost::filesystem::exists(file)) {
 | |
|                 start_as_gcodeviewer = true;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     if (!start_as_gcodeviewer) {
 | |
| #endif // ENABLE_GCODE_VIEWER
 | |
|         for (const std::string& file : m_input_files) {
 | |
|             if (!boost::filesystem::exists(file)) {
 | |
|                 boost::nowide::cerr << "No such file: " << file << std::endl;
 | |
|                 exit(1);
 | |
|             }
 | |
|             Model model;
 | |
|             try {
 | |
|                 // When loading an AMF or 3MF, config is imported as well, including the printer technology.
 | |
|                 DynamicPrintConfig config;
 | |
|                 model = Model::read_from_file(file, &config, true);
 | |
|                 PrinterTechnology other_printer_technology = Slic3r::printer_technology(config);
 | |
|                 if (printer_technology == ptUnknown) {
 | |
|                     printer_technology = other_printer_technology;
 | |
|                 }
 | |
|                 else if (printer_technology != other_printer_technology && other_printer_technology != ptUnknown) {
 | |
|                     boost::nowide::cerr << "Mixing configurations for FFF and SLA technologies" << std::endl;
 | |
|                     return 1;
 | |
|                 }
 | |
|                 // config is applied to m_print_config before the current m_config values.
 | |
|                 config += std::move(m_print_config);
 | |
|                 m_print_config = std::move(config);
 | |
|             }
 | |
|             catch (std::exception& e) {
 | |
|                 boost::nowide::cerr << file << ": " << e.what() << std::endl;
 | |
|                 return 1;
 | |
|             }
 | |
|             if (model.objects.empty()) {
 | |
|                 boost::nowide::cerr << "Error: file is empty: " << file << std::endl;
 | |
|                 continue;
 | |
|             }
 | |
|             m_models.push_back(model);
 | |
|         }
 | |
| #if ENABLE_GCODE_VIEWER
 | |
|     }
 | |
| #endif // ENABLE_GCODE_VIEWER
 | |
| 
 | |
|     // Apply command line options to a more specific DynamicPrintConfig which provides normalize()
 | |
|     // (command line options override --load files)
 | |
|     m_print_config.apply(m_extra_config, true);
 | |
|     // Normalizing after importing the 3MFs / AMFs
 | |
|     m_print_config.normalize_fdm();
 | |
| 
 | |
|     // Initialize full print configs for both the FFF and SLA technologies.
 | |
|     FullPrintConfig    fff_print_config;
 | |
|     SLAFullPrintConfig sla_print_config;
 | |
|     
 | |
|     // Synchronize the default parameters and the ones received on the command line.
 | |
|     if (printer_technology == ptFFF) {
 | |
|         fff_print_config.apply(m_print_config, true);
 | |
|         m_print_config.apply(fff_print_config, true);
 | |
|     } else if (printer_technology == ptSLA) {
 | |
|         // The default value has to be different from the one in fff mode.
 | |
|         sla_print_config.printer_technology.value = ptSLA;
 | |
|         sla_print_config.output_filename_format.value = "[input_filename_base].sl1";
 | |
|         
 | |
|         // The default bed shape should reflect the default display parameters
 | |
|         // and not the fff defaults.
 | |
|         double w = sla_print_config.display_width.getFloat();
 | |
|         double h = sla_print_config.display_height.getFloat();
 | |
|         sla_print_config.bed_shape.values = { Vec2d(0, 0), Vec2d(w, 0), Vec2d(w, h), Vec2d(0, h) };
 | |
|         
 | |
|         sla_print_config.apply(m_print_config, true);
 | |
|         m_print_config.apply(sla_print_config, true);
 | |
|     }
 | |
|     
 | |
|     std::string validity = m_print_config.validate();
 | |
|     if (!validity.empty()) {
 | |
|         boost::nowide::cerr << "error: " << validity << std::endl;
 | |
|         return 1;
 | |
|     }
 | |
|     
 | |
|     // Loop through transform options.
 | |
|     bool user_center_specified = false;
 | |
|     Points bed = get_bed_shape(m_print_config);
 | |
|     ArrangeParams arrange_cfg;
 | |
|     arrange_cfg.min_obj_distance = scaled(min_object_distance(m_print_config));
 | |
|     
 | |
|     for (auto const &opt_key : m_transforms) {
 | |
|         if (opt_key == "merge") {
 | |
|             Model m;
 | |
|             for (auto &model : m_models)
 | |
|                 for (ModelObject *o : model.objects)
 | |
|                     m.add_object(*o);
 | |
|             // Rearrange instances unless --dont-arrange is supplied
 | |
|             if (! m_config.opt_bool("dont_arrange")) {
 | |
|                 m.add_default_instances();
 | |
|                 if (this->has_print_action())
 | |
|                     arrange_objects(m, bed, arrange_cfg);
 | |
|                 else
 | |
|                     arrange_objects(m, InfiniteBed{}, arrange_cfg);
 | |
|             }
 | |
|             m_models.clear();
 | |
|             m_models.emplace_back(std::move(m));
 | |
|         } else if (opt_key == "duplicate") {
 | |
|             for (auto &model : m_models) {
 | |
|                 const bool all_objects_have_instances = std::none_of(
 | |
|                     model.objects.begin(), model.objects.end(),
 | |
|                     [](ModelObject* o){ return o->instances.empty(); }
 | |
|                 );
 | |
|                 
 | |
|                 int dups = m_config.opt_int("duplicate");
 | |
|                 if (!all_objects_have_instances) model.add_default_instances();
 | |
|                 
 | |
|                 try {
 | |
|                     if (dups > 1) {
 | |
|                         // if all input objects have defined position(s) apply duplication to the whole model
 | |
|                         duplicate(model, size_t(dups), bed, arrange_cfg);
 | |
|                     } else {
 | |
|                         arrange_objects(model, bed, arrange_cfg);
 | |
|                     }
 | |
|                 } catch (std::exception &ex) {
 | |
|                     boost::nowide::cerr << "error: " << ex.what() << std::endl;
 | |
|                     return 1;
 | |
|                 }
 | |
|             }
 | |
|         } else if (opt_key == "duplicate_grid") {
 | |
|             std::vector<int> &ints = m_config.option<ConfigOptionInts>("duplicate_grid")->values;
 | |
|             const int x = ints.size() > 0 ? ints.at(0) : 1;
 | |
|             const int y = ints.size() > 1 ? ints.at(1) : 1;
 | |
|             const double distance = fff_print_config.duplicate_distance.value;
 | |
|             for (auto &model : m_models)
 | |
|                 model.duplicate_objects_grid(x, y, (distance > 0) ? distance : 6);  // TODO: this is not the right place for setting a default
 | |
|         } else if (opt_key == "center") {
 | |
|         	user_center_specified = true;
 | |
|             for (auto &model : m_models) {
 | |
|                 model.add_default_instances();
 | |
|                 // this affects instances:
 | |
|                 model.center_instances_around_point(m_config.option<ConfigOptionPoint>("center")->value);
 | |
|                 // this affects volumes:
 | |
|                 //FIXME Vojtech: Who knows why the complete model should be aligned with Z as a single rigid body?
 | |
|                 //model.align_to_ground();
 | |
|                 BoundingBoxf3 bbox;
 | |
|                 for (ModelObject *model_object : model.objects)
 | |
|                     // We are interested into the Z span only, therefore it is sufficient to measure the bounding box of the 1st instance only.
 | |
|                     bbox.merge(model_object->instance_bounding_box(0, false));
 | |
|                 for (ModelObject *model_object : model.objects)
 | |
|                     for (ModelInstance *model_instance : model_object->instances)
 | |
|                         model_instance->set_offset(Z, model_instance->get_offset(Z) - bbox.min.z());
 | |
|             }
 | |
|         } else if (opt_key == "align_xy") {
 | |
|             const Vec2d &p = m_config.option<ConfigOptionPoint>("align_xy")->value;
 | |
|             for (auto &model : m_models) {
 | |
|                 BoundingBoxf3 bb = model.bounding_box();
 | |
|                 // this affects volumes:
 | |
|                 model.translate(-(bb.min.x() - p.x()), -(bb.min.y() - p.y()), -bb.min.z());
 | |
|             }
 | |
|         } else if (opt_key == "dont_arrange") {
 | |
|             // do nothing - this option alters other transform options
 | |
|         } else if (opt_key == "rotate") {
 | |
|             for (auto &model : m_models)
 | |
|                 for (auto &o : model.objects)
 | |
|                     // this affects volumes:
 | |
|                     o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), Z);
 | |
|         } else if (opt_key == "rotate_x") {
 | |
|             for (auto &model : m_models)
 | |
|                 for (auto &o : model.objects)
 | |
|                     // this affects volumes:
 | |
|                     o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), X);
 | |
|         } else if (opt_key == "rotate_y") {
 | |
|             for (auto &model : m_models)
 | |
|                 for (auto &o : model.objects)
 | |
|                     // this affects volumes:
 | |
|                     o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), Y);
 | |
|         } else if (opt_key == "scale") {
 | |
|             for (auto &model : m_models)
 | |
|                 for (auto &o : model.objects)
 | |
|                     // this affects volumes:
 | |
|                     o->scale(m_config.get_abs_value(opt_key, 1));
 | |
|         } else if (opt_key == "scale_to_fit") {
 | |
|             const Vec3d &opt = m_config.opt<ConfigOptionPoint3>(opt_key)->value;
 | |
|             if (opt.x() <= 0 || opt.y() <= 0 || opt.z() <= 0) {
 | |
|                 boost::nowide::cerr << "--scale-to-fit requires a positive volume" << std::endl;
 | |
|                 return 1;
 | |
|             }
 | |
|             for (auto &model : m_models)
 | |
|                 for (auto &o : model.objects)
 | |
|                     // this affects volumes:
 | |
|                     o->scale_to_fit(opt);
 | |
|         } else if (opt_key == "cut" || opt_key == "cut_x" || opt_key == "cut_y") {
 | |
|             std::vector<Model> new_models;
 | |
|             for (auto &model : m_models) {
 | |
|                 model.translate(0, 0, -model.bounding_box().min.z());  // align to z = 0
 | |
|                 size_t num_objects = model.objects.size();
 | |
|                 for (size_t i = 0; i < num_objects; ++ i) {
 | |
| 
 | |
| #if 0
 | |
|                     if (opt_key == "cut_x") {
 | |
|                         o->cut(X, m_config.opt_float("cut_x"), &out);
 | |
|                     } else if (opt_key == "cut_y") {
 | |
|                         o->cut(Y, m_config.opt_float("cut_y"), &out);
 | |
|                     } else if (opt_key == "cut") {
 | |
|                         o->cut(Z, m_config.opt_float("cut"), &out);
 | |
|                     }
 | |
| #else
 | |
|                     model.objects.front()->cut(0, m_config.opt_float("cut"), true, true, true);
 | |
| #endif
 | |
|                     model.delete_object(size_t(0));
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // TODO: copy less stuff around using pointers
 | |
|             m_models = new_models;
 | |
| 
 | |
|             if (m_actions.empty())
 | |
|                 m_actions.push_back("export_stl");
 | |
|         }
 | |
| #if 0
 | |
|         else if (opt_key == "cut_grid") {
 | |
|             std::vector<Model> new_models;
 | |
|             for (auto &model : m_models) {
 | |
|                 TriangleMesh mesh = model.mesh();
 | |
|                 mesh.repair();
 | |
| 
 | |
|                 TriangleMeshPtrs meshes = mesh.cut_by_grid(m_config.option<ConfigOptionPoint>("cut_grid")->value);
 | |
|                 size_t i = 0;
 | |
|                 for (TriangleMesh* m : meshes) {
 | |
|                     Model out;
 | |
|                     auto o = out.add_object();
 | |
|                     o->add_volume(*m);
 | |
|                     o->input_file += "_" + std::to_string(i++);
 | |
|                     delete m;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // TODO: copy less stuff around using pointers
 | |
|             m_models = new_models;
 | |
| 
 | |
|             if (m_actions.empty())
 | |
|                 m_actions.push_back("export_stl");
 | |
|         }
 | |
| #endif
 | |
|         else if (opt_key == "split") {
 | |
|             for (Model &model : m_models) {
 | |
|                 size_t num_objects = model.objects.size();
 | |
|                 for (size_t i = 0; i < num_objects; ++ i) {
 | |
|                     model.objects.front()->split(nullptr);
 | |
|                     model.delete_object(size_t(0));
 | |
|                 }
 | |
|             }
 | |
|         } else if (opt_key == "repair") {
 | |
|             // Models are repaired by default.
 | |
|             //for (auto &model : m_models)
 | |
|             //    model.repair();
 | |
|         } else {
 | |
|             boost::nowide::cerr << "error: option not implemented yet: " << opt_key << std::endl;
 | |
|             return 1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // loop through action options
 | |
|     for (auto const &opt_key : m_actions) {
 | |
|         if (opt_key == "help") {
 | |
|             this->print_help();
 | |
|         } else if (opt_key == "help_fff") {
 | |
|             this->print_help(true, ptFFF);
 | |
|         } else if (opt_key == "help_sla") {
 | |
|             this->print_help(true, ptSLA);
 | |
|         } else if (opt_key == "save") {
 | |
|             //FIXME check for mixing the FFF / SLA parameters.
 | |
|             // or better save fff_print_config vs. sla_print_config
 | |
|             m_print_config.save(m_config.opt_string("save"));
 | |
|         } else if (opt_key == "info") {
 | |
|             // --info works on unrepaired model
 | |
|             for (Model &model : m_models) {
 | |
|                 model.add_default_instances();
 | |
|                 model.print_info();
 | |
|             }
 | |
|         } else if (opt_key == "export_stl") {
 | |
|             for (auto &model : m_models)
 | |
|                 model.add_default_instances();
 | |
|             if (! this->export_models(IO::STL))
 | |
|                 return 1;
 | |
|         } else if (opt_key == "export_obj") {
 | |
|             for (auto &model : m_models)
 | |
|                 model.add_default_instances();
 | |
|             if (! this->export_models(IO::OBJ))
 | |
|                 return 1;
 | |
|         } else if (opt_key == "export_amf") {
 | |
|             if (! this->export_models(IO::AMF))
 | |
|                 return 1;
 | |
|         } else if (opt_key == "export_3mf") {
 | |
|             if (! this->export_models(IO::TMF))
 | |
|                 return 1;
 | |
|         } else if (opt_key == "export_gcode" || opt_key == "export_sla" || opt_key == "slice") {
 | |
|             if (opt_key == "export_gcode" && printer_technology == ptSLA) {
 | |
|                 boost::nowide::cerr << "error: cannot export G-code for an FFF configuration" << std::endl;
 | |
|                 return 1;
 | |
|             } else if (opt_key == "export_sla" && printer_technology == ptFFF) {
 | |
|                 boost::nowide::cerr << "error: cannot export SLA slices for a SLA configuration" << std::endl;
 | |
|                 return 1;
 | |
|             }
 | |
|             // Make a copy of the model if the current action is not the last action, as the model may be
 | |
|             // modified by the centering and such.
 | |
|             Model model_copy;
 | |
|             bool  make_copy = &opt_key != &m_actions.back();
 | |
|             for (Model &model_in : m_models) {
 | |
|                 if (make_copy)
 | |
|                     model_copy = model_in;
 | |
|                 Model &model = make_copy ? model_copy : model_in;
 | |
|                 // If all objects have defined instances, their relative positions will be
 | |
|                 // honored when printing (they will be only centered, unless --dont-arrange
 | |
|                 // is supplied); if any object has no instances, it will get a default one
 | |
|                 // and all instances will be rearranged (unless --dont-arrange is supplied).
 | |
|                 std::string outfile = m_config.opt_string("output");
 | |
|                 Print       fff_print;
 | |
|                 SLAPrint    sla_print;
 | |
|                 SL1Archive  sla_archive(sla_print.printer_config());
 | |
|                 sla_print.set_printer(&sla_archive);
 | |
|                 sla_print.set_status_callback(
 | |
|                             [](const PrintBase::SlicingStatus& s)
 | |
|                 {
 | |
|                     if(s.percent >= 0) // FIXME: is this sufficient?
 | |
|                         printf("%3d%s %s\n", s.percent, "% =>", s.text.c_str());
 | |
|                 });
 | |
| 
 | |
|                 PrintBase  *print = (printer_technology == ptFFF) ? static_cast<PrintBase*>(&fff_print) : static_cast<PrintBase*>(&sla_print);
 | |
|                 if (! m_config.opt_bool("dont_arrange")) {
 | |
|                     if (user_center_specified) {
 | |
|                         Vec2d c = m_config.option<ConfigOptionPoint>("center")->value;
 | |
|                         arrange_objects(model, InfiniteBed{scaled(c)}, arrange_cfg);
 | |
|                     } else
 | |
|                         arrange_objects(model, bed, arrange_cfg);
 | |
|                 }
 | |
|                 if (printer_technology == ptFFF) {
 | |
|                     for (auto* mo : model.objects)
 | |
|                         fff_print.auto_assign_extruders(mo);
 | |
|                 }
 | |
|                 print->apply(model, m_print_config);
 | |
|                 std::string err = print->validate();
 | |
|                 if (! err.empty()) {
 | |
|                     boost::nowide::cerr << err << std::endl;
 | |
|                     return 1;
 | |
|                 }
 | |
|                 if (print->empty())
 | |
|                     boost::nowide::cout << "Nothing to print for " << outfile << " . Either the print is empty or no object is fully inside the print volume." << std::endl;
 | |
|                 else
 | |
|                     try {
 | |
|                         std::string outfile_final;
 | |
|                         print->process();
 | |
|                         if (printer_technology == ptFFF) {
 | |
|                             // The outfile is processed by a PlaceholderParser.
 | |
| #if ENABLE_GCODE_VIEWER
 | |
|                             outfile = fff_print.export_gcode(outfile, nullptr, nullptr);
 | |
| #else
 | |
|                             outfile = fff_print.export_gcode(outfile, nullptr);
 | |
| #endif // ENABLE_GCODE_VIEWER
 | |
|                             outfile_final = fff_print.print_statistics().finalize_output_path(outfile);
 | |
|                         } else {
 | |
|                             outfile = sla_print.output_filepath(outfile);
 | |
|                             // We need to finalize the filename beforehand because the export function sets the filename inside the zip metadata
 | |
|                             outfile_final = sla_print.print_statistics().finalize_output_path(outfile);
 | |
|                             sla_archive.export_print(outfile_final, sla_print);
 | |
|                         }
 | |
|                         if (outfile != outfile_final) {
 | |
|                             if (Slic3r::rename_file(outfile, outfile_final)) {
 | |
|                                 boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl;
 | |
|                                 return 1;
 | |
|                             }
 | |
|                             outfile = outfile_final;
 | |
|                         }
 | |
|                         boost::nowide::cout << "Slicing result exported to " << outfile << std::endl;
 | |
|                     } catch (const std::exception &ex) {
 | |
|                         boost::nowide::cerr << ex.what() << std::endl;
 | |
|                         return 1;
 | |
|                     }
 | |
| /*
 | |
|                 print.center = ! m_config.has("center")
 | |
|                     && ! m_config.has("align_xy")
 | |
|                     && ! m_config.opt_bool("dont_arrange");
 | |
|                 print.set_model(model);
 | |
| 
 | |
|                 // start chronometer
 | |
|                 typedef std::chrono::high_resolution_clock clock_;
 | |
|                 typedef std::chrono::duration<double, std::ratio<1> > second_;
 | |
|                 std::chrono::time_point<clock_> t0{ clock_::now() };
 | |
| 
 | |
|                 const std::string outfile = this->output_filepath(model, IO::Gcode);
 | |
|                 try {
 | |
|                     print.export_gcode(outfile);
 | |
|                 } catch (std::runtime_error &e) {
 | |
|                     boost::nowide::cerr << e.what() << std::endl;
 | |
|                     return 1;
 | |
|                 }
 | |
|                 boost::nowide::cout << "G-code exported to " << outfile << std::endl;
 | |
| 
 | |
|                 // output some statistics
 | |
|                 double duration { std::chrono::duration_cast<second_>(clock_::now() - t0).count() };
 | |
|                 boost::nowide::cout << std::fixed << std::setprecision(0)
 | |
|                     << "Done. Process took " << (duration/60) << " minutes and "
 | |
|                     << std::setprecision(3)
 | |
|                     << std::fmod(duration, 60.0) << " seconds." << std::endl
 | |
|                     << std::setprecision(2)
 | |
|                     << "Filament required: " << print.total_used_filament() << "mm"
 | |
|                     << " (" << print.total_extruded_volume()/1000 << "cm3)" << std::endl;
 | |
| */
 | |
|             }
 | |
| #if !ENABLE_GCODE_VIEWER
 | |
|         } else if (opt_key == "gcodeviewer") {
 | |
|             start_gui = true;
 | |
|         	start_as_gcodeviewer = true;
 | |
| #endif // !ENABLE_GCODE_VIEWER
 | |
|         } else {
 | |
|             boost::nowide::cerr << "error: option not supported yet: " << opt_key << std::endl;
 | |
|             return 1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (start_gui) {
 | |
| #ifdef SLIC3R_GUI
 | |
|         Slic3r::GUI::GUI_InitParams params;
 | |
|         params.argc = argc;
 | |
|         params.argv = argv;
 | |
|         params.load_configs = load_configs;
 | |
|         params.extra_config = std::move(m_extra_config);
 | |
|         params.input_files  = std::move(m_input_files);
 | |
|         params.start_as_gcodeviewer = start_as_gcodeviewer;
 | |
|         return Slic3r::GUI::GUI_Run(params);
 | |
| #else // SLIC3R_GUI
 | |
|         // No GUI support. Just print out a help.
 | |
|         this->print_help(false);
 | |
|         // If started without a parameter, consider it to be OK, otherwise report an error code (no action etc).
 | |
|         return (argc == 0) ? 0 : 1;
 | |
| #endif // SLIC3R_GUI
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| bool CLI::setup(int argc, char **argv)
 | |
| {
 | |
|     {
 | |
| 	    Slic3r::set_logging_level(1);
 | |
|         const char *loglevel = boost::nowide::getenv("SLIC3R_LOGLEVEL");
 | |
|         if (loglevel != nullptr) {
 | |
|             if (loglevel[0] >= '0' && loglevel[0] <= '9' && loglevel[1] == 0)
 | |
|                 set_logging_level(loglevel[0] - '0');
 | |
|             else
 | |
|                 boost::nowide::cerr << "Invalid SLIC3R_LOGLEVEL environment variable: " << loglevel << std::endl;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     boost::filesystem::path path_to_binary = boost::filesystem::system_complete(argv[0]);
 | |
| 
 | |
|     // Path from the Slic3r binary to its resources.
 | |
| #ifdef __APPLE__
 | |
|     // The application is packed in the .dmg archive as 'Slic3r.app/Contents/MacOS/Slic3r'
 | |
|     // The resources are packed to 'Slic3r.app/Contents/Resources'
 | |
|     boost::filesystem::path path_resources = boost::filesystem::canonical(path_to_binary).parent_path() / "../Resources";
 | |
| #elif defined _WIN32
 | |
|     // The application is packed in the .zip archive in the root,
 | |
|     // The resources are packed to 'resources'
 | |
|     // Path from Slic3r binary to resources:
 | |
|     boost::filesystem::path path_resources = path_to_binary.parent_path() / "resources";
 | |
| #elif defined SLIC3R_FHS
 | |
|     // The application is packaged according to the Linux Filesystem Hierarchy Standard
 | |
|     // Resources are set to the 'Architecture-independent (shared) data', typically /usr/share or /usr/local/share
 | |
|     boost::filesystem::path path_resources = SLIC3R_FHS_RESOURCES;
 | |
| #else
 | |
|     // The application is packed in the .tar.bz archive (or in AppImage) as 'bin/slic3r',
 | |
|     // The resources are packed to 'resources'
 | |
|     // Path from Slic3r binary to resources:
 | |
|     boost::filesystem::path path_resources = boost::filesystem::canonical(path_to_binary).parent_path() / "../resources";
 | |
| #endif
 | |
| 
 | |
|     set_resources_dir(path_resources.string());
 | |
|     set_var_dir((path_resources / "icons").string());
 | |
|     set_local_dir((path_resources / "localization").string());
 | |
| 
 | |
|     // Parse all command line options into a DynamicConfig.
 | |
|     // If any option is unsupported, print usage and abort immediately.
 | |
|     t_config_option_keys opt_order;
 | |
|     if (! m_config.read_cli(argc, argv, &m_input_files, &opt_order)) {
 | |
|         // Separate error message reported by the CLI parser from the help.
 | |
|         boost::nowide::cerr << std::endl;
 | |
|         this->print_help();
 | |
|         return false;
 | |
|     }
 | |
|     // Parse actions and transform options.
 | |
|     for (auto const &opt_key : opt_order) {
 | |
|         if (cli_actions_config_def.has(opt_key))
 | |
|             m_actions.emplace_back(opt_key);
 | |
|         else if (cli_transform_config_def.has(opt_key))
 | |
|             m_transforms.emplace_back(opt_key);
 | |
|     }
 | |
| 
 | |
|     {
 | |
|         const ConfigOptionInt *opt_loglevel = m_config.opt<ConfigOptionInt>("loglevel");
 | |
|         if (opt_loglevel != 0)
 | |
|             set_logging_level(opt_loglevel->value);
 | |
|     }
 | |
|     
 | |
|     std::string validity = m_config.validate();
 | |
| 
 | |
|     // Initialize with defaults.
 | |
|     for (const t_optiondef_map *options : { &cli_actions_config_def.options, &cli_transform_config_def.options, &cli_misc_config_def.options })
 | |
|         for (const std::pair<t_config_option_key, ConfigOptionDef> &optdef : *options)
 | |
|             m_config.option(optdef.first, true);
 | |
| 
 | |
|     set_data_dir(m_config.opt_string("datadir"));
 | |
|     
 | |
|     if (!validity.empty()) {
 | |
|         boost::nowide::cerr << "error: " << validity << std::endl;
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| void CLI::print_help(bool include_print_options, PrinterTechnology printer_technology) const
 | |
| {
 | |
|     boost::nowide::cout
 | |
|         << SLIC3R_BUILD_ID << " " << "based on Slic3r"
 | |
| #ifdef SLIC3R_GUI
 | |
|         << " (with GUI support)"
 | |
| #else /* SLIC3R_GUI */
 | |
|         << " (without GUI support)"
 | |
| #endif /* SLIC3R_GUI */
 | |
|         << std::endl
 | |
|         << "https://github.com/prusa3d/PrusaSlicer" << std::endl << std::endl
 | |
|         << "Usage: prusa-slicer [ ACTIONS ] [ TRANSFORM ] [ OPTIONS ] [ file.stl ... ]" << std::endl
 | |
|         << std::endl
 | |
|         << "Actions:" << std::endl;
 | |
|     cli_actions_config_def.print_cli_help(boost::nowide::cout, false);
 | |
| 
 | |
|     boost::nowide::cout
 | |
|         << std::endl
 | |
|         << "Transform options:" << std::endl;
 | |
|         cli_transform_config_def.print_cli_help(boost::nowide::cout, false);
 | |
| 
 | |
|     boost::nowide::cout
 | |
|         << std::endl
 | |
|         << "Other options:" << std::endl;
 | |
|         cli_misc_config_def.print_cli_help(boost::nowide::cout, false);
 | |
| 
 | |
|     boost::nowide::cout
 | |
|         << std::endl
 | |
|         << "Print options are processed in the following order:" << std::endl
 | |
|         << "\t1) Config keys from the command line, for example --fill-pattern=stars" << std::endl
 | |
|         << "\t   (highest priority, overwrites everything below)" << std::endl
 | |
|         << "\t2) Config files loaded with --load" << std::endl
 | |
| 	    << "\t3) Config values loaded from amf or 3mf files" << std::endl;
 | |
| 
 | |
|     if (include_print_options) {
 | |
|         boost::nowide::cout << std::endl;
 | |
|         print_config_def.print_cli_help(boost::nowide::cout, true, [printer_technology](const ConfigOptionDef &def)
 | |
|             { return printer_technology == ptAny || def.printer_technology == ptAny || printer_technology == def.printer_technology; });
 | |
|     } else {
 | |
|         boost::nowide::cout
 | |
|             << std::endl
 | |
|             << "Run --help-fff / --help-sla to see the full listing of print options." << std::endl;
 | |
|     }
 | |
| }
 | |
| 
 | |
| bool CLI::export_models(IO::ExportFormat format)
 | |
| {
 | |
|     for (Model &model : m_models) {
 | |
|         const std::string path = this->output_filepath(model, format);
 | |
|         bool success = false;
 | |
|         switch (format) {
 | |
|             case IO::AMF: success = Slic3r::store_amf(path.c_str(), &model, nullptr, false); break;
 | |
|             case IO::OBJ: success = Slic3r::store_obj(path.c_str(), &model);          break;
 | |
|             case IO::STL: success = Slic3r::store_stl(path.c_str(), &model, true);    break;
 | |
|             case IO::TMF: success = Slic3r::store_3mf(path.c_str(), &model, nullptr, false); break;
 | |
|             default: assert(false); break;
 | |
|         }
 | |
|         if (success)
 | |
|             std::cout << "File exported to " << path << std::endl;
 | |
|         else {
 | |
|             std::cerr << "File export to " << path << " failed" << std::endl;
 | |
|             return false;
 | |
|         }
 | |
|     }
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| std::string CLI::output_filepath(const Model &model, IO::ExportFormat format) const
 | |
| {
 | |
|     std::string ext;
 | |
|     switch (format) {
 | |
|         case IO::AMF: ext = ".zip.amf"; break;
 | |
|         case IO::OBJ: ext = ".obj"; break;
 | |
|         case IO::STL: ext = ".stl"; break;
 | |
|         case IO::TMF: ext = ".3mf"; break;
 | |
|         default: assert(false); break;
 | |
|     };
 | |
|     auto proposed_path = boost::filesystem::path(model.propose_export_file_name_and_path(ext));
 | |
|     // use --output when available
 | |
|     std::string cmdline_param = m_config.opt_string("output");
 | |
|     if (! cmdline_param.empty()) {
 | |
|         // if we were supplied a directory, use it and append our automatically generated filename
 | |
|         boost::filesystem::path cmdline_path(cmdline_param);
 | |
|         if (boost::filesystem::is_directory(cmdline_path))
 | |
|             proposed_path = cmdline_path / proposed_path.filename();
 | |
|         else
 | |
|             proposed_path = cmdline_path;
 | |
|     }
 | |
|     return proposed_path.string();
 | |
| }
 | |
| 
 | |
| #if defined(_MSC_VER) || defined(__MINGW32__)
 | |
| extern "C" {
 | |
|     __declspec(dllexport) int __stdcall slic3r_main(int argc, wchar_t **argv)
 | |
|     {
 | |
|         // Convert wchar_t arguments to UTF8.
 | |
|         std::vector<std::string> 	argv_narrow;
 | |
|         std::vector<char*>			argv_ptrs(argc + 1, nullptr);
 | |
|         for (size_t i = 0; i < argc; ++ i)
 | |
|             argv_narrow.emplace_back(boost::nowide::narrow(argv[i]));
 | |
|         for (size_t i = 0; i < argc; ++ i)
 | |
|             argv_ptrs[i] = argv_narrow[i].data();
 | |
|         // Call the UTF8 main.
 | |
|         return CLI().run(argc, argv_ptrs.data());
 | |
|     }
 | |
| }
 | |
| #else /* _MSC_VER */
 | |
| int main(int argc, char **argv)
 | |
| {
 | |
|     return CLI().run(argc, argv);
 | |
| }
 | |
| #endif /* _MSC_VER */
 |