mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-04 12:10:47 +08:00
Merge branch 'ys_spe2598_PrusaSlicer'
This commit is contained in:
commit
6a7318c18a
@ -626,7 +626,6 @@ if(SLIC3R_BUILD_TESTS)
|
||||
endif()
|
||||
|
||||
|
||||
# Resources install target, configure fhs.hpp on UNIX
|
||||
if (WIN32)
|
||||
install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "${CMAKE_INSTALL_PREFIX}/resources")
|
||||
elseif (SLIC3R_FHS)
|
||||
@ -646,10 +645,9 @@ elseif (SLIC3R_FHS)
|
||||
)
|
||||
endforeach()
|
||||
install(DIRECTORY ${SLIC3R_RESOURCES_DIR}/udev/ DESTINATION lib/udev/rules.d)
|
||||
target_compile_definitions(PrusaSlicer PUBLIC SLIC3R_FHS SLIC3R_FHS_RESOURCES="${SLIC3R_FHS_RESOURCES}")
|
||||
else ()
|
||||
install(FILES src/platform/unix/PrusaSlicer.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/resources/applications)
|
||||
install(FILES src/platform/unix/PrusaGcodeviewer.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/resources/applications)
|
||||
install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "${CMAKE_INSTALL_PREFIX}/resources")
|
||||
endif ()
|
||||
|
||||
configure_file(src/platform/unix/fhs.hpp.in ${LIBDIR_BIN}/platform/unix/fhs.hpp)
|
||||
|
73
src/CLI/CLI.hpp
Normal file
73
src/CLI/CLI.hpp
Normal file
@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "CLI_DynamicPrintConfig.hpp"
|
||||
|
||||
#ifdef SLIC3R_GUI
|
||||
#include "slic3r/GUI/GUI_Init.hpp"
|
||||
#endif
|
||||
|
||||
namespace Slic3r::CLI
|
||||
{
|
||||
// struct which is filled from comand line input
|
||||
struct Data
|
||||
{
|
||||
Data();
|
||||
|
||||
CLI_DynamicPrintConfig input_config;
|
||||
CLI_DynamicPrintConfig overrides_config;
|
||||
CLI_DynamicPrintConfig transform_config;
|
||||
CLI_DynamicPrintConfig misc_config;
|
||||
CLI_DynamicPrintConfig actions_config;
|
||||
|
||||
std::vector<std::string> input_files;
|
||||
|
||||
bool empty() {
|
||||
return input_files.empty()
|
||||
&& input_config.empty()
|
||||
&& overrides_config.empty()
|
||||
&& transform_config.empty()
|
||||
&& actions_config.empty();
|
||||
}
|
||||
};
|
||||
|
||||
// Implemented in PrintHelp.cpp
|
||||
|
||||
void print_help(bool include_print_options = false, PrinterTechnology printer_technology = ptAny);
|
||||
|
||||
// Implemented in Setup.cpp
|
||||
|
||||
bool setup(Data& cli, int argc, char** argv);
|
||||
|
||||
// Implemented in LoadPrintData.cpp
|
||||
|
||||
PrinterTechnology get_printer_technology(const DynamicConfig& config);
|
||||
bool load_print_data(std::vector<Model>& models,
|
||||
DynamicPrintConfig& print_config,
|
||||
PrinterTechnology& printer_technology,
|
||||
Data& cli);
|
||||
bool is_needed_post_processing(const DynamicPrintConfig& print_config);
|
||||
|
||||
// Implemented in ProcessTransform.cpp
|
||||
|
||||
bool process_transform(Data& cli, const DynamicPrintConfig& print_config, std::vector<Model>& models);
|
||||
|
||||
// Implemented in ProcessActions.cpp
|
||||
|
||||
bool has_full_config_from_profiles(const Data& cli);
|
||||
bool process_profiles_sharing(const Data& cli);
|
||||
bool process_actions(Data& cli, const DynamicPrintConfig& print_config, std::vector<Model>& models);
|
||||
|
||||
// Implemented in GuiParams.cpp
|
||||
#ifdef SLIC3R_GUI
|
||||
// set data for init GUI parameters
|
||||
// and return state of start_gui
|
||||
bool init_gui_params(GUI::GUI_InitParams& gui_params, int argc, char** argv, Data& cli);
|
||||
int start_gui_with_params(GUI::GUI_InitParams& params);
|
||||
int start_as_gcode_viewer(GUI::GUI_InitParams& gui_params);
|
||||
#endif
|
||||
|
||||
}
|
47
src/CLI/CLI_DynamicPrintConfig.hpp
Normal file
47
src/CLI/CLI_DynamicPrintConfig.hpp
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "libslic3r/Config.hpp"
|
||||
|
||||
namespace Slic3r::CLI
|
||||
{
|
||||
enum class Type
|
||||
{
|
||||
Input,
|
||||
Overrides,
|
||||
Transformations,
|
||||
Misc,
|
||||
Actions,
|
||||
Undef
|
||||
};
|
||||
|
||||
class CLI_DynamicPrintConfig : public DynamicPrintConfig
|
||||
{
|
||||
public:
|
||||
CLI_DynamicPrintConfig() {}
|
||||
CLI_DynamicPrintConfig(Type type, const ConfigDef* config_def) :
|
||||
m_type (type),
|
||||
m_config_def (config_def) {}
|
||||
CLI_DynamicPrintConfig(const CLI_DynamicPrintConfig& other) :
|
||||
DynamicPrintConfig(other),
|
||||
m_type (other.type()),
|
||||
m_config_def (other.def()) {}
|
||||
|
||||
// Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here.
|
||||
const ConfigDef* def() const override { return m_config_def; }
|
||||
|
||||
// Verify whether the opt_key has not been obsoleted or renamed.
|
||||
// Both opt_key and value may be modified by handle_legacy().
|
||||
// If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy().
|
||||
// handle_legacy() is called internally by set_deserialize().
|
||||
void handle_legacy(t_config_option_key& opt_key, std::string& value) const override {
|
||||
if (m_type == Type::Overrides)
|
||||
DynamicPrintConfig::handle_legacy(opt_key, value);
|
||||
}
|
||||
|
||||
Type type() const { return m_type; }
|
||||
|
||||
private:
|
||||
Type m_type { Type::Undef };
|
||||
const ConfigDef* m_config_def { nullptr };
|
||||
};
|
||||
}
|
150
src/CLI/GuiParams.cpp
Normal file
150
src/CLI/GuiParams.cpp
Normal file
@ -0,0 +1,150 @@
|
||||
#include <string>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/nowide/iostream.hpp>
|
||||
#include <boost/nowide/cstdlib.hpp>
|
||||
|
||||
#include "CLI.hpp"
|
||||
|
||||
#ifdef SLIC3R_GUI
|
||||
|
||||
namespace Slic3r::CLI {
|
||||
|
||||
bool init_gui_params(GUI::GUI_InitParams& gui_params, int argc, char** argv, Data& cli)
|
||||
{
|
||||
bool start_gui = false;
|
||||
|
||||
gui_params.argc = argc;
|
||||
gui_params.argv = argv;
|
||||
gui_params.input_files = cli.input_files;
|
||||
|
||||
if (cli.misc_config.has("opengl-aa")) {
|
||||
start_gui = true;
|
||||
gui_params.opengl_aa = true;
|
||||
}
|
||||
#if SLIC3R_OPENGL_ES
|
||||
// are we starting as gcodeviewer ?
|
||||
if (cli.misc_config.has("gcodeviewer")) {
|
||||
cli.start_gui = true;
|
||||
cli.start_as_gcodeviewer = true;
|
||||
}
|
||||
#else
|
||||
|
||||
// search for special keys into command line parameters
|
||||
if (cli.misc_config.has("gcodeviewer")) {
|
||||
start_gui = true;
|
||||
gui_params.start_as_gcodeviewer = true;
|
||||
}
|
||||
else {
|
||||
#ifndef _WIN32
|
||||
// On Unix systems, the prusa-slicer binary may be symlinked to give the application a different meaning.
|
||||
gui_params.start_as_gcodeviewer = boost::algorithm::iends_with(boost::filesystem::path(argv[0]).filename().string(), "gcodeviewer");
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
if (cli.misc_config.has("opengl-version")) {
|
||||
const Semver opengl_minimum = Semver(3, 2, 0);
|
||||
const std::string opengl_version_str = cli.misc_config.opt_string("opengl-version");
|
||||
boost::optional<Semver> semver = Semver::parse(opengl_version_str);
|
||||
if (semver.has_value() && (*semver) >= opengl_minimum) {
|
||||
std::pair<int, int>& version = gui_params.opengl_version;
|
||||
version.first = semver->maj();
|
||||
version.second = semver->min();
|
||||
if (std::find(Slic3r::GUI::OpenGLVersions::core.begin(), Slic3r::GUI::OpenGLVersions::core.end(), std::make_pair(version.first, version.second)) == Slic3r::GUI::OpenGLVersions::core.end()) {
|
||||
version = { 0, 0 };
|
||||
boost::nowide::cerr << "Required OpenGL version " << opengl_version_str << " not recognized.\n Option 'opengl-version' ignored." << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
boost::nowide::cerr << "Required OpenGL version " << opengl_version_str << " is invalid. Must be greater than or equal to " <<
|
||||
opengl_minimum.to_string() << "\n Option 'opengl-version' ignored." << std::endl;
|
||||
start_gui = true;
|
||||
}
|
||||
|
||||
if (cli.misc_config.has("opengl-compatibility")) {
|
||||
start_gui = true;
|
||||
gui_params.opengl_compatibility_profile = true;
|
||||
// reset version as compatibility profile always take the highest version
|
||||
// supported by the graphic card
|
||||
gui_params.opengl_version = std::make_pair(0, 0);
|
||||
}
|
||||
|
||||
if (cli.misc_config.has("opengl-debug")) {
|
||||
start_gui = true;
|
||||
gui_params.opengl_debug = true;
|
||||
}
|
||||
#endif // SLIC3R_OPENGL_ES
|
||||
|
||||
if (cli.misc_config.has("delete-after-load")) {
|
||||
gui_params.delete_after_load = true;
|
||||
}
|
||||
|
||||
if (!gui_params.start_as_gcodeviewer && !cli.input_config.has("load")) {
|
||||
// Read input file(s) if any and check if can start GcodeViewer
|
||||
if (cli.input_files.size() == 1 && is_gcode_file(cli.input_files[0]) && boost::filesystem::exists(cli.input_files[0]))
|
||||
gui_params.start_as_gcodeviewer = true;
|
||||
}
|
||||
|
||||
if (has_full_config_from_profiles(cli)) {
|
||||
gui_params.selected_presets = Slic3r::GUI::CLISelectedProfiles{ cli.input_config.opt_string("print-profile"),
|
||||
cli.input_config.opt_string("printer-profile") ,
|
||||
cli.input_config.option<ConfigOptionStrings>("material-profile")->values };
|
||||
}
|
||||
|
||||
if (!cli.overrides_config.empty())
|
||||
gui_params.extra_config = cli.overrides_config;
|
||||
|
||||
if (cli.input_config.has("load"))
|
||||
gui_params.load_configs = cli.input_config.option<ConfigOptionStrings>("load")->values;
|
||||
|
||||
for (const std::string& file : cli.input_files) {
|
||||
if (boost::starts_with(file, "prusaslicer://")) {
|
||||
gui_params.start_downloader = true;
|
||||
gui_params.download_url = file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return start_gui;
|
||||
}
|
||||
|
||||
int start_gui_with_params(GUI::GUI_InitParams& params)
|
||||
{
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
// likely some linux / unix system
|
||||
const char* display = boost::nowide::getenv("DISPLAY");
|
||||
// const char *wayland_display = boost::nowide::getenv("WAYLAND_DISPLAY");
|
||||
//if (! ((display && *display) || (wayland_display && *wayland_display))) {
|
||||
if (!(display && *display)) {
|
||||
// DISPLAY not set.
|
||||
boost::nowide::cerr << "DISPLAY not set, GUI mode not available." << std::endl << std::endl;
|
||||
print_help(false);
|
||||
// Indicate an error.
|
||||
return 1;
|
||||
}
|
||||
#endif // some linux / unix system
|
||||
return Slic3r::GUI::GUI_Run(params);
|
||||
}
|
||||
|
||||
int start_as_gcode_viewer(GUI::GUI_InitParams& gui_params)
|
||||
{
|
||||
if (gui_params.input_files.size() > 1) {
|
||||
boost::nowide::cerr << "You can open only one .gcode file at a time in GCodeViewer" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!gui_params.input_files.empty()) {
|
||||
const std::string& file = gui_params.input_files[0];
|
||||
if (!is_gcode_file(file) || !boost::filesystem::exists(file)) {
|
||||
boost::nowide::cerr << "Input file isn't a .gcode file or doesn't exist. GCodeViewer can't be start." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return start_gui_with_params(gui_params);
|
||||
}
|
||||
|
||||
}
|
||||
#else // SLIC3R_GUI
|
||||
// If there is no GUI, we shall ignore the parameters. Remove them from the list.
|
||||
#endif // SLIC3R_GUI
|
259
src/CLI/LoadPrintData.cpp
Normal file
259
src/CLI/LoadPrintData.cpp
Normal file
@ -0,0 +1,259 @@
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/nowide/args.hpp>
|
||||
#include <boost/nowide/iostream.hpp>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/GCode/PostProcessor.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/ProfilesSharingUtils.hpp"
|
||||
#include "libslic3r/FileReader.hpp"
|
||||
|
||||
#include "CLI.hpp"
|
||||
|
||||
namespace Slic3r::CLI {
|
||||
|
||||
PrinterTechnology get_printer_technology(const DynamicConfig &config)
|
||||
{
|
||||
const ConfigOptionEnum<PrinterTechnology> *opt = config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology");
|
||||
return (opt == nullptr) ? ptUnknown : opt->value;
|
||||
}
|
||||
|
||||
// may be "validate_and_apply_printer_technology" will be better?
|
||||
static bool can_apply_printer_technology(PrinterTechnology& printer_technology, const PrinterTechnology& other_printer_technology)
|
||||
{
|
||||
if (printer_technology == ptUnknown) {
|
||||
printer_technology = other_printer_technology;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool invalid_other_pt = printer_technology != other_printer_technology && other_printer_technology != ptUnknown;
|
||||
|
||||
if (invalid_other_pt)
|
||||
boost::nowide::cerr << "Mixing configurations for FFF and SLA technologies" << std::endl;
|
||||
|
||||
return !invalid_other_pt;
|
||||
}
|
||||
|
||||
static void print_config_substitutions(const ConfigSubstitutions& config_substitutions, const std::string& file)
|
||||
{
|
||||
if (config_substitutions.empty())
|
||||
return;
|
||||
boost::nowide::cout << "The following configuration values were substituted when loading \" << file << \":\n";
|
||||
for (const ConfigSubstitution& subst : config_substitutions)
|
||||
boost::nowide::cout << "\tkey = \"" << subst.opt_def->opt_key << "\"\t loaded = \"" << subst.old_value << "\tsubstituted = \"" << subst.new_value->serialize() << "\"\n";
|
||||
}
|
||||
|
||||
static bool load_print_config(DynamicPrintConfig &print_config, PrinterTechnology& printer_technology, const Data& cli)
|
||||
{
|
||||
// first of all load configuration from "--load" if any
|
||||
|
||||
if (cli.input_config.has("load")) {
|
||||
|
||||
const std::vector<std::string>& load_configs = cli.input_config.option<ConfigOptionStrings>("load")->values;
|
||||
ForwardCompatibilitySubstitutionRule config_substitution_rule = cli.misc_config.option<ConfigOptionEnum<ForwardCompatibilitySubstitutionRule>>("config_compatibility")->value;
|
||||
|
||||
// load config files supplied via --load
|
||||
for (auto const& file : load_configs) {
|
||||
if (!boost::filesystem::exists(file)) {
|
||||
if (cli.misc_config.has("ignore_nonexistent_config") && cli.misc_config.opt_bool("ignore_nonexistent_config")) {
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
boost::nowide::cerr << "No such file: " << file << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
DynamicPrintConfig config;
|
||||
ConfigSubstitutions config_substitutions;
|
||||
try {
|
||||
config_substitutions = config.load(file, config_substitution_rule);
|
||||
}
|
||||
catch (std::exception& ex) {
|
||||
boost::nowide::cerr << "Error while reading config file \"" << file << "\": " << ex.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!can_apply_printer_technology(printer_technology, get_printer_technology(config)))
|
||||
return false;
|
||||
|
||||
print_config_substitutions(config_substitutions, file);
|
||||
|
||||
config.normalize_fdm();
|
||||
print_config.apply(config);
|
||||
}
|
||||
}
|
||||
|
||||
// than apply other options from full print config if any is provided by prifiles set
|
||||
|
||||
if (has_full_config_from_profiles(cli)) {
|
||||
DynamicPrintConfig config;
|
||||
// load config from profiles set
|
||||
std::string errors = Slic3r::load_full_print_config(cli.input_config.opt_string("print-profile"),
|
||||
cli.input_config.option<ConfigOptionStrings>("material-profile")->values,
|
||||
cli.input_config.opt_string("printer-profile"),
|
||||
config, printer_technology);
|
||||
if (!errors.empty()) {
|
||||
boost::nowide::cerr << "Error while loading config from profiles: " << errors << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!can_apply_printer_technology(printer_technology, get_printer_technology(config)))
|
||||
return false;
|
||||
|
||||
config.normalize_fdm();
|
||||
|
||||
// config is applied with print_config loaded before
|
||||
config += std::move(print_config);
|
||||
print_config = std::move(config);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool process_input_files(std::vector<Model>& models, DynamicPrintConfig& print_config, PrinterTechnology& printer_technology, Data& cli)
|
||||
{
|
||||
for (const std::string& file : cli.input_files) {
|
||||
if (boost::starts_with(file, "prusaslicer://")) {
|
||||
continue;
|
||||
}
|
||||
if (!boost::filesystem::exists(file)) {
|
||||
boost::nowide::cerr << "No such file: " << file << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
Model model;
|
||||
try {
|
||||
if (has_full_config_from_profiles(cli) || !FileReader::is_project_file(file)) {
|
||||
// we have full banch of options from profiles set
|
||||
// so, just load a geometry
|
||||
model = FileReader::load_model(file);
|
||||
}
|
||||
else {
|
||||
// load model and configuration from the file
|
||||
DynamicPrintConfig config;
|
||||
ConfigSubstitutionContext config_substitutions_ctxt(cli.misc_config.option<ConfigOptionEnum<ForwardCompatibilitySubstitutionRule>>("config_compatibility")->value);
|
||||
boost::optional<Semver> prusaslicer_generator_version;
|
||||
|
||||
//FIXME should we check the version here? // | Model::LoadAttribute::CheckVersion ?
|
||||
model = FileReader::load_model_with_config(file, &config, &config_substitutions_ctxt, prusaslicer_generator_version, FileReader::LoadAttribute::AddDefaultInstances);
|
||||
|
||||
if (!can_apply_printer_technology(printer_technology, get_printer_technology(config)))
|
||||
return false;
|
||||
|
||||
print_config_substitutions(config_substitutions_ctxt.substitutions, file);
|
||||
|
||||
// config is applied with print_config loaded before
|
||||
config += std::move(print_config);
|
||||
print_config = std::move(config);
|
||||
}
|
||||
|
||||
// If model for slicing is loaded from 3mf file, then its geometry has to be used and arrange couldn't be apply for this model.
|
||||
if (FileReader::is_project_file(file) &&
|
||||
(!cli.transform_config.has("dont_arrange") || !cli.transform_config.opt_bool("dont_arrange"))) {
|
||||
//So, check a state of "dont_arrange" parameter and set it to true, if its value is false.
|
||||
cli.transform_config.set_key_value("dont_arrange", new ConfigOptionBool(true));
|
||||
}
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
boost::nowide::cerr << file << ": " << e.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (model.objects.empty()) {
|
||||
boost::nowide::cerr << "Error: file is empty: " << file << std::endl;
|
||||
continue;
|
||||
}
|
||||
models.push_back(model);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool finalize_print_config(DynamicPrintConfig& print_config, PrinterTechnology& printer_technology, const Data& cli)
|
||||
{
|
||||
// Apply command line options to a more specific DynamicPrintConfig which provides normalize()
|
||||
// (command line options override --load files or congiguration which is loaded prom profiles)
|
||||
print_config.apply(cli.overrides_config, true);
|
||||
// Normalizing after importing the 3MFs / AMFs
|
||||
print_config.normalize_fdm();
|
||||
|
||||
if (printer_technology == ptUnknown)
|
||||
printer_technology = cli.actions_config.has("export_sla") ? ptSLA : ptFFF;
|
||||
print_config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology", true)->value = printer_technology;
|
||||
|
||||
// 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(print_config, true);
|
||||
print_config.apply(fff_print_config, true);
|
||||
}
|
||||
else {
|
||||
assert(printer_technology == 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(print_config, true);
|
||||
print_config.apply(sla_print_config, true);
|
||||
}
|
||||
|
||||
// validate print configuration
|
||||
std::string validity = print_config.validate();
|
||||
if (!validity.empty()) {
|
||||
boost::nowide::cerr << "Error: The composite configation is not valid: " << validity << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool load_print_data(std::vector<Model>& models,
|
||||
DynamicPrintConfig& print_config,
|
||||
PrinterTechnology& printer_technology,
|
||||
Data& cli)
|
||||
{
|
||||
if (!load_print_config(print_config, printer_technology, cli))
|
||||
return false;
|
||||
|
||||
if (!process_input_files(models, print_config, printer_technology, cli))
|
||||
return false;
|
||||
|
||||
if (!finalize_print_config(print_config, printer_technology, cli))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_needed_post_processing(const DynamicPrintConfig& print_config)
|
||||
{
|
||||
if (print_config.has("post_process")) {
|
||||
const std::vector<std::string>& post_process = print_config.opt<ConfigOptionStrings>("post_process")->values;
|
||||
if (!post_process.empty()) {
|
||||
boost::nowide::cout << "\nA post-processing script has been detected in the config data:\n\n";
|
||||
for (const std::string& s : post_process) {
|
||||
boost::nowide::cout << "> " << s << "\n";
|
||||
}
|
||||
boost::nowide::cout << "\nContinue(Y/N) ? ";
|
||||
char in;
|
||||
boost::nowide::cin >> in;
|
||||
if (in != 'Y' && in != 'y')
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
180
src/CLI/PrintHelp.cpp
Normal file
180
src/CLI/PrintHelp.cpp
Normal file
@ -0,0 +1,180 @@
|
||||
#include <string>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/nowide/iostream.hpp>
|
||||
|
||||
#include "CLI.hpp"
|
||||
|
||||
namespace Slic3r::CLI {
|
||||
|
||||
static void print_help(const ConfigDef& config_def, bool show_defaults, std::function<bool(const ConfigOptionDef&)> filter = [](const ConfigOptionDef &){ return true; })
|
||||
{
|
||||
|
||||
// prepare a function for wrapping text
|
||||
auto wrap = [](const std::string& text, size_t line_length) -> std::string {
|
||||
std::istringstream words(text);
|
||||
std::ostringstream wrapped;
|
||||
std::string word;
|
||||
|
||||
if (words >> word) {
|
||||
wrapped << word;
|
||||
size_t space_left = line_length - word.length();
|
||||
while (words >> word) {
|
||||
if (space_left < word.length() + 1) {
|
||||
wrapped << '\n' << word;
|
||||
space_left = line_length - word.length();
|
||||
}
|
||||
else {
|
||||
wrapped << ' ' << word;
|
||||
space_left -= word.length() + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return wrapped.str();
|
||||
};
|
||||
|
||||
// List of opt_keys that should be hidden from the CLI help.
|
||||
const std::vector<std::string> silent_options = { "webdev", "single_instance_on_url" };
|
||||
|
||||
// get the unique categories
|
||||
std::set<std::string> categories;
|
||||
for (const auto& opt : config_def.options) {
|
||||
const ConfigOptionDef& def = opt.second;
|
||||
if (filter(def))
|
||||
categories.insert(def.category);
|
||||
}
|
||||
|
||||
for (const std::string& category : categories) {
|
||||
if (category != "") {
|
||||
boost::nowide::cout << category << ":" << std::endl;
|
||||
}
|
||||
else if (categories.size() > 1) {
|
||||
boost::nowide::cout << "Misc options:" << std::endl;
|
||||
}
|
||||
|
||||
for (const auto& opt : config_def.options) {
|
||||
const ConfigOptionDef& def = opt.second;
|
||||
if (def.category != category || def.cli == ConfigOptionDef::nocli || !filter(def))
|
||||
continue;
|
||||
|
||||
if (std::find(silent_options.begin(), silent_options.end(), opt.second.opt_key) != silent_options.end())
|
||||
continue;
|
||||
|
||||
// get all possible variations: --foo, --foobar, -f...
|
||||
std::vector<std::string> cli_args = def.cli_args(opt.first);
|
||||
if (cli_args.empty())
|
||||
continue;
|
||||
|
||||
for (auto& arg : cli_args) {
|
||||
arg.insert(0, (arg.size() == 1) ? "-" : "--");
|
||||
if (def.type == coFloat || def.type == coInt || def.type == coFloatOrPercent
|
||||
|| def.type == coFloats || def.type == coInts) {
|
||||
arg += " N";
|
||||
}
|
||||
else if (def.type == coPoint) {
|
||||
arg += " X,Y";
|
||||
}
|
||||
else if (def.type == coPoint3) {
|
||||
arg += " X,Y,Z";
|
||||
}
|
||||
else if (def.type == coString || def.type == coStrings) {
|
||||
arg += " ABCD";
|
||||
}
|
||||
}
|
||||
|
||||
// left: command line options
|
||||
const std::string cli = boost::algorithm::join(cli_args, ", ");
|
||||
boost::nowide::cout << " " << std::left << std::setw(20) << cli;
|
||||
|
||||
// right: option description
|
||||
std::string descr = def.tooltip;
|
||||
bool show_defaults_this = show_defaults || def.opt_key == "config_compatibility";
|
||||
if (show_defaults_this && def.default_value && def.type != coBool
|
||||
&& (def.type != coString || !def.default_value->serialize().empty())) {
|
||||
descr += " (";
|
||||
if (!def.sidetext.empty()) {
|
||||
descr += def.sidetext + ", ";
|
||||
}
|
||||
else if (def.enum_def && def.enum_def->has_values()) {
|
||||
descr += boost::algorithm::join(def.enum_def->values(), ", ") + "; ";
|
||||
}
|
||||
descr += "default: " + def.default_value->serialize() + ")";
|
||||
}
|
||||
|
||||
// wrap lines of description
|
||||
descr = wrap(descr, 80);
|
||||
std::vector<std::string> lines;
|
||||
boost::split(lines, descr, boost::is_any_of("\n"));
|
||||
|
||||
// if command line options are too long, print description in new line
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
if (i == 0 && cli.size() > 19)
|
||||
boost::nowide::cout << std::endl;
|
||||
if (i > 0 || cli.size() > 19)
|
||||
boost::nowide::cout << std::string(21, ' ');
|
||||
boost::nowide::cout << lines[i] << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void print_help(bool include_print_options/* = false*/, PrinterTechnology printer_technology/* = ptAny*/)
|
||||
{
|
||||
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 [ INPUT ] [ OPTIONS ] [ ACTIONS ] [ TRANSFORM ] [ file.stl ... ]" << std::endl;
|
||||
|
||||
boost::nowide::cout
|
||||
<< std::endl
|
||||
<< "Input:" << std::endl;
|
||||
print_help(cli_input_config_def, false);
|
||||
|
||||
boost::nowide::cout
|
||||
<< std::endl
|
||||
<< "Note: To load configuration from profiles, you need to set whole banch of presets" << std::endl;
|
||||
|
||||
boost::nowide::cout
|
||||
<< std::endl
|
||||
<< "Actions:" << std::endl;
|
||||
print_help(cli_actions_config_def, false);
|
||||
|
||||
boost::nowide::cout
|
||||
<< std::endl
|
||||
<< "Transform options:" << std::endl;
|
||||
print_help(cli_transform_config_def, false);
|
||||
|
||||
boost::nowide::cout
|
||||
<< std::endl
|
||||
<< "Other options:" << std::endl;
|
||||
print_help(cli_misc_config_def, 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 3mf files" << std::endl;
|
||||
|
||||
if (include_print_options) {
|
||||
boost::nowide::cout << std::endl;
|
||||
print_help(print_config_def, 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
367
src/CLI/ProcessActions.cpp
Normal file
367
src/CLI/ProcessActions.cpp
Normal file
@ -0,0 +1,367 @@
|
||||
#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/cstdlib.hpp>
|
||||
#include <boost/nowide/iostream.hpp>
|
||||
#include <boost/nowide/filesystem.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/nowide/iostream.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <boost/dll/runtime_symbol_info.hpp>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#if !SLIC3R_OPENGL_ES
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#endif // !SLIC3R_OPENGL_ES
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#include "libslic3r/GCode/PostProcessor.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/Preset.hpp"
|
||||
#include "libslic3r/ProfilesSharingUtils.hpp"
|
||||
#include <arrange-wrapper/ModelArrange.hpp>
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/SLAPrint.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/miniz_extension.hpp"
|
||||
#include "libslic3r/PNGReadWrite.hpp"
|
||||
#include "libslic3r/MultipleBeds.hpp"
|
||||
|
||||
#include "CLI.hpp"
|
||||
|
||||
namespace Slic3r::CLI {
|
||||
|
||||
static bool has_profile_sharing_action(const Data& cli)
|
||||
{
|
||||
return cli.actions_config.has("query-printer-models") || cli.actions_config.has("query-print-filament-profiles");
|
||||
}
|
||||
|
||||
bool has_full_config_from_profiles(const Data& cli)
|
||||
{
|
||||
const DynamicPrintConfig& input = cli.input_config;
|
||||
return !has_profile_sharing_action(cli) &&
|
||||
(input.has("print-profile") && !input.opt_string("print-profile").empty() ||
|
||||
input.has("material-profile") && !input.option<ConfigOptionStrings>("material-profile")->values.empty() ||
|
||||
input.has("printer-profile") && !input.opt_string("printer-profile").empty());
|
||||
}
|
||||
|
||||
bool process_profiles_sharing(const Data& cli)
|
||||
{
|
||||
if (!has_profile_sharing_action(cli))
|
||||
return false;
|
||||
|
||||
std::string ret;
|
||||
|
||||
if (cli.actions_config.has("query-printer-models")) {
|
||||
ret = Slic3r::get_json_printer_models(get_printer_technology(cli.overrides_config));
|
||||
}
|
||||
else if (cli.actions_config.has("query-print-filament-profiles")) {
|
||||
if (cli.input_config.has("printer-profile") && !cli.input_config.opt_string("printer-profile").empty()) {
|
||||
const std::string printer_profile = cli.input_config.opt_string("printer-profile");
|
||||
ret = Slic3r::get_json_print_filament_profiles(printer_profile);
|
||||
if (ret.empty()) {
|
||||
boost::nowide::cerr << "query-print-filament-profiles error: Printer profile '" << printer_profile <<
|
||||
"' wasn't found among installed printers." << std::endl <<
|
||||
"Or the request can be wrong." << std::endl;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
boost::nowide::cerr << "query-print-filament-profiles error: This action requires set 'printer-profile' option" << std::endl;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret.empty()) {
|
||||
boost::nowide::cerr << "Wrong request" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
// use --output when available
|
||||
|
||||
if (cli.misc_config.has("output")) {
|
||||
std::string cmdline_param = cli.misc_config.opt_string("output");
|
||||
// if we were supplied a directory, use it and append our automatically generated filename
|
||||
boost::filesystem::path cmdline_path(cmdline_param);
|
||||
boost::filesystem::path proposed_path = boost::filesystem::path(Slic3r::resources_dir()) / "out.json";
|
||||
if (boost::filesystem::is_directory(cmdline_path))
|
||||
proposed_path = (cmdline_path / proposed_path.filename());
|
||||
else if (cmdline_path.extension().empty())
|
||||
proposed_path = cmdline_path.replace_extension("json");
|
||||
else
|
||||
proposed_path = cmdline_path;
|
||||
const std::string file = proposed_path.string();
|
||||
|
||||
boost::nowide::ofstream c;
|
||||
c.open(file, std::ios::out | std::ios::trunc);
|
||||
c << ret << std::endl;
|
||||
c.close();
|
||||
|
||||
boost::nowide::cout << "Output for your request is written into " << file << std::endl;
|
||||
}
|
||||
else
|
||||
printf("%s", ret.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace IO {
|
||||
enum ExportFormat : int {
|
||||
OBJ,
|
||||
STL,
|
||||
// SVG,
|
||||
TMF,
|
||||
Gcode
|
||||
};
|
||||
}
|
||||
|
||||
static std::string output_filepath(const Model& model, IO::ExportFormat format, const std::string& cmdline_param)
|
||||
{
|
||||
std::string ext;
|
||||
switch (format) {
|
||||
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
|
||||
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();
|
||||
}
|
||||
|
||||
static bool export_models(std::vector<Model>& models, IO::ExportFormat format, const std::string& cmdline_param)
|
||||
{
|
||||
for (Model& model : models) {
|
||||
const std::string path = output_filepath(model, format, cmdline_param);
|
||||
bool success = false;
|
||||
switch (format) {
|
||||
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;
|
||||
}
|
||||
|
||||
static std::function<ThumbnailsList(const ThumbnailsParams&)> get_thumbnail_generator_cli(Print& fff_print)
|
||||
{
|
||||
if (!fff_print.model().objects.empty() && boost::iends_with(fff_print.model().objects.front()->input_file, ".3mf")) {
|
||||
std::string filename = fff_print.model().objects.front()->input_file;
|
||||
return [filename](const ThumbnailsParams&) {
|
||||
ThumbnailsList list_out;
|
||||
|
||||
mz_zip_archive archive;
|
||||
mz_zip_zero_struct(&archive);
|
||||
|
||||
if (!open_zip_reader(&archive, filename))
|
||||
return list_out;
|
||||
mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
|
||||
mz_zip_archive_file_stat stat;
|
||||
|
||||
int index = mz_zip_reader_locate_file(&archive, "Metadata/thumbnail.png", nullptr, 0);
|
||||
if (index < 0 || !mz_zip_reader_file_stat(&archive, index, &stat))
|
||||
return list_out;
|
||||
std::string buffer;
|
||||
buffer.resize(int(stat.m_uncomp_size));
|
||||
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, buffer.data(), (size_t)stat.m_uncomp_size, 0);
|
||||
if (res == 0)
|
||||
return list_out;
|
||||
close_zip_reader(&archive);
|
||||
|
||||
std::vector<unsigned char> data;
|
||||
unsigned width = 0;
|
||||
unsigned height = 0;
|
||||
png::decode_png(buffer, data, width, height);
|
||||
|
||||
{
|
||||
// Flip the image vertically so it matches the convention in Thumbnails generator.
|
||||
const int row_size = width * 4; // Each pixel is 4 bytes (RGBA)
|
||||
std::vector<unsigned char> temp_row(row_size);
|
||||
for (int i = 0; i < height / 2; ++i) {
|
||||
unsigned char* top_row = &data[i * row_size];
|
||||
unsigned char* bottom_row = &data[(height - i - 1) * row_size];
|
||||
std::copy(bottom_row, bottom_row + row_size, temp_row.begin());
|
||||
std::copy(top_row, top_row + row_size, bottom_row);
|
||||
std::copy(temp_row.begin(), temp_row.end(), top_row);
|
||||
}
|
||||
}
|
||||
|
||||
ThumbnailData th;
|
||||
th.set(width, height);
|
||||
th.pixels = data;
|
||||
list_out.push_back(th);
|
||||
return list_out;
|
||||
};
|
||||
}
|
||||
|
||||
return [](const ThumbnailsParams&) ->ThumbnailsList { return {}; };
|
||||
}
|
||||
|
||||
bool process_actions(Data& cli, const DynamicPrintConfig& print_config, std::vector<Model>& models)
|
||||
{
|
||||
DynamicPrintConfig& actions = cli.actions_config;
|
||||
DynamicPrintConfig& transform = cli.transform_config;
|
||||
|
||||
// doesn't need any aditional input
|
||||
|
||||
if (actions.has("help")) {
|
||||
print_help();
|
||||
}
|
||||
if (actions.has("help_fff")) {
|
||||
print_help(true, ptFFF);
|
||||
}
|
||||
if (actions.has("help_sla")) {
|
||||
print_help(true, ptSLA);
|
||||
}
|
||||
|
||||
if (actions.has("info")) {
|
||||
if (models.empty()) {
|
||||
boost::nowide::cerr << "error: cannot show info for empty models." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
// --info works on unrepaired model
|
||||
for (Model& model : models) {
|
||||
model.add_default_instances();
|
||||
model.print_info();
|
||||
}
|
||||
}
|
||||
|
||||
if (actions.has("save")) {
|
||||
//FIXME check for mixing the FFF / SLA parameters.
|
||||
// or better save fff_print_config vs. sla_print_config
|
||||
print_config.save(actions.opt_string("save"));
|
||||
}
|
||||
|
||||
if (models.empty() && (actions.has("export_stl") || actions.has("export_obj") || actions.has("export_3mf"))) {
|
||||
boost::nowide::cerr << "error: cannot export empty models." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const std::string output = cli.misc_config.has("output") ? cli.misc_config.opt_string("output") : "";
|
||||
|
||||
if (actions.has("export_stl")) {
|
||||
for (auto& model : models)
|
||||
model.add_default_instances();
|
||||
if (!export_models(models, IO::STL, output))
|
||||
return 1;
|
||||
}
|
||||
if (actions.has("export_obj")) {
|
||||
for (auto& model : models)
|
||||
model.add_default_instances();
|
||||
if (!export_models(models, IO::OBJ, output))
|
||||
return 1;
|
||||
}
|
||||
if (actions.has("export_3mf")) {
|
||||
if (!export_models(models, IO::TMF, output))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (actions.has("slice")) {
|
||||
PrinterTechnology printer_technology = Preset::printer_technology(print_config);
|
||||
|
||||
const Vec2crd gap{ s_multiple_beds.get_bed_gap() };
|
||||
arr2::ArrangeBed bed = arr2::to_arrange_bed(get_bed_shape(print_config), gap);
|
||||
arr2::ArrangeSettings arrange_cfg;
|
||||
arrange_cfg.set_distance_from_objects(min_object_distance(print_config));
|
||||
|
||||
for (Model& model : models) {
|
||||
// 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).
|
||||
if (!transform.has("dont_arrange") || !transform.opt_bool("dont_arrange")) {
|
||||
if (transform.has("center")) {
|
||||
Vec2d c = transform.option<ConfigOptionPoint>("center")->value;
|
||||
arrange_objects(model, arr2::InfiniteBed{ scaled(c) }, arrange_cfg);
|
||||
}
|
||||
else
|
||||
arrange_objects(model, bed, arrange_cfg);
|
||||
}
|
||||
|
||||
Print fff_print;
|
||||
SLAPrint sla_print;
|
||||
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());
|
||||
std::fflush(stdout);
|
||||
}
|
||||
});
|
||||
|
||||
PrintBase* print = (printer_technology == ptFFF) ? static_cast<PrintBase*>(&fff_print) : static_cast<PrintBase*>(&sla_print);
|
||||
if (printer_technology == ptFFF) {
|
||||
for (auto* mo : model.objects)
|
||||
fff_print.auto_assign_extruders(mo);
|
||||
}
|
||||
print->apply(model, print_config);
|
||||
std::string err = print->validate();
|
||||
if (!err.empty()) {
|
||||
boost::nowide::cerr << err << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string outfile = output;
|
||||
|
||||
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.
|
||||
outfile = fff_print.export_gcode(outfile, nullptr, get_thumbnail_generator_cli(fff_print));
|
||||
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_print(outfile_final);
|
||||
}
|
||||
if (outfile != outfile_final) {
|
||||
if (Slic3r::rename_file(outfile, outfile_final)) {
|
||||
boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl;
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
210
src/CLI/ProcessTransform.cpp
Normal file
210
src/CLI/ProcessTransform.cpp
Normal file
@ -0,0 +1,210 @@
|
||||
#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/cstdlib.hpp>
|
||||
#include <boost/nowide/iostream.hpp>
|
||||
#include <boost/nowide/filesystem.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/nowide/iostream.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <boost/dll/runtime_symbol_info.hpp>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#if !SLIC3R_OPENGL_ES
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#endif // !SLIC3R_OPENGL_ES
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/ModelProcessing.hpp"
|
||||
#include "libslic3r/CutUtils.hpp"
|
||||
#include <arrange-wrapper/ModelArrange.hpp>
|
||||
#include "libslic3r/MultipleBeds.hpp"
|
||||
|
||||
#include "CLI.hpp"
|
||||
|
||||
namespace Slic3r::CLI {
|
||||
|
||||
bool process_transform(Data& cli, const DynamicPrintConfig& print_config, std::vector<Model>& models)
|
||||
{
|
||||
DynamicPrintConfig& transform = cli.transform_config;
|
||||
DynamicPrintConfig& actions = cli.actions_config;
|
||||
|
||||
const Vec2crd gap{ s_multiple_beds.get_bed_gap() };
|
||||
arr2::ArrangeBed bed = arr2::to_arrange_bed(get_bed_shape(print_config), gap);
|
||||
arr2::ArrangeSettings arrange_cfg;
|
||||
if (transform.has("merge") || transform.has("duplicate"))
|
||||
arrange_cfg.set_distance_from_objects(min_object_distance(print_config));
|
||||
|
||||
if (transform.has("merge")) {
|
||||
Model m;
|
||||
|
||||
for (auto& model : models)
|
||||
for (ModelObject* o : model.objects)
|
||||
m.add_object(*o);
|
||||
// Rearrange instances unless --dont-arrange is supplied
|
||||
if (!transform.has("dont_arrange") && !transform.opt_bool("dont_arrange")) {
|
||||
m.add_default_instances();
|
||||
if (actions.has("slice"))
|
||||
arrange_objects(m, bed, arrange_cfg);
|
||||
else
|
||||
arrange_objects(m, arr2::InfiniteBed{}, arrange_cfg);//??????
|
||||
}
|
||||
models.clear();
|
||||
models.emplace_back(std::move(m));
|
||||
}
|
||||
|
||||
if (transform.has("duplicate")) {
|
||||
for (auto& model : models) {
|
||||
const bool all_objects_have_instances = std::none_of(
|
||||
model.objects.begin(), model.objects.end(),
|
||||
[](ModelObject* o) { return o->instances.empty(); }
|
||||
);
|
||||
|
||||
int dups = transform.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 false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (transform.has("duplicate_grid")) {
|
||||
std::vector<int>& ints = transform.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 = print_config.opt_float("duplicate_distance");
|
||||
for (auto& model : models)
|
||||
model.duplicate_objects_grid(x, y, (distance > 0) ? distance : 6); // TODO: this is not the right place for setting a default
|
||||
}
|
||||
|
||||
if (transform.has("center")) {
|
||||
for (auto& model : models) {
|
||||
model.add_default_instances();
|
||||
// this affects instances:
|
||||
model.center_instances_around_point(transform.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());
|
||||
}
|
||||
}
|
||||
|
||||
if (transform.has("align_xy")) {
|
||||
const Vec2d& p = transform.option<ConfigOptionPoint>("align_xy")->value;
|
||||
for (auto& model : models) {
|
||||
BoundingBoxf3 bb = model.bounding_box_exact();
|
||||
// this affects volumes:
|
||||
model.translate(-(bb.min.x() - p.x()), -(bb.min.y() - p.y()), -bb.min.z());
|
||||
}
|
||||
}
|
||||
|
||||
if (transform.has("rotate")) {
|
||||
for (auto& model : models)
|
||||
for (auto& o : model.objects)
|
||||
// this affects volumes:
|
||||
o->rotate(Geometry::deg2rad(transform.opt_float("rotate")), Z);
|
||||
}
|
||||
if (transform.has("rotate_x")) {
|
||||
for (auto& model : models)
|
||||
for (auto& o : model.objects)
|
||||
// this affects volumes:
|
||||
o->rotate(Geometry::deg2rad(transform.opt_float("rotate_x")), X);
|
||||
}
|
||||
if (transform.has("rotate_y")) {
|
||||
for (auto& model : models)
|
||||
for (auto& o : model.objects)
|
||||
// this affects volumes:
|
||||
o->rotate(Geometry::deg2rad(transform.opt_float("rotate_y")), Y);
|
||||
}
|
||||
|
||||
if (transform.has("scale")) {
|
||||
for (auto& model : models)
|
||||
for (auto& o : model.objects)
|
||||
// this affects volumes:
|
||||
o->scale(transform.get_abs_value("scale", 1));
|
||||
}
|
||||
if (transform.has("scale_to_fit")) {
|
||||
const Vec3d& opt = transform.opt<ConfigOptionPoint3>("scale_to_fit")->value;
|
||||
if (opt.x() <= 0 || opt.y() <= 0 || opt.z() <= 0) {
|
||||
boost::nowide::cerr << "--scale-to-fit requires a positive volume" << std::endl;
|
||||
return false;
|
||||
}
|
||||
for (auto& model : models)
|
||||
for (auto& o : model.objects)
|
||||
// this affects volumes:
|
||||
o->scale_to_fit(opt);
|
||||
}
|
||||
|
||||
if (transform.has("cut")) {
|
||||
std::vector<Model> new_models;
|
||||
const Vec3d plane_center = transform.opt_float("cut") * Vec3d::UnitZ();
|
||||
for (auto& model : models) {
|
||||
Model new_model;
|
||||
model.translate(0, 0, -model.bounding_box_exact().min.z()); // align to z = 0
|
||||
size_t num_objects = model.objects.size();
|
||||
for (size_t i = 0; i < num_objects; ++i) {
|
||||
ModelObject* mo = model.objects.front();
|
||||
const Vec3d cut_center_offset = plane_center - mo->instances[0]->get_offset();
|
||||
Cut cut(mo, 0, Geometry::translation_transform(cut_center_offset),
|
||||
ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::PlaceOnCutUpper);
|
||||
auto cut_objects = cut.perform_with_plane();
|
||||
for (ModelObject* obj : cut_objects)
|
||||
new_model.add_object(*obj);
|
||||
model.delete_object(size_t(0));
|
||||
}
|
||||
new_models.push_back(new_model);
|
||||
}
|
||||
|
||||
// TODO: copy less stuff around using pointers
|
||||
models = new_models;
|
||||
|
||||
if (actions.empty()) {
|
||||
// cutting transformations are setting an "export" action.
|
||||
actions.set_key_value("export_stl", new ConfigOptionBool(true));
|
||||
}
|
||||
}
|
||||
|
||||
if (transform.has("split")) {
|
||||
for (Model& model : models) {
|
||||
size_t num_objects = model.objects.size();
|
||||
for (size_t i = 0; i < num_objects; ++i) {
|
||||
ModelObjectPtrs new_objects;
|
||||
ModelProcessing::split(model.objects.front(), &new_objects);
|
||||
model.delete_object(size_t(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All transforms have been dealt with. Now ensure that the objects are on bed.
|
||||
// (Unless the user said otherwise.)
|
||||
if (!transform.has("ensure_on_bed") || transform.opt_bool("ensure_on_bed"))
|
||||
for (auto& model : models)
|
||||
for (auto& o : model.objects)
|
||||
o->ensure_on_bed();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
54
src/CLI/Run.cpp
Normal file
54
src/CLI/Run.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
#include "../PrusaSlicer.hpp"
|
||||
#include "CLI.hpp"
|
||||
|
||||
namespace Slic3r::CLI {
|
||||
|
||||
int run(int argc, char** argv)
|
||||
{
|
||||
Data cli;
|
||||
if (!setup(cli, argc, argv))
|
||||
return 1;
|
||||
|
||||
if (process_profiles_sharing(cli))
|
||||
return 1;
|
||||
|
||||
bool start_gui = cli.empty() || (cli.actions_config.empty() && !cli.transform_config.has("cut"));
|
||||
PrinterTechnology printer_technology = get_printer_technology(cli.overrides_config);
|
||||
DynamicPrintConfig print_config = {};
|
||||
std::vector<Model> models;
|
||||
|
||||
#ifdef SLIC3R_GUI
|
||||
GUI::GUI_InitParams gui_params;
|
||||
start_gui |= init_gui_params(gui_params, argc, argv, cli);
|
||||
|
||||
if (gui_params.start_as_gcodeviewer)
|
||||
return start_as_gcode_viewer(gui_params);
|
||||
#endif
|
||||
|
||||
if (!load_print_data(models, print_config, printer_technology, cli))
|
||||
return 1;
|
||||
|
||||
if (!start_gui && is_needed_post_processing(print_config))
|
||||
return 0;
|
||||
|
||||
if (!process_transform(cli, print_config, models))
|
||||
return 1;
|
||||
|
||||
if (!process_actions(cli, print_config, models))
|
||||
return 1;
|
||||
|
||||
if (start_gui) {
|
||||
#ifdef SLIC3R_GUI
|
||||
return start_gui_with_params(gui_params);
|
||||
#else
|
||||
// No GUI support. Just print out a help.
|
||||
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
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
341
src/CLI/Setup.cpp
Normal file
341
src/CLI/Setup.cpp
Normal file
@ -0,0 +1,341 @@
|
||||
#include <boost/nowide/cstdlib.hpp>
|
||||
#include <boost/nowide/filesystem.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/nowide/iostream.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <boost/dll/runtime_symbol_info.hpp>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/Platform.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Thread.hpp"
|
||||
#include "libslic3r/BlacklistedLibraryCheck.hpp"
|
||||
#include "libslic3r/Utils/DirectoriesUtils.hpp"
|
||||
|
||||
#include "CLI.hpp"
|
||||
|
||||
#ifdef SLIC3R_GUI
|
||||
#include "slic3r/Utils/ServiceConfig.hpp"
|
||||
#endif /* SLIC3R_GUI */
|
||||
|
||||
|
||||
namespace Slic3r::CLI {
|
||||
|
||||
Data::Data()
|
||||
{
|
||||
input_config = CLI_DynamicPrintConfig(Type::Input, &cli_input_config_def);
|
||||
overrides_config = CLI_DynamicPrintConfig(Type::Overrides, &print_config_def);
|
||||
transform_config = CLI_DynamicPrintConfig(Type::Transformations, &cli_transform_config_def);
|
||||
misc_config = CLI_DynamicPrintConfig(Type::Misc, &cli_misc_config_def);
|
||||
actions_config = CLI_DynamicPrintConfig(Type::Actions, &cli_actions_config_def);
|
||||
}
|
||||
|
||||
using opts_map = std::map<std::string, std::pair<std::string, Type> >;
|
||||
|
||||
static opts_map get_opts_map(const Data& data)
|
||||
{
|
||||
opts_map ret;
|
||||
|
||||
for (const CLI_DynamicPrintConfig* config : { &data.input_config ,
|
||||
&data.overrides_config,
|
||||
&data.transform_config,
|
||||
&data.misc_config ,
|
||||
&data.actions_config })
|
||||
{
|
||||
for (const auto& oit : config->def()->options)
|
||||
for (const std::string& t : oit.second.cli_args(oit.first))
|
||||
ret[t] = { oit.first , config->type()};
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static CLI_DynamicPrintConfig* get_config(Data& data, Type type)
|
||||
{
|
||||
for (CLI_DynamicPrintConfig* config : { &data.input_config ,
|
||||
&data.overrides_config,
|
||||
&data.transform_config,
|
||||
&data.misc_config ,
|
||||
&data.actions_config })
|
||||
{
|
||||
if (type == config->type())
|
||||
return config;
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool read(Data& data, int argc, const char* const argv[])
|
||||
{
|
||||
// cache the CLI option => opt_key mapping
|
||||
opts_map opts = get_opts_map(data);
|
||||
|
||||
bool parse_options = true;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::string token = argv[i];
|
||||
// Store non-option arguments in the provided vector.
|
||||
if (!parse_options || !boost::starts_with(token, "-")) {
|
||||
data.input_files.push_back(token);
|
||||
continue;
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
if (boost::starts_with(token, "-psn_"))
|
||||
// OSX launcher may add a "process serial number", for example "-psn_0_989382" to the command line.
|
||||
// While it is supposed to be dropped since OSX 10.9, we will rather ignore it.
|
||||
continue;
|
||||
#endif /* __APPLE__ */
|
||||
// Stop parsing tokens as options when -- is supplied.
|
||||
if (token == "--") {
|
||||
parse_options = false;
|
||||
continue;
|
||||
}
|
||||
// Remove leading dashes (one or two).
|
||||
token.erase(token.begin(), token.begin() + (boost::starts_with(token, "--") ? 2 : 1));
|
||||
// Read value when supplied in the --key=value form.
|
||||
std::string value;
|
||||
{
|
||||
size_t equals_pos = token.find("=");
|
||||
if (equals_pos != std::string::npos) {
|
||||
value = token.substr(equals_pos + 1);
|
||||
token.erase(equals_pos);
|
||||
}
|
||||
}
|
||||
// Look for the cli -> option mapping.
|
||||
auto it = opts.find(token);
|
||||
bool no = false;
|
||||
if (it == opts.end()) {
|
||||
// Remove the "no-" prefix used to negate boolean options.
|
||||
std::string yes_token;
|
||||
if (boost::starts_with(token, "no-")) {
|
||||
yes_token = token.substr(3);
|
||||
it = opts.find(yes_token);
|
||||
no = true;
|
||||
}
|
||||
if (it == opts.end()) {
|
||||
boost::nowide::cerr << "Unknown option --" << token.c_str() << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (no)
|
||||
token = yes_token;
|
||||
}
|
||||
|
||||
const auto& [opt_key, type] = it->second;
|
||||
|
||||
CLI_DynamicPrintConfig* config = get_config(data, type);
|
||||
const ConfigOptionDef* optdef = config->option_def(opt_key);
|
||||
assert(optdef);
|
||||
|
||||
// If the option type expects a value and it was not already provided,
|
||||
// look for it in the next token.
|
||||
if (value.empty() && optdef->type != coBool && optdef->type != coBools) {
|
||||
if (i == argc - 1) {
|
||||
boost::nowide::cerr << "No value supplied for --" << token.c_str() << std::endl;
|
||||
return false;
|
||||
}
|
||||
value = argv[++i];
|
||||
}
|
||||
|
||||
if (no) {
|
||||
assert(optdef->type == coBool || optdef->type == coBools);
|
||||
if (!value.empty()) {
|
||||
boost::nowide::cerr << "Boolean options negated by the --no- prefix cannot have a value." << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Store the option value.
|
||||
const bool existing = config->has(opt_key);
|
||||
ConfigOption* opt_base = existing ? config->option(opt_key) : optdef->create_default_option();
|
||||
if (!existing)
|
||||
config->set_key_value(opt_key, opt_base);
|
||||
ConfigOptionVectorBase* opt_vector = opt_base->is_vector() ? static_cast<ConfigOptionVectorBase*>(opt_base) : nullptr;
|
||||
if (opt_vector) {
|
||||
if (!existing)
|
||||
// remove the default values
|
||||
opt_vector->clear();
|
||||
// Vector values will be chained. Repeated use of a parameter will append the parameter or parameters
|
||||
// to the end of the value.
|
||||
if (opt_base->type() == coBools && value.empty())
|
||||
static_cast<ConfigOptionBools*>(opt_base)->values.push_back(!no);
|
||||
else
|
||||
// Deserialize any other vector value (ConfigOptionInts, Floats, Percents, Points) the same way
|
||||
// they get deserialized from an .ini file. For ConfigOptionStrings, that means that the C-style unescape
|
||||
// will be applied for values enclosed in quotes, while values non-enclosed in quotes are left to be
|
||||
// unescaped by the calling shell.
|
||||
opt_vector->deserialize(value, true);
|
||||
}
|
||||
else if (opt_base->type() == coBool) {
|
||||
if (value.empty())
|
||||
static_cast<ConfigOptionBool*>(opt_base)->value = !no;
|
||||
else
|
||||
opt_base->deserialize(value);
|
||||
}
|
||||
else if (opt_base->type() == coString) {
|
||||
// Do not unescape single string values, the unescaping is left to the calling shell.
|
||||
static_cast<ConfigOptionString*>(opt_base)->value = value;
|
||||
}
|
||||
else {
|
||||
// Just bail out if the configuration value is not understood.
|
||||
ConfigSubstitutionContext context(ForwardCompatibilitySubstitutionRule::Disable);
|
||||
// Any scalar value of a type different from Bool and String.
|
||||
if (!config->set_deserialize_nothrow(opt_key, value, context, false)) {
|
||||
boost::nowide::cerr << "Invalid value supplied for --" << token.c_str() << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// normalize override options
|
||||
if (!data.overrides_config.empty())
|
||||
data.overrides_config.normalize_fdm();
|
||||
|
||||
if (!data.misc_config.has("config_compatibility")) {
|
||||
// "config_compatibility" can be used during the loading configuration
|
||||
// So, if this option wasn't set, then initialise it from default value
|
||||
const ConfigOptionDef* optdef = cli_misc_config_def.get("config_compatibility");
|
||||
ConfigOption* opt_with_def_value = optdef->create_default_option();
|
||||
if (opt_with_def_value)
|
||||
data.misc_config.set_key_value("config_compatibility", opt_with_def_value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool setup_common()
|
||||
{
|
||||
// Mark the main thread for the debugger and for runtime checks.
|
||||
set_current_thread_name("slic3r_main");
|
||||
// Save the thread ID of the main thread.
|
||||
save_main_thread_id();
|
||||
|
||||
#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);
|
||||
|
||||
// https://github.com/prusa3d/PrusaSlicer/issues/12969
|
||||
::setenv("WEBKIT_DISABLE_COMPOSITING_MODE", "1", /* replace */ false);
|
||||
::setenv("WEBKIT_DISABLE_DMABUF_RENDERER", "1", /* replace */ false);
|
||||
#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)
|
||||
MessageBoxA(NULL, text.c_str(), caption.c_str(), MB_OK | MB_ICONERROR);
|
||||
#endif
|
||||
boost::nowide::cerr << text.c_str() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Detect the operating system flavor after SLIC3R_LOGLEVEL is set.
|
||||
detect_platform();
|
||||
|
||||
#ifdef WIN32
|
||||
// Notify user that a blacklisted DLL was injected into PrusaSlicer process (for example Nahimic, see GH #5573).
|
||||
// We hope that if a DLL is being injected into a PrusaSlicer process, it happens at the very start of the application,
|
||||
// thus we shall detect them now.
|
||||
if (BlacklistedLibraryCheck::get_instance().perform_check()) {
|
||||
std::wstring text = L"Following DLLs have been injected into the PrusaSlicer process:\n\n";
|
||||
text += BlacklistedLibraryCheck::get_instance().get_blacklisted_string();
|
||||
text += L"\n\n"
|
||||
L"PrusaSlicer is known to not run correctly with these DLLs injected. "
|
||||
L"We suggest stopping or uninstalling these services if you experience "
|
||||
L"crashes or unexpected behaviour while using PrusaSlicer.\n"
|
||||
L"For example, ASUS Sonic Studio injects a Nahimic driver, which makes PrusaSlicer "
|
||||
L"to crash on a secondary monitor, see PrusaSlicer github issue #5573";
|
||||
MessageBoxW(NULL, text.c_str(), L"Warning"/*L"Incopatible library found"*/, MB_OK);
|
||||
}
|
||||
#endif
|
||||
|
||||
// See Invoking prusa-slicer from $PATH environment variable crashes #5542
|
||||
// boost::filesystem::path path_to_binary = boost::filesystem::system_complete(argv[0]);
|
||||
boost::filesystem::path path_to_binary = boost::dll::program_location();
|
||||
|
||||
// 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());
|
||||
set_sys_shapes_dir((path_resources / "shapes").string());
|
||||
set_custom_gcodes_dir((path_resources / "custom_gcodes").string());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setup(Data& cli, int argc, char** argv)
|
||||
{
|
||||
if (!setup_common())
|
||||
return false;
|
||||
|
||||
if (!read(cli, argc, argv)) {
|
||||
// Separate error message reported by the CLI parser from the help.
|
||||
boost::nowide::cerr << std::endl;
|
||||
print_help();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cli.misc_config.has("loglevel"))
|
||||
{
|
||||
int loglevel = cli.misc_config.opt_int("loglevel");
|
||||
if (loglevel != 0)
|
||||
set_logging_level(loglevel);
|
||||
}
|
||||
|
||||
if (cli.misc_config.has("threads"))
|
||||
thread_count = cli.misc_config.opt_int("threads");
|
||||
|
||||
set_data_dir(cli.misc_config.has("datadir") ? cli.misc_config.opt_string("datadir") : get_default_datadir());
|
||||
|
||||
#ifdef SLIC3R_GUI
|
||||
if (cli.misc_config.has("webdev")) {
|
||||
Utils::ServiceConfig::instance().set_webdev_enabled(cli.misc_config.opt_bool("webdev"));
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -95,12 +95,36 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/msw/PrusaSlicer.rc.in ${CMAK
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/msw/PrusaSlicer-gcodeviewer.rc.in ${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer-gcodeviewer.rc @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/msw/PrusaSlicer.manifest.in ${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer.manifest @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/osx/Info.plist.in ${CMAKE_CURRENT_BINARY_DIR}/Info.plist @ONLY)
|
||||
if (WIN32)
|
||||
add_library(PrusaSlicer SHARED PrusaSlicer.cpp PrusaSlicer.hpp)
|
||||
else ()
|
||||
add_executable(PrusaSlicer PrusaSlicer.cpp PrusaSlicer.hpp)
|
||||
|
||||
set(SLIC3R_CLI_SOURCES
|
||||
PrusaSlicer.hpp
|
||||
CLI/CLI.hpp
|
||||
CLI/CLI_DynamicPrintConfig.hpp
|
||||
CLI/PrintHelp.cpp
|
||||
CLI/Setup.cpp
|
||||
CLI/LoadPrintData.cpp
|
||||
CLI/ProcessTransform.cpp
|
||||
CLI/ProcessActions.cpp
|
||||
CLI/Run.cpp
|
||||
)
|
||||
if (SLIC3R_GUI)
|
||||
list(APPEND SLIC3R_CLI_SOURCES
|
||||
CLI/GuiParams.cpp
|
||||
)
|
||||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
add_library(PrusaSlicer SHARED PrusaSlicer.cpp ${SLIC3R_CLI_SOURCES})
|
||||
else ()
|
||||
add_executable(PrusaSlicer PrusaSlicer.cpp ${SLIC3R_CLI_SOURCES})
|
||||
endif ()
|
||||
|
||||
foreach(_source IN ITEMS ${SLIC3R_CLI_SOURCES})
|
||||
get_filename_component(_source_path "${_source}" PATH)
|
||||
string(REPLACE "/" "\\" _group_path "${_source_path}")
|
||||
source_group("${_group_path}" FILES "${_source}")
|
||||
endforeach()
|
||||
|
||||
if (MINGW)
|
||||
target_link_options(PrusaSlicer PUBLIC "-Wl,-allow-multiple-definition")
|
||||
set_target_properties(PrusaSlicer PROPERTIES PREFIX "")
|
||||
|
1153
src/PrusaSlicer.cpp
1153
src/PrusaSlicer.cpp
File diff suppressed because it is too large
Load Diff
@ -1,52 +1,9 @@
|
||||
#ifndef SLIC3R_HPP
|
||||
#define SLIC3R_HPP
|
||||
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace IO {
|
||||
enum ExportFormat : int {
|
||||
OBJ,
|
||||
STL,
|
||||
// SVG,
|
||||
TMF,
|
||||
Gcode
|
||||
};
|
||||
}
|
||||
|
||||
class CLI {
|
||||
public:
|
||||
namespace Slic3r::CLI
|
||||
{
|
||||
int run(int argc, char **argv);
|
||||
|
||||
private:
|
||||
DynamicPrintAndCLIConfig m_config;
|
||||
DynamicPrintConfig m_print_config;
|
||||
DynamicPrintConfig m_extra_config;
|
||||
std::vector<std::string> m_input_files;
|
||||
std::vector<std::string> m_actions;
|
||||
std::vector<std::string> m_transforms;
|
||||
std::vector<std::string> m_profiles_sharing;
|
||||
std::vector<Model> m_models;
|
||||
|
||||
bool setup(int argc, char **argv);
|
||||
|
||||
/// Prints usage of the CLI.
|
||||
void print_help(bool include_print_options = false, PrinterTechnology printer_technology = ptAny) const;
|
||||
|
||||
/// Exports loaded models to a file of the specified format, according to the options affecting output filename.
|
||||
bool export_models(IO::ExportFormat format);
|
||||
|
||||
bool has_print_action() const { return m_config.opt_bool("export_gcode") || m_config.opt_bool("export_sla"); }
|
||||
|
||||
bool processed_profiles_sharing();
|
||||
|
||||
bool check_and_load_input_profiles(PrinterTechnology& printer_technology);
|
||||
|
||||
std::string output_filepath(const Model &model, IO::ExportFormat format) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -352,111 +352,6 @@ void ConfigDef::finalize()
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& ConfigDef::print_cli_help(std::ostream& out, bool show_defaults, std::function<bool(const ConfigOptionDef &)> filter) const
|
||||
{
|
||||
// prepare a function for wrapping text
|
||||
auto wrap = [](const std::string& text, size_t line_length) -> std::string {
|
||||
std::istringstream words(text);
|
||||
std::ostringstream wrapped;
|
||||
std::string word;
|
||||
|
||||
if (words >> word) {
|
||||
wrapped << word;
|
||||
size_t space_left = line_length - word.length();
|
||||
while (words >> word) {
|
||||
if (space_left < word.length() + 1) {
|
||||
wrapped << '\n' << word;
|
||||
space_left = line_length - word.length();
|
||||
} else {
|
||||
wrapped << ' ' << word;
|
||||
space_left -= word.length() + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return wrapped.str();
|
||||
};
|
||||
|
||||
// List of opt_keys that should be hidden from the CLI help.
|
||||
const std::vector<std::string> silent_options = { "webdev", "single_instance_on_url" };
|
||||
|
||||
// get the unique categories
|
||||
std::set<std::string> categories;
|
||||
for (const auto& opt : this->options) {
|
||||
const ConfigOptionDef& def = opt.second;
|
||||
if (filter(def))
|
||||
categories.insert(def.category);
|
||||
}
|
||||
|
||||
for (const std::string& category : categories) {
|
||||
if (category != "") {
|
||||
out << category << ":" << std::endl;
|
||||
} else if (categories.size() > 1) {
|
||||
out << "Misc options:" << std::endl;
|
||||
}
|
||||
|
||||
for (const auto& opt : this->options) {
|
||||
const ConfigOptionDef& def = opt.second;
|
||||
if (def.category != category || def.cli == ConfigOptionDef::nocli || !filter(def))
|
||||
continue;
|
||||
|
||||
if (std::find(silent_options.begin(), silent_options.end(), opt.second.opt_key) != silent_options.end())
|
||||
continue;
|
||||
|
||||
// get all possible variations: --foo, --foobar, -f...
|
||||
std::vector<std::string> cli_args = def.cli_args(opt.first);
|
||||
if (cli_args.empty())
|
||||
continue;
|
||||
|
||||
for (auto& arg : cli_args) {
|
||||
arg.insert(0, (arg.size() == 1) ? "-" : "--");
|
||||
if (def.type == coFloat || def.type == coInt || def.type == coFloatOrPercent
|
||||
|| def.type == coFloats || def.type == coInts) {
|
||||
arg += " N";
|
||||
} else if (def.type == coPoint) {
|
||||
arg += " X,Y";
|
||||
} else if (def.type == coPoint3) {
|
||||
arg += " X,Y,Z";
|
||||
} else if (def.type == coString || def.type == coStrings) {
|
||||
arg += " ABCD";
|
||||
}
|
||||
}
|
||||
|
||||
// left: command line options
|
||||
const std::string cli = boost::algorithm::join(cli_args, ", ");
|
||||
out << " " << std::left << std::setw(20) << cli;
|
||||
|
||||
// right: option description
|
||||
std::string descr = def.tooltip;
|
||||
bool show_defaults_this = show_defaults || def.opt_key == "config_compatibility";
|
||||
if (show_defaults_this && def.default_value && def.type != coBool
|
||||
&& (def.type != coString || !def.default_value->serialize().empty())) {
|
||||
descr += " (";
|
||||
if (!def.sidetext.empty()) {
|
||||
descr += def.sidetext + ", ";
|
||||
} else if (def.enum_def && def.enum_def->has_values()) {
|
||||
descr += boost::algorithm::join(def.enum_def->values(), ", ") + "; ";
|
||||
}
|
||||
descr += "default: " + def.default_value->serialize() + ")";
|
||||
}
|
||||
|
||||
// wrap lines of description
|
||||
descr = wrap(descr, 80);
|
||||
std::vector<std::string> lines;
|
||||
boost::split(lines, descr, boost::is_any_of("\n"));
|
||||
|
||||
// if command line options are too long, print description in new line
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
if (i == 0 && cli.size() > 19)
|
||||
out << std::endl;
|
||||
if (i > 0 || cli.size() > 19)
|
||||
out << std::string(21, ' ');
|
||||
out << lines[i] << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string ConfigBase::SetDeserializeItem::format(std::initializer_list<int> values)
|
||||
{
|
||||
std::string out;
|
||||
@ -1220,127 +1115,6 @@ const ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key) co
|
||||
return (it == options.end()) ? nullptr : it->second.get();
|
||||
}
|
||||
|
||||
bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option_keys* extra, t_config_option_keys* keys)
|
||||
{
|
||||
// cache the CLI option => opt_key mapping
|
||||
std::map<std::string,std::string> opts;
|
||||
for (const auto &oit : this->def()->options)
|
||||
for (const std::string &t : oit.second.cli_args(oit.first))
|
||||
opts[t] = oit.first;
|
||||
|
||||
bool parse_options = true;
|
||||
for (int i = 1; i < argc; ++ i) {
|
||||
std::string token = argv[i];
|
||||
// Store non-option arguments in the provided vector.
|
||||
if (! parse_options || ! boost::starts_with(token, "-")) {
|
||||
extra->push_back(token);
|
||||
continue;
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
if (boost::starts_with(token, "-psn_"))
|
||||
// OSX launcher may add a "process serial number", for example "-psn_0_989382" to the command line.
|
||||
// While it is supposed to be dropped since OSX 10.9, we will rather ignore it.
|
||||
continue;
|
||||
#endif /* __APPLE__ */
|
||||
// Stop parsing tokens as options when -- is supplied.
|
||||
if (token == "--") {
|
||||
parse_options = false;
|
||||
continue;
|
||||
}
|
||||
// Remove leading dashes (one or two).
|
||||
token.erase(token.begin(), token.begin() + (boost::starts_with(token, "--") ? 2 : 1));
|
||||
// Read value when supplied in the --key=value form.
|
||||
std::string value;
|
||||
{
|
||||
size_t equals_pos = token.find("=");
|
||||
if (equals_pos != std::string::npos) {
|
||||
value = token.substr(equals_pos+1);
|
||||
token.erase(equals_pos);
|
||||
}
|
||||
}
|
||||
// Look for the cli -> option mapping.
|
||||
auto it = opts.find(token);
|
||||
bool no = false;
|
||||
if (it == opts.end()) {
|
||||
// Remove the "no-" prefix used to negate boolean options.
|
||||
std::string yes_token;
|
||||
if (boost::starts_with(token, "no-")) {
|
||||
yes_token = token.substr(3);
|
||||
it = opts.find(yes_token);
|
||||
no = true;
|
||||
}
|
||||
if (it == opts.end()) {
|
||||
boost::nowide::cerr << "Unknown option --" << token.c_str() << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (no)
|
||||
token = yes_token;
|
||||
}
|
||||
|
||||
const t_config_option_key &opt_key = it->second;
|
||||
const ConfigOptionDef &optdef = *this->option_def(opt_key);
|
||||
|
||||
// If the option type expects a value and it was not already provided,
|
||||
// look for it in the next token.
|
||||
if (value.empty() && optdef.type != coBool && optdef.type != coBools) {
|
||||
if (i == argc-1) {
|
||||
boost::nowide::cerr << "No value supplied for --" << token.c_str() << std::endl;
|
||||
return false;
|
||||
}
|
||||
value = argv[++ i];
|
||||
}
|
||||
|
||||
if (no) {
|
||||
assert(optdef.type == coBool || optdef.type == coBools);
|
||||
if (! value.empty()) {
|
||||
boost::nowide::cerr << "Boolean options negated by the --no- prefix cannot have a value." << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Store the option value.
|
||||
const bool existing = this->has(opt_key);
|
||||
if (keys != nullptr && ! existing) {
|
||||
// Save the order of detected keys.
|
||||
keys->push_back(opt_key);
|
||||
}
|
||||
ConfigOption *opt_base = this->option(opt_key, true);
|
||||
ConfigOptionVectorBase *opt_vector = opt_base->is_vector() ? static_cast<ConfigOptionVectorBase*>(opt_base) : nullptr;
|
||||
if (opt_vector) {
|
||||
if (! existing)
|
||||
// remove the default values
|
||||
opt_vector->clear();
|
||||
// Vector values will be chained. Repeated use of a parameter will append the parameter or parameters
|
||||
// to the end of the value.
|
||||
if (opt_base->type() == coBools && value.empty())
|
||||
static_cast<ConfigOptionBools*>(opt_base)->values.push_back(!no);
|
||||
else
|
||||
// Deserialize any other vector value (ConfigOptionInts, Floats, Percents, Points) the same way
|
||||
// they get deserialized from an .ini file. For ConfigOptionStrings, that means that the C-style unescape
|
||||
// will be applied for values enclosed in quotes, while values non-enclosed in quotes are left to be
|
||||
// unescaped by the calling shell.
|
||||
opt_vector->deserialize(value, true);
|
||||
} else if (opt_base->type() == coBool) {
|
||||
if (value.empty())
|
||||
static_cast<ConfigOptionBool*>(opt_base)->value = !no;
|
||||
else
|
||||
opt_base->deserialize(value);
|
||||
} else if (opt_base->type() == coString) {
|
||||
// Do not unescape single string values, the unescaping is left to the calling shell.
|
||||
static_cast<ConfigOptionString*>(opt_base)->value = value;
|
||||
} else {
|
||||
// Just bail out if the configuration value is not understood.
|
||||
ConfigSubstitutionContext context(ForwardCompatibilitySubstitutionRule::Disable);
|
||||
// Any scalar value of a type different from Bool and String.
|
||||
if (! this->set_deserialize_nothrow(opt_key, value, context, false)) {
|
||||
boost::nowide::cerr << "Invalid value supplied for --" << token.c_str() << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
t_config_option_keys DynamicConfig::keys() const
|
||||
{
|
||||
t_config_option_keys keys;
|
||||
|
@ -2522,11 +2522,6 @@ public:
|
||||
}
|
||||
bool empty() const { return options.empty(); }
|
||||
|
||||
// Iterate through all of the CLI options and write them to a stream.
|
||||
std::ostream& print_cli_help(
|
||||
std::ostream& out, bool show_defaults,
|
||||
std::function<bool(const ConfigOptionDef &)> filter = [](const ConfigOptionDef &){ return true; }) const;
|
||||
|
||||
protected:
|
||||
ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type);
|
||||
ConfigOptionDef* add_nullable(const t_config_option_key &opt_key, ConfigOptionType type);
|
||||
@ -2898,9 +2893,6 @@ public:
|
||||
// Returns options being equal in the two configs, ignoring options not present in both configs.
|
||||
t_config_option_keys equal(const DynamicConfig &other) const;
|
||||
|
||||
// Command line processing
|
||||
bool read_cli(int argc, const char* const argv[], t_config_option_keys* extra, t_config_option_keys* keys = nullptr);
|
||||
|
||||
std::map<t_config_option_key, std::unique_ptr<ConfigOption>>::const_iterator cbegin() const { return options.cbegin(); }
|
||||
std::map<t_config_option_key, std::unique_ptr<ConfigOption>>::const_iterator cend() const { return options.cend(); }
|
||||
size_t size() const { return options.size(); }
|
||||
|
@ -30,6 +30,12 @@
|
||||
|
||||
namespace Slic3r::FileReader
|
||||
{
|
||||
|
||||
bool is_project_file(const std::string& input_file)
|
||||
{
|
||||
return boost::algorithm::iends_with(input_file, ".3mf") || boost::algorithm::iends_with(input_file, ".zip");
|
||||
}
|
||||
|
||||
// Loading model from a file, it may be a simple geometry file as STL or OBJ, however it may be a project file as well.
|
||||
static Model read_model_from_file(const std::string& input_file, LoadAttributes options)
|
||||
{
|
||||
@ -83,15 +89,14 @@ static Model read_all_from_file(const std::string& input_file,
|
||||
boost::optional<Semver> &prusaslicer_generator_version,
|
||||
LoadAttributes options)
|
||||
{
|
||||
const bool is_project_file = boost::algorithm::iends_with(input_file, ".3mf") || boost::algorithm::iends_with(input_file, ".zip");
|
||||
assert(is_project_file);
|
||||
assert(is_project_file(input_file));
|
||||
assert(config != nullptr);
|
||||
assert(config_substitutions != nullptr);
|
||||
|
||||
Model model;
|
||||
|
||||
bool result = false;
|
||||
if (is_project_file)
|
||||
if (is_project_file(input_file))
|
||||
result = load_3mf(input_file.c_str(), *config, *config_substitutions, &model, options & LoadAttribute::CheckVersion, prusaslicer_generator_version);
|
||||
else
|
||||
throw Slic3r::RuntimeError(L("Unknown file format. Input file must have .3mf extension."));
|
||||
|
@ -35,6 +35,8 @@ namespace FileReader
|
||||
bool looks_like_multipart_object { false };
|
||||
};
|
||||
|
||||
bool is_project_file(const std::string& input_file);
|
||||
|
||||
// Load model from input file and return the its mesh.
|
||||
// Throw RuntimeError if some problem was detected during model loading
|
||||
TriangleMesh load_mesh(const std::string& input_file);
|
||||
|
@ -5586,82 +5586,36 @@ PRINT_CONFIG_CACHE_INITIALIZE((
|
||||
SLAMaterialConfig, SLAPrintConfig, SLAPrintObjectConfig, SLAPrinterConfig, SLAFullPrintConfig))
|
||||
static int print_config_static_initialized = print_config_static_initializer();
|
||||
|
||||
CLIInputConfigDef::CLIInputConfigDef()
|
||||
{
|
||||
ConfigOptionDef* def;
|
||||
|
||||
def = this->add("load", coStrings);
|
||||
def->label = L("Load config file");
|
||||
def->tooltip = L("Load configuration from the specified file. It can be used more than once to load options from multiple files.");
|
||||
|
||||
def = this->add("printer-profile", coString);
|
||||
def->label = ("Printer preset name");
|
||||
def->tooltip = ("Name of the printer preset used for slicing.");
|
||||
def->set_default_value(new ConfigOptionString());
|
||||
|
||||
def = this->add("print-profile", coString);
|
||||
def->label = ("Print preset name");
|
||||
def->tooltip = ("Name of the print preset used for slicing.");
|
||||
def->set_default_value(new ConfigOptionString());
|
||||
|
||||
def = this->add("material-profile", coStrings);
|
||||
def->label = ("Material preset name(s)");
|
||||
def->tooltip = ("Name(s) of the material preset(s) used for slicing.\n"
|
||||
"Could be filaments or sla_material preset name(s) depending on printer tochnology");
|
||||
def->set_default_value(new ConfigOptionStrings());
|
||||
}
|
||||
|
||||
CLIActionsConfigDef::CLIActionsConfigDef()
|
||||
{
|
||||
ConfigOptionDef* def;
|
||||
|
||||
// Actions:
|
||||
def = this->add("export_obj", coBool);
|
||||
def->label = L("Export OBJ");
|
||||
def->tooltip = L("Export the model(s) as OBJ.");
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
/*
|
||||
def = this->add("export_svg", coBool);
|
||||
def->label = L("Export SVG");
|
||||
def->tooltip = L("Slice the model and export solid slices as SVG.");
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
*/
|
||||
|
||||
def = this->add("export_sla", coBool);
|
||||
def->label = L("Export SLA");
|
||||
def->tooltip = L("Slice the model and export SLA printing layers as PNG.");
|
||||
def->cli = "export-sla|sla";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("export_3mf", coBool);
|
||||
def->label = L("Export 3MF");
|
||||
def->tooltip = L("Export the model(s) as 3MF.");
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("export_stl", coBool);
|
||||
def->label = L("Export STL");
|
||||
def->tooltip = L("Export the model(s) as STL.");
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("export_gcode", coBool);
|
||||
def->label = L("Export G-code");
|
||||
def->tooltip = L("Slice the model and export toolpaths as G-code.");
|
||||
def->cli = "export-gcode|gcode|g";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("gcodeviewer", coBool);
|
||||
def->label = L("G-code viewer");
|
||||
def->tooltip = L("Visualize an already sliced and saved G-code");
|
||||
def->cli = "gcodeviewer";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("opengl-aa", coBool);
|
||||
def->label = L("Automatic OpenGL antialiasing samples number selection");
|
||||
def->tooltip = L("Automatically select the highest number of samples for OpenGL antialiasing.");
|
||||
def->cli = "opengl-aa";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
#if !SLIC3R_OPENGL_ES
|
||||
def = this->add("opengl-version", coString);
|
||||
def->label = L("OpenGL version");
|
||||
def->tooltip = L("Select a specific version of OpenGL");
|
||||
def->cli = "opengl-version";
|
||||
def->set_default_value(new ConfigOptionString());
|
||||
|
||||
def = this->add("opengl-compatibility", coBool);
|
||||
def->label = L("OpenGL compatibility profile");
|
||||
def->tooltip = L("Enable OpenGL compatibility profile");
|
||||
def->cli = "opengl-compatibility";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("opengl-debug", coBool);
|
||||
def->label = L("OpenGL debug output");
|
||||
def->tooltip = L("Activate OpenGL debug output on graphic cards which support it (OpenGL 4.3 or higher)");
|
||||
def->cli = "opengl-debug";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
#endif // !SLIC3R_OPENGL_ES
|
||||
|
||||
def = this->add("slice", coBool);
|
||||
def->label = L("Slice");
|
||||
def->tooltip = L("Slice the model as FFF or SLA based on the printer_technology configuration value.");
|
||||
def->cli = "slice|s";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
// doesn't need any aditional input
|
||||
|
||||
def = this->add("help", coBool);
|
||||
def->label = L("Help");
|
||||
@ -5679,15 +5633,86 @@ CLIActionsConfigDef::CLIActionsConfigDef()
|
||||
def->tooltip = L("Show the full list of SLA print configuration options.");
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("info", coBool);
|
||||
def->label = L("Output Model Info");
|
||||
def->tooltip = L("Write information about the model to the console.");
|
||||
def = this->add("query-printer-models", coBool);
|
||||
def->label = ("Get list of printer models");
|
||||
def->tooltip = ("Get list of installed printer models into JSON.\n"
|
||||
"Note:\n"
|
||||
"To print printer models for required technology use 'printer-technology' option with value FFF or SLA. By default printer_technology is FFF.\n"
|
||||
"To print out JSON into file use 'output' option.\n"
|
||||
"To specify configuration folder use 'datadir' option.");
|
||||
|
||||
// needs a --printer-profile input
|
||||
|
||||
def = this->add("query-print-filament-profiles", coBool);
|
||||
def->label = ("Get list of print profiles and filament profiles for the selected printer profile");
|
||||
def->tooltip = ("Get list of print profiles and filament profiles for the selected 'printer-profile' into JSON.\n"
|
||||
"Note:\n"
|
||||
"To print out JSON into file use 'output' option.\n"
|
||||
"To specify configuration folder use 'datadir' option.");
|
||||
|
||||
// needs nothing or input just one *.gcode file
|
||||
|
||||
def = this->add("gcodeviewer", coBool);
|
||||
def->label = L("G-code viewer");
|
||||
def->tooltip = L("Visualize an already sliced and saved G-code");
|
||||
def->cli = "gcodeviewer";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
// needs a configuration input
|
||||
|
||||
def = this->add("save", coString);
|
||||
def->label = L("Save config file");
|
||||
def->tooltip = L("Save configuration to the specified file.");
|
||||
def->set_default_value(new ConfigOptionString());
|
||||
|
||||
// needs a model to process this actions
|
||||
|
||||
def = this->add("info", coBool);
|
||||
def->label = L("Output Model Info");
|
||||
def->tooltip = L("Write information about the model to the console.");
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("export_obj", coBool);
|
||||
def->label = L("Export OBJ");
|
||||
def->tooltip = L("Export the model(s) as OBJ.");
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("export_stl", coBool);
|
||||
def->label = L("Export STL");
|
||||
def->tooltip = L("Export the model(s) as STL.");
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
// needs model and configuration
|
||||
|
||||
def = this->add("export_3mf", coBool);
|
||||
def->label = L("Export 3MF");
|
||||
def->tooltip = L("Export the model(s) as 3MF.");
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
//! slice/export_sla/export_gcode is the same action
|
||||
//! May be merged into one action "slice_and_export"
|
||||
|
||||
def = this->add("slice", coBool);
|
||||
def->label = L("Slice");
|
||||
// def->tooltip = L("Slice the model as FFF or SLA based on the printer_technology configuration value.");
|
||||
def->tooltip = L("Slice the model as FFF or SLA based on the printer_technology configuration value "
|
||||
"and export FFF printing toolpaths as G-code or SLA printing layers as PNG.");
|
||||
def->cli = "slice|s";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
/* looks like redundant actions. "slice" is complitely enough
|
||||
def = this->add("export_sla", coBool);
|
||||
def->label = L("Export SLA");
|
||||
def->tooltip = L("Slice the model and export SLA printing layers as PNG.");
|
||||
def->cli = "export-sla|sla";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("export_gcode", coBool);
|
||||
def->label = L("Export G-code");
|
||||
def->tooltip = L("Slice the model and export toolpaths as G-code.");
|
||||
def->cli = "export-gcode|gcode|g";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
*/
|
||||
}
|
||||
|
||||
CLITransformConfigDef::CLITransformConfigDef()
|
||||
@ -5705,23 +5730,6 @@ CLITransformConfigDef::CLITransformConfigDef()
|
||||
def->tooltip = L("Cut model at the given Z.");
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
/*
|
||||
def = this->add("cut_grid", coFloat);
|
||||
def->label = L("Cut");
|
||||
def->tooltip = L("Cut model in the XY plane into tiles of the specified max size.");
|
||||
def->set_default_value(new ConfigOptionPoint());
|
||||
|
||||
def = this->add("cut_x", coFloat);
|
||||
def->label = L("Cut");
|
||||
def->tooltip = L("Cut model at the given X.");
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
def = this->add("cut_y", coFloat);
|
||||
def->label = L("Cut");
|
||||
def->tooltip = L("Cut model at the given Y.");
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
*/
|
||||
|
||||
def = this->add("center", coPoint);
|
||||
def->label = L("Center");
|
||||
def->tooltip = L("Center the print around the given center.");
|
||||
@ -5750,10 +5758,6 @@ CLITransformConfigDef::CLITransformConfigDef()
|
||||
def->tooltip = L("Arrange the supplied models in a plate and merge them in a single model in order to perform actions once.");
|
||||
def->cli = "merge|m";
|
||||
|
||||
def = this->add("repair", coBool);
|
||||
def->label = L("Repair");
|
||||
def->tooltip = L("Try to repair any non-manifold meshes (this option is implicitly added whenever we need to slice the model to perform the requested action).");
|
||||
|
||||
def = this->add("rotate", coFloat);
|
||||
def->label = L("Rotate");
|
||||
def->tooltip = L("Rotation angle around the Z axis in degrees.");
|
||||
@ -5781,11 +5785,7 @@ CLITransformConfigDef::CLITransformConfigDef()
|
||||
def = this->add("scale_to_fit", coPoint3);
|
||||
def->label = L("Scale to Fit");
|
||||
def->tooltip = L("Scale to fit the given volume.");
|
||||
def->set_default_value(new ConfigOptionPoint3(Vec3d(0,0,0)));
|
||||
|
||||
def = this->add("delete-after-load", coString);
|
||||
def->label = L("Delete files after loading");
|
||||
def->tooltip = L("Delete files after loading.");
|
||||
def->set_default_value(new ConfigOptionPoint3(Vec3d(0, 0, 0)));
|
||||
}
|
||||
|
||||
CLIMiscConfigDef::CLIMiscConfigDef()
|
||||
@ -5799,34 +5799,20 @@ CLIMiscConfigDef::CLIMiscConfigDef()
|
||||
def = this->add("config_compatibility", coEnum);
|
||||
def->label = L("Forward-compatibility rule when loading configurations from config files and project files (3MF, AMF).");
|
||||
def->tooltip = L("This version of PrusaSlicer may not understand configurations produced by the newest PrusaSlicer versions. "
|
||||
"For example, newer PrusaSlicer may extend the list of supported firmware flavors. One may decide to "
|
||||
"bail out or to substitute an unknown value with a default silently or verbosely.");
|
||||
"For example, newer PrusaSlicer may extend the list of supported firmware flavors. One may decide to "
|
||||
"bail out or to substitute an unknown value with a default silently or verbosely.");
|
||||
def->set_enum<ForwardCompatibilitySubstitutionRule>({
|
||||
{ "disable", L("Bail out on unknown configuration values") },
|
||||
{ "enable", L("Enable reading unknown configuration values by verbosely substituting them with defaults.") },
|
||||
{ "enable_silent", L("Enable reading unknown configuration values by silently substituting them with defaults.") }
|
||||
});
|
||||
});
|
||||
def->set_default_value(new ConfigOptionEnum<ForwardCompatibilitySubstitutionRule>(ForwardCompatibilitySubstitutionRule::Enable));
|
||||
|
||||
def = this->add("load", coStrings);
|
||||
def->label = L("Load config file");
|
||||
def->tooltip = L("Load configuration from the specified file. It can be used more than once to load options from multiple files.");
|
||||
|
||||
def = this->add("output", coString);
|
||||
def->label = L("Output File");
|
||||
def->tooltip = L("The file where the output will be written (if not specified, it will be based on the input file).");
|
||||
def->cli = "output|o";
|
||||
|
||||
def = this->add("single_instance", coBool);
|
||||
def->label = L("Single instance mode");
|
||||
def->tooltip = L("If enabled, the command line arguments are sent to an existing instance of GUI PrusaSlicer, "
|
||||
"or an existing PrusaSlicer window is activated. "
|
||||
"Overrides the \"single_instance\" configuration value from application preferences.");
|
||||
|
||||
def = this->add("single_instance_on_url", coBool);
|
||||
def->label = "Single instance mode for prusaslicer url"; // Not translated on purpose - for internal use only.
|
||||
def->tooltip = "Works as single_instance but only if prusaslicer url is present.";
|
||||
|
||||
def = this->add("datadir", coString);
|
||||
def->label = L("Data directory");
|
||||
def->tooltip = L("Load and store settings at the given directory. This is useful for maintaining different profiles or including configurations from a network storage.");
|
||||
@ -5839,85 +5825,69 @@ CLIMiscConfigDef::CLIMiscConfigDef()
|
||||
def = this->add("loglevel", coInt);
|
||||
def->label = L("Logging level");
|
||||
def->tooltip = L("Sets logging sensitivity. 0:fatal, 1:error, 2:warning, 3:info, 4:debug, 5:trace\n"
|
||||
"For example. loglevel=2 logs fatal, error and warning level messages.");
|
||||
"For example. loglevel=2 logs fatal, error and warning level messages.");
|
||||
def->min = 0;
|
||||
|
||||
def = this->add("webdev", coBool);
|
||||
def->label = "Enable webdev tools"; // Not translated on purpose - for internal use only.
|
||||
def->tooltip = "Enable webdev tools";
|
||||
#ifdef SLIC3R_GUI
|
||||
def = this->add("opengl-aa", coBool);
|
||||
def->label = L("Automatic OpenGL antialiasing samples number selection");
|
||||
def->tooltip = L("Automatically select the highest number of samples for OpenGL antialiasing.");
|
||||
def->cli = "opengl-aa";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
#if !SLIC3R_OPENGL_ES
|
||||
def = this->add("opengl-version", coString);
|
||||
def->label = L("OpenGL version");
|
||||
def->tooltip = L("Select a specific version of OpenGL");
|
||||
def->cli = "opengl-version";
|
||||
def->set_default_value(new ConfigOptionString());
|
||||
|
||||
def = this->add("opengl-compatibility", coBool);
|
||||
def->label = L("OpenGL compatibility profile");
|
||||
def->tooltip = L("Enable OpenGL compatibility profile");
|
||||
def->cli = "opengl-compatibility";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("opengl-debug", coBool);
|
||||
def->label = L("OpenGL debug output");
|
||||
def->tooltip = L("Activate OpenGL debug output on graphic cards which support it (OpenGL 4.3 or higher)");
|
||||
def->cli = "opengl-debug";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
#endif // !SLIC3R_OPENGL_ES
|
||||
|
||||
def = this->add("single_instance", coBool);
|
||||
def->label = L("Single instance mode");
|
||||
def->tooltip = L("If enabled, the command line arguments are sent to an existing instance of GUI PrusaSlicer, "
|
||||
"or an existing PrusaSlicer window is activated. "
|
||||
"Overrides the \"single_instance\" configuration value from application preferences.");
|
||||
|
||||
def = this->add("delete-after-load", coString);
|
||||
def->label = L("Delete files after loading");
|
||||
def->tooltip = L("Delete files after loading.");
|
||||
|
||||
#if (defined(_MSC_VER) || defined(__MINGW32__)) && defined(SLIC3R_GUI)
|
||||
def = this->add("sw_renderer", coBool);
|
||||
def->label = L("Render with a software renderer");
|
||||
def->tooltip = L("Render with a software renderer. The bundled MESA software renderer is loaded instead of the default OpenGL driver.");
|
||||
def->min = 0;
|
||||
#endif /* _MSC_VER */
|
||||
#endif // _MSC_VER
|
||||
|
||||
def = this->add("printer-profile", coString);
|
||||
def->label = ("Printer preset name");
|
||||
def->tooltip = ("Name of the printer preset used for slicing.");
|
||||
def->set_default_value(new ConfigOptionString());
|
||||
// for internal use only
|
||||
def = this->add("webdev", coBool);
|
||||
def->label = "Enable webdev tools"; // Not translated on purpose - for internal use only.
|
||||
def->tooltip = "Enable webdev tools";
|
||||
|
||||
def = this->add("print-profile", coString);
|
||||
def->label = ("Print preset name");
|
||||
def->tooltip = ("Name of the print preset used for slicing.");
|
||||
def->set_default_value(new ConfigOptionString());
|
||||
def = this->add("single_instance_on_url", coBool);
|
||||
def->label = "Single instance mode for prusaslicer url"; // Not translated on purpose - for internal use only.
|
||||
def->tooltip = "Works as single_instance but only if prusaslicer url is present.";
|
||||
|
||||
def = this->add("material-profile", coStrings);
|
||||
def->label = ("Material preset name(s)");
|
||||
def->tooltip = ("Name(s) of the material preset(s) used for slicing.\n"
|
||||
"Could be filaments or sla_material preset name(s) depending on printer tochnology");
|
||||
def->set_default_value(new ConfigOptionStrings());
|
||||
#endif // SLIC3R_GUI
|
||||
}
|
||||
|
||||
CLIProfilesSharingConfigDef::CLIProfilesSharingConfigDef()
|
||||
{
|
||||
ConfigOptionDef* def;
|
||||
|
||||
// Information from this def will be used just for console output.
|
||||
// So, don't use L marker to label and tooltips values to avoid extract those phrases to translation.
|
||||
|
||||
def = this->add("query-printer-models", coBool);
|
||||
def->label = ("Get list of printer models");
|
||||
def->tooltip = ("Get list of installed printer models into JSON.\n"
|
||||
"Note:\n"
|
||||
"To print printer models for required technology use 'printer-technology' option with value FFF or SLA. By default printer_technology is FFF.\n"
|
||||
"To print out JSON into file use 'output' option.\n"
|
||||
"To specify configuration folder use 'datadir' option.");
|
||||
|
||||
/*
|
||||
def = this->add("query-printer-profiles", coBool);
|
||||
def->label = ("Get list of printer profiles for the selected printer model and printer variant");
|
||||
def->tooltip = ("Get list of printer profiles for the selected 'printer-model' and 'printer-variant' into JSON.\n"
|
||||
"Note:\n"
|
||||
"To print out JSON into file use 'output' option.\n"
|
||||
"To specify configuration folder use 'datadir' option.");
|
||||
*/
|
||||
|
||||
def = this->add("query-print-filament-profiles", coBool);
|
||||
def->label = ("Get list of print profiles and filament profiles for the selected printer profile");
|
||||
def->tooltip = ("Get list of print profiles and filament profiles for the selected 'printer-profile' into JSON.\n"
|
||||
"Note:\n"
|
||||
"To print out JSON into file use 'output' option.\n"
|
||||
"To specify configuration folder use 'datadir' option.");
|
||||
}
|
||||
|
||||
const CLIActionsConfigDef cli_actions_config_def;
|
||||
const CLITransformConfigDef cli_transform_config_def;
|
||||
const CLIMiscConfigDef cli_misc_config_def;
|
||||
const CLIProfilesSharingConfigDef cli_profiles_sharing_config_def;
|
||||
|
||||
DynamicPrintAndCLIConfig::PrintAndCLIConfigDef DynamicPrintAndCLIConfig::s_def;
|
||||
|
||||
void DynamicPrintAndCLIConfig::handle_legacy(t_config_option_key &opt_key, std::string &value) const
|
||||
{
|
||||
if (cli_actions_config_def .options.find(opt_key) == cli_actions_config_def .options.end() &&
|
||||
cli_profiles_sharing_config_def.options.find(opt_key) == cli_profiles_sharing_config_def.options.end() &&
|
||||
cli_transform_config_def.options.find(opt_key) == cli_transform_config_def.options.end() &&
|
||||
cli_misc_config_def .options.find(opt_key) == cli_misc_config_def .options.end()) {
|
||||
PrintConfigDef::handle_legacy(opt_key, value);
|
||||
}
|
||||
}
|
||||
const CLIInputConfigDef cli_input_config_def;
|
||||
const CLIActionsConfigDef cli_actions_config_def;
|
||||
const CLITransformConfigDef cli_transform_config_def;
|
||||
const CLIMiscConfigDef cli_misc_config_def;
|
||||
|
||||
// SlicingStatesConfigDefs
|
||||
|
||||
|
@ -1341,10 +1341,10 @@ public:
|
||||
CLIMiscConfigDef();
|
||||
};
|
||||
|
||||
class CLIProfilesSharingConfigDef : public ConfigDef
|
||||
class CLIInputConfigDef : public ConfigDef
|
||||
{
|
||||
public:
|
||||
CLIProfilesSharingConfigDef();
|
||||
CLIInputConfigDef();
|
||||
};
|
||||
|
||||
typedef std::string t_custom_gcode_key;
|
||||
@ -1409,7 +1409,7 @@ public:
|
||||
};
|
||||
extern const CustomGcodeSpecificConfigDef custom_gcode_specific_config_def;
|
||||
|
||||
// This class defines the command line options representing actions.
|
||||
// This class defines the command line options representing actions including options representing profiles sharing commands.
|
||||
extern const CLIActionsConfigDef cli_actions_config_def;
|
||||
|
||||
// This class defines the command line options representing transforms.
|
||||
@ -1418,42 +1418,8 @@ extern const CLITransformConfigDef cli_transform_config_def;
|
||||
// This class defines all command line options that are not actions or transforms.
|
||||
extern const CLIMiscConfigDef cli_misc_config_def;
|
||||
|
||||
// This class defines the command line options representing profiles sharing commands.
|
||||
extern const CLIProfilesSharingConfigDef cli_profiles_sharing_config_def;
|
||||
|
||||
class DynamicPrintAndCLIConfig : public DynamicPrintConfig
|
||||
{
|
||||
public:
|
||||
DynamicPrintAndCLIConfig() {}
|
||||
DynamicPrintAndCLIConfig(const DynamicPrintAndCLIConfig &other) : DynamicPrintConfig(other) {}
|
||||
|
||||
// Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here.
|
||||
const ConfigDef* def() const override { return &s_def; }
|
||||
|
||||
// Verify whether the opt_key has not been obsoleted or renamed.
|
||||
// Both opt_key and value may be modified by handle_legacy().
|
||||
// If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy().
|
||||
// handle_legacy() is called internally by set_deserialize().
|
||||
void handle_legacy(t_config_option_key &opt_key, std::string &value) const override;
|
||||
|
||||
private:
|
||||
class PrintAndCLIConfigDef : public ConfigDef
|
||||
{
|
||||
public:
|
||||
PrintAndCLIConfigDef() {
|
||||
this->options.insert(print_config_def.options.begin(), print_config_def.options.end());
|
||||
this->options.insert(cli_actions_config_def.options.begin(), cli_actions_config_def.options.end());
|
||||
this->options.insert(cli_transform_config_def.options.begin(), cli_transform_config_def.options.end());
|
||||
this->options.insert(cli_misc_config_def.options.begin(), cli_misc_config_def.options.end());
|
||||
this->options.insert(cli_profiles_sharing_config_def.options.begin(), cli_profiles_sharing_config_def.options.end());
|
||||
for (const auto &kvp : this->options)
|
||||
this->by_serialization_key_ordinal[kvp.second.serialization_key_ordinal] = &kvp.second;
|
||||
}
|
||||
// Do not release the default values, they are handled by print_config_def & cli_actions_config_def / cli_transform_config_def / cli_misc_config_def.
|
||||
~PrintAndCLIConfigDef() { this->options.clear(); }
|
||||
};
|
||||
static PrintAndCLIConfigDef s_def;
|
||||
};
|
||||
// This class defines the command line options representing commands for loading configuration from CLI
|
||||
extern const CLIInputConfigDef cli_input_config_def;
|
||||
|
||||
bool is_XL_printer(const DynamicPrintConfig &cfg);
|
||||
bool is_XL_printer(const PrintConfig &cfg);
|
||||
|
@ -907,7 +907,7 @@ wxGLContext* GUI_App::init_glcontext(wxGLCanvas& canvas)
|
||||
return m_opengl_mgr.init_glcontext(canvas);
|
||||
#else
|
||||
return m_opengl_mgr.init_glcontext(canvas, init_params != nullptr ? init_params->opengl_version : std::make_pair(0, 0),
|
||||
init_params != nullptr ? init_params->opengl_compatibiity_profile : false, init_params != nullptr ? init_params->opengl_debug : false);
|
||||
init_params != nullptr ? init_params->opengl_compatibility_profile : false, init_params != nullptr ? init_params->opengl_debug : false);
|
||||
#endif // SLIC3R_OPENGL_ES
|
||||
}
|
||||
|
||||
|
@ -39,16 +39,16 @@ struct GUI_InitParams
|
||||
std::vector<std::string> input_files;
|
||||
CLISelectedProfiles selected_presets;
|
||||
|
||||
bool start_as_gcodeviewer;
|
||||
bool start_downloader;
|
||||
bool delete_after_load;
|
||||
bool start_as_gcodeviewer { false };
|
||||
bool start_downloader { false };
|
||||
bool delete_after_load { false };
|
||||
std::string download_url;
|
||||
#if !SLIC3R_OPENGL_ES
|
||||
std::pair<int, int> opengl_version;
|
||||
bool opengl_debug;
|
||||
bool opengl_compatibiity_profile;
|
||||
std::pair<int, int> opengl_version { 0, 0 };
|
||||
bool opengl_debug { false };
|
||||
bool opengl_compatibility_profile { false };
|
||||
#endif // !SLIC3R_OPENGL_ES
|
||||
bool opengl_aa;
|
||||
bool opengl_aa { false };
|
||||
};
|
||||
|
||||
int GUI_Run(GUI_InitParams ¶ms);
|
||||
|
Loading…
x
Reference in New Issue
Block a user