Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/PrusaSlicer.cpp')
-rw-r--r--src/PrusaSlicer.cpp251
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)