diff options
Diffstat (limited to 'src/PrusaSlicer.cpp')
-rw-r--r-- | src/PrusaSlicer.cpp | 251 |
1 files changed, 153 insertions, 98 deletions
diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 048aea886..60f3a1321 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -22,6 +22,7 @@ #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> @@ -33,7 +34,9 @@ #include "libslic3r/libslic3r.h" #include "libslic3r/Config.hpp" #include "libslic3r/Geometry.hpp" +#include "libslic3r/GCode/PostProcessor.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/ModelArrange.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/SLAPrint.hpp" #include "libslic3r/TriangleMesh.hpp" @@ -41,26 +44,30 @@ #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.hpp" - #include "slic3r/GUI/GUI_App.hpp" - #include "slic3r/GUI/3DScene.hpp" + #include "slic3r/GUI/GUI_Init.hpp" #endif /* SLIC3R_GUI */ using namespace Slic3r; -PrinterTechnology get_printer_technology(const DynamicConfig &config) -{ - const ConfigOptionEnum<PrinterTechnology> *opt = config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology"); - return (opt == nullptr) ? ptUnknown : opt->value; -} - 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(); @@ -85,14 +92,23 @@ int CLI::run(int argc, char **argv) return 1; m_extra_config.apply(m_config, true); - m_extra_config.normalize(); + 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(); - PrinterTechnology printer_technology = get_printer_technology(m_extra_config); + 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 @@ -112,8 +128,8 @@ int CLI::run(int argc, char **argv) boost::nowide::cerr << "Error while reading config file: " << ex.what() << std::endl; return 1; } - config.normalize(); - PrinterTechnology other_printer_technology = get_printer_technology(config); + 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) { @@ -123,42 +139,62 @@ int CLI::run(int argc, char **argv) m_print_config.apply(config); } + // 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; + } + } + // Read input file(s) if any. - for (const std::string &file : m_input_files) { - if (! boost::filesystem::exists(file)) { - boost::nowide::cerr << "No such file: " << file << std::endl; - exit(1); + for (const std::string& file : m_input_files) + if (is_gcode_file(file) && boost::filesystem::exists(file)) { + start_as_gcodeviewer = true; + break; } - Model model; - try { - // When loading an AMF or 3MF, config is imported as well, including the printer technology. - model = Model::read_from_file(file, &m_print_config, true); - PrinterTechnology other_printer_technology = get_printer_technology(m_print_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; + if (!start_as_gcodeviewer) { + 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; } - } 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; + if (model.objects.empty()) { + boost::nowide::cerr << "Error: file is empty: " << file << std::endl; + continue; + } + m_models.push_back(model); } - m_models.push_back(model); } // 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(); - - if (printer_technology == ptUnknown) - printer_technology = std::find(m_actions.begin(), m_actions.end(), "export_sla") == m_actions.end() ? ptFFF : ptSLA; + m_print_config.normalize_fdm(); // Initialize full print configs for both the FFF and SLA technologies. FullPrintConfig fff_print_config; @@ -170,6 +206,7 @@ int CLI::run(int argc, char **argv) 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 @@ -182,8 +219,18 @@ int CLI::run(int argc, char **argv) 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; @@ -193,29 +240,33 @@ int CLI::run(int argc, char **argv) // Rearrange instances unless --dont-arrange is supplied if (! m_config.opt_bool("dont_arrange")) { m.add_default_instances(); - const BoundingBoxf &bb = fff_print_config.bed_shape.values; - m.arrange_objects( - fff_print_config.min_object_distance(), - // If we are going to use the merged model for printing, honor - // the configured print bed for arranging, otherwise do it freely. - this->has_print_action() ? &bb : nullptr - ); + 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") { - const BoundingBoxf &bb = fff_print_config.bed_shape.values; 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(); } ); - if (all_objects_have_instances) { - // if all input objects have defined position(s) apply duplication to the whole model - model.duplicate(m_config.opt_int("duplicate"), fff_print_config.min_object_distance(), &bb); - } else { - model.add_default_instances(); - model.duplicate_objects(m_config.opt_int("duplicate"), fff_print_config.min_object_distance(), &bb); + + 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") { @@ -409,7 +460,8 @@ int CLI::run(int argc, char **argv) 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) { @@ -419,15 +471,21 @@ int CLI::run(int argc, char **argv) PrintBase *print = (printer_technology == ptFFF) ? static_cast<PrintBase*>(&fff_print) : static_cast<PrintBase*>(&sla_print); if (! m_config.opt_bool("dont_arrange")) { - //FIXME make the min_object_distance configurable. - model.arrange_objects(fff_print.config().min_object_distance()); - model.center_instances_around_point((! user_center_specified && m_print_config.has("bed_shape")) ? - BoundingBoxf(m_print_config.opt<ConfigOptionPoints>("bed_shape")->values).center() : - m_config.option<ConfigOptionPoint>("center")->value); + 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); + } else { + // The default for "output_filename_format" is good for FDM: "[input_filename_base].gcode" + // Replace it with a reasonable SLA default. + std::string &format = m_print_config.opt_string("output_filename_format", true); + if (format == static_cast<const ConfigOptionString*>(m_print_config.def()->get("output_filename_format")->default_value.get())->value) + format = "[input_filename_base].SL1"; } print->apply(model, m_print_config); std::string err = print->validate(); @@ -443,18 +501,23 @@ int CLI::run(int argc, char **argv) print->process(); if (printer_technology == ptFFF) { // The outfile is processed by a PlaceholderParser. - outfile = fff_print.export_gcode(outfile, nullptr); + outfile = fff_print.export_gcode(outfile, nullptr, nullptr); 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_print.export_raster(outfile_final); + sla_archive.export_print(outfile_final, sla_print); } - if (outfile != outfile_final && Slic3r::rename_file(outfile, outfile_final)) { - boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl; - return 1; + 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; } + // Run the post-processing scripts if defined. + run_post_process_scripts(outfile, fff_print.full_print_config()); boost::nowide::cout << "Slicing result exported to " << outfile << std::endl; } catch (const std::exception &ex) { boost::nowide::cerr << ex.what() << std::endl; @@ -499,43 +562,20 @@ int CLI::run(int argc, char **argv) if (start_gui) { #ifdef SLIC3R_GUI -// #ifdef USE_WX - GUI::GUI_App *gui = new GUI::GUI_App(); -// gui->autosave = m_config.opt_string("autosave"); - GUI::GUI_App::SetInstance(gui); - gui->CallAfter([gui, this, &load_configs] { - if (!gui->initialized()) { - return; - } -#if 0 - // Load the cummulative config over the currently active profiles. - //FIXME if multiple configs are loaded, only the last one will have an effect. - // We need to decide what to do about loading of separate presets (just print preset, just filament preset etc). - // As of now only the full configs are supported here. - if (!m_print_config.empty()) - gui->mainframe->load_config(m_print_config); -#endif - if (! load_configs.empty()) - // Load the last config to give it a name at the UI. The name of the preset may be later - // changed by loading an AMF or 3MF. - //FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config. - gui->mainframe->load_config_file(load_configs.back()); - // If loading a 3MF file, the config is loaded from the last one. - if (! m_input_files.empty()) - gui->plater()->load_files(m_input_files, true, true); - if (! m_extra_config.empty()) - gui->mainframe->load_config(m_extra_config); - }); - int result = wxEntry(argc, argv); - //FIXME this is a workaround for the PrusaSlicer 2.1 release. - _3DScene::destroy(); - return result; -#else /* 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 */ +#endif // SLIC3R_GUI } return 0; @@ -560,7 +600,7 @@ bool CLI::setup(int argc, char **argv) #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 = path_to_binary.parent_path() / "../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' @@ -574,7 +614,7 @@ bool CLI::setup(int argc, char **argv) // 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 = path_to_binary.parent_path() / "../resources"; + boost::filesystem::path path_resources = boost::filesystem::canonical(path_to_binary).parent_path() / "../resources"; #endif set_resources_dir(path_resources.string()); @@ -603,6 +643,8 @@ bool CLI::setup(int argc, char **argv) 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 }) @@ -610,6 +652,11 @@ bool CLI::setup(int argc, char **argv) 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; } @@ -640,6 +687,14 @@ void CLI::print_help(bool include_print_options, PrinterTechnology printer_techn << "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) |