update src and test

This commit is contained in:
QIDI TECH
2025-03-22 09:44:19 +08:00
parent b15deeb656
commit 7e7d699e43
151 changed files with 36981 additions and 1531 deletions

73
src/CLI/CLI.hpp Normal file
View 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
}

View 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 };
};
}

144
src/CLI/GuiParams.cpp Normal file
View File

@@ -0,0 +1,144 @@
#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;
}
// are we starting as gcodeviewer ?
if (cli.actions_config.has("gcodeviewer")) {
start_gui = true;
gui_params.start_as_gcodeviewer = true;
}
#ifndef _WIN32
else {
// On Unix systems, the qidi-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 !SLIC3R_OPENGL_ES
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, "qidislicer://")) {
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
View 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/FileReader.hpp"
#include "CLI/CLI.hpp"
#include "CLI/ProfilesSharingUtils.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, "qidislicer://")) {
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> qidislicer_generator_version;
//FIXME should we check the version here? // | Model::LoadAttribute::CheckVersion ?
model = FileReader::load_model_with_config(file, &config, &config_substitutions_ctxt, qidislicer_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
View 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/qiditech/QIDISlicer" << std::endl << std::endl
<< "Usage: qidi-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;
}
}
}

421
src/CLI/ProcessActions.cpp Normal file
View File

@@ -0,0 +1,421 @@
#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 <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 "libslic3r/BuildVolume.hpp"
#include "CLI/CLI.hpp"
#include "CLI/ProfilesSharingUtils.hpp"
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "stb_image_resize2.h"
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 ThumbnailData resize_and_crop(const std::vector<unsigned char>& data, int width, int height, int width_new, int height_new) {
ThumbnailData th;
float scale_x = float(width_new) / width;
float scale_y = float(height_new) / height;
float scale = std::max(scale_x, scale_y); // Choose the larger scale to fill the box
int resized_width = int(width * scale);
int resized_height = int(height * scale);
std::vector<unsigned char> resized_rgba(resized_width * resized_height * 4);
stbir_resize_uint8_linear(data.data(), width, height, 4 * width,
resized_rgba.data(), resized_width, resized_height, 4 * resized_width,
STBIR_RGBA);
th.set(width_new, height_new);
int crop_x = (resized_width - width_new) / 2;
int crop_y = (resized_height - height_new) / 2;
for (int y = 0; y < height_new; ++y) {
std::memcpy(th.pixels.data() + y * width_new * 4,
resized_rgba.data() + ((y + crop_y) * resized_width + crop_x) * 4,
width_new * 4);
}
return th;
}
static std::function<ThumbnailsList(const ThumbnailsParams&)> get_thumbnail_generator_cli(const std::string& filename)
{
if (boost::iends_with(filename, ".3mf")) {
return [filename](const ThumbnailsParams& params) {
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);
}
}
for (const Vec2d& size : params.sizes) {
Point isize(size);
list_out.push_back(resize_and_crop(data, width, height, isize.x(), isize.y()));
}
return list_out;
};
}
return [](const ThumbnailsParams&) ->ThumbnailsList { return {}; };
}
static void update_instances_outside_state(Model& model, const DynamicPrintConfig& config)
{
Pointfs bed_shape = dynamic_cast<const ConfigOptionPoints*>(config.option("bed_shape"))->values;
BuildVolume build_volume(bed_shape, config.opt_float("max_print_height"));
s_multiple_beds.update_build_volume(BoundingBoxf(bed_shape));
model.update_print_volume_state(build_volume);
}
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") || actions.has("export_gcode") || actions.has("export_sla")) {
PrinterTechnology printer_technology = Preset::printer_technology(print_config);
if (actions.has("export_gcode") && printer_technology == ptSLA) {
boost::nowide::cerr << "error: cannot export G-code for an FFF configuration" << std::endl;
return 1;
}
else if (actions.has("export_sla") && printer_technology == ptFFF) {
boost::nowide::cerr << "error: cannot export SLA slices for a SLA configuration" << std::endl;
return 1;
}
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);
}
update_instances_outside_state(model, print_config);
MultipleBedsUtils::with_single_bed_model_fff(model, 0, [&print, &model, &print_config]()
{
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.
const std::string input_file = fff_print.model().objects.empty() ? "" : fff_print.model().objects.front()->input_file;
outfile = fff_print.export_gcode(outfile, nullptr, get_thumbnail_generator_cli(input_file));
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;
}
}

View 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;
}
}

View File

@@ -0,0 +1,584 @@
#include "ProfilesSharingUtils.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/format.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/Utils/DirectoriesUtils.hpp"
#include "libslic3r/Utils/JsonUtils.hpp"
#include "libslic3r/BuildVolume.hpp"
#include <boost/log/trivial.hpp>
namespace Slic3r {
static bool load_preset_bundle_from_datadir(PresetBundle& preset_bundle)
{
AppConfig app_config = AppConfig(AppConfig::EAppMode::Editor);
if (!app_config.exists()) {
BOOST_LOG_TRIVIAL(error) << "Configuration wasn't found. Check your 'datadir' value.";
return false;
}
if (std::string error = app_config.load(); !error.empty()) {
BOOST_LOG_TRIVIAL(error) << Slic3r::format("Error parsing QIDISlicer config file, it is probably corrupted. "
"Try to manually delete the file to recover from the error. Your user profiles will not be affected."
"\n%1%\n%2%", app_config.config_path(), error);
return false;
}
// just checking for existence of Slic3r::data_dir is not enough : it may be an empty directory
// supplied as argument to --datadir; in that case we should still run the wizard
preset_bundle.setup_directories();
std::string delayed_error_load_presets;
// Suppress the '- default -' presets.
preset_bundle.set_default_suppressed(app_config.get_bool("no_defaults"));
try {
auto preset_substitutions = preset_bundle.load_presets(app_config, ForwardCompatibilitySubstitutionRule::EnableSystemSilent);
if (!preset_substitutions.empty()) {
BOOST_LOG_TRIVIAL(error) << "Some substitutions are found during loading presets.";
return false;
}
// Post-process vendor map to delete non-installed models/varians
VendorMap& vendors = preset_bundle.vendors;
for (auto& [vendor_id, vendor_profile] : vendors) {
std::vector<VendorProfile::PrinterModel> models;
for (auto& printer_model : vendor_profile.models) {
std::vector<VendorProfile::PrinterVariant> variants;
for (const auto& variant : printer_model.variants) {
// check if printer model with variant is intalled
if (app_config.get_variant(vendor_id, printer_model.id, variant.name))
variants.push_back(variant);
}
if (!variants.empty()) {
if (printer_model.variants.size() != variants.size())
printer_model.variants = variants;
models.push_back(printer_model);
}
}
if (!models.empty()) {
if (vendor_profile.models.size() != models.size())
vendor_profile.models = models;
}
}
}
catch (const std::exception& ex) {
BOOST_LOG_TRIVIAL(error) << ex.what();
return false;
}
return true;
}
namespace pt = boost::property_tree;
/*
struct PrinterAttr_
{
std::string model_name;
std::string variant;
};
static std::string get_printer_profiles(const VendorProfile* vendor_profile,
const PresetBundle* preset_bundle,
const PrinterAttr_& printer_attr)
{
for (const auto& printer_model : vendor_profile->models) {
if (printer_model.name != printer_attr.model_name)
continue;
for (const auto& variant : printer_model.variants)
if (variant.name == printer_attr.variant)
{
pt::ptree data_node;
data_node.put("printer_model", printer_model.name);
data_node.put("printer_variant", printer_attr.variant);
pt::ptree printer_profiles_node;
for (const Preset& printer_preset : preset_bundle->printers) {
if (printer_preset.vendor->id == vendor_profile->id &&
printer_preset.is_visible && // ???
printer_preset.config.opt_string("printer_model") == printer_model.id &&
printer_preset.config.opt_string("printer_variant") == printer_attr.variant) {
pt::ptree profile_node;
profile_node.put("", printer_preset.name);
printer_profiles_node.push_back(std::make_pair("", profile_node));
}
}
data_node.add_child("printer_profiles", printer_profiles_node);
// Serialize the tree into JSON and return it.
return write_json_with_post_process(data_node);
}
}
return "";
}
std::string get_json_printer_profiles(const std::string& printer_model_name, const std::string& printer_variant)
{
if (!is_datadir())
return "";
PrinterAttr_ printer_attr({printer_model_name, printer_variant});
PresetBundle preset_bundle;
if (!load_preset_bundle_from_datadir(preset_bundle))
return "";
const VendorMap& vendors = preset_bundle.vendors;
for (const auto& [vendor_id, vendor] : vendors) {
std::string out = get_printer_profiles(&vendor, &preset_bundle, printer_attr);
if (!out.empty())
return out;
}
return "";
}
*/
struct PrinterAttr
{
std::string vendor_id;
std::string model_id;
std::string variant_name;
};
static bool is_compatible_preset(const Preset& printer_preset, const PrinterAttr& attr)
{
return printer_preset.vendor->id == attr.vendor_id &&
printer_preset.config.opt_string("printer_model") == attr.model_id &&
printer_preset.config.opt_string("printer_variant") == attr.variant_name;
}
static void add_profile_node(pt::ptree& printer_profiles_node, const Preset& printer_preset)
{
pt::ptree profile_node;
const DynamicPrintConfig& config = printer_preset.config;
int extruders_cnt = printer_preset.printer_technology() == ptSLA ? 0 :
config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
profile_node.put("name", printer_preset.name);
if (extruders_cnt > 0)
profile_node.put("extruders_cnt", extruders_cnt);
const double max_print_height = config.opt_float("max_print_height");
const ConfigOptionPoints& bed_shape = *config.option<ConfigOptionPoints>("bed_shape");
BuildVolume build_volume = BuildVolume { bed_shape.values, max_print_height };
BoundingBoxf bb = build_volume.bounding_volume2d();
Vec2d origin_pt;
if (build_volume.type() == BuildVolume::Type::Circle) {
origin_pt = build_volume.bed_center();
}
else {
origin_pt = to_2d(-1 * build_volume.bounding_volume().min);
}
std::string origin = Slic3r::format("[%1%, %2%]", is_approx(origin_pt.x(), 0.) ? 0 : origin_pt.x(),
is_approx(origin_pt.y(), 0.) ? 0 : origin_pt.y());
pt::ptree bed_node;
bed_node.put("type", build_volume.type_name());
bed_node.put("width", bb.max.x() - bb.min.x());
bed_node.put("height", bb.max.y() - bb.min.y());
bed_node.put("origin", origin);
bed_node.put("max_print_height", max_print_height);
profile_node.add_child("bed", bed_node);
printer_profiles_node.push_back(std::make_pair("", profile_node));
}
static void get_printer_profiles_node(pt::ptree& printer_profiles_node,
pt::ptree& user_printer_profiles_node,
const PrinterPresetCollection& printer_presets,
const PrinterAttr& attr)
{
printer_profiles_node.clear();
user_printer_profiles_node.clear();
for (const Preset& printer_preset : printer_presets) {
if (!printer_preset.is_visible)
continue;
if (printer_preset.is_user()) {
const Preset* parent_preset = printer_presets.get_preset_parent(printer_preset);
if (parent_preset && is_compatible_preset(*parent_preset, attr))
add_profile_node(user_printer_profiles_node, printer_preset);
}
else if (is_compatible_preset(printer_preset, attr))
add_profile_node(printer_profiles_node, printer_preset);
}
}
static void add_printer_models(pt::ptree& vendor_node,
const VendorProfile* vendor_profile,
PrinterTechnology printer_technology,
const PrinterPresetCollection& printer_presets)
{
for (const auto& printer_model : vendor_profile->models) {
if (printer_technology != ptUnknown && printer_model.technology != printer_technology)
continue;
pt::ptree variants_node;
pt::ptree printer_profiles_node;
pt::ptree user_printer_profiles_node;
if (printer_model.technology == ptSLA) {
PrinterAttr attr({ vendor_profile->id, printer_model.id, "default" });
get_printer_profiles_node(printer_profiles_node, user_printer_profiles_node, printer_presets, attr);
if (printer_profiles_node.empty() && user_printer_profiles_node.empty())
continue;
}
else {
for (const auto& variant : printer_model.variants) {
PrinterAttr attr({ vendor_profile->id, printer_model.id, variant.name });
get_printer_profiles_node(printer_profiles_node, user_printer_profiles_node, printer_presets, attr);
if (printer_profiles_node.empty() && user_printer_profiles_node.empty())
continue;
pt::ptree variant_node;
variant_node.put("name", variant.name);
variant_node.add_child("printer_profiles", printer_profiles_node);
if (!user_printer_profiles_node.empty())
variant_node.add_child("user_printer_profiles", user_printer_profiles_node);
variants_node.push_back(std::make_pair("", variant_node));
}
if (variants_node.empty())
continue;
}
pt::ptree data_node;
data_node.put("id", printer_model.id);
data_node.put("name", printer_model.name);
data_node.put("technology", printer_model.technology == ptFFF ? "FFF" : "SLA");
if (!variants_node.empty())
data_node.add_child("variants", variants_node);
else {
data_node.add_child("printer_profiles", printer_profiles_node);
if (!user_printer_profiles_node.empty())
data_node.add_child("user_printer_profiles", user_printer_profiles_node);
}
data_node.put("vendor_name", vendor_profile->name);
data_node.put("vendor_id", vendor_profile->id);
vendor_node.push_back(std::make_pair("", data_node));
}
}
static void add_undef_printer_models(pt::ptree& vendor_node,
PrinterTechnology printer_technology,
const PrinterPresetCollection& printer_presets)
{
for (auto pt : { ptFFF, ptSLA }) {
if (printer_technology != ptUnknown && printer_technology != pt)
continue;
pt::ptree printer_profiles_node;
for (const Preset& preset : printer_presets) {
if (!preset.is_visible || preset.printer_technology() != pt ||
preset.vendor || printer_presets.get_preset_parent(preset))
continue;
add_profile_node(printer_profiles_node, preset);
}
if (!printer_profiles_node.empty()) {
pt::ptree data_node;
data_node.put("id", "");
data_node.put("technology", pt == ptFFF ? "FFF" : "SLA");
data_node.add_child("printer_profiles", printer_profiles_node);
data_node.put("vendor_name", "");
data_node.put("vendor_id", "");
vendor_node.push_back(std::make_pair("", data_node));
}
}
}
std::string get_json_printer_models(PrinterTechnology printer_technology)
{
PresetBundle preset_bundle;
if (!load_preset_bundle_from_datadir(preset_bundle))
return "";
pt::ptree vendor_node;
const VendorMap& vendors_map = preset_bundle.vendors;
for (const auto& [vendor_id, vendor] : vendors_map)
add_printer_models(vendor_node, &vendor, printer_technology, preset_bundle.printers);
// add printers with no vendor information
add_undef_printer_models(vendor_node, printer_technology, preset_bundle.printers);
pt::ptree root;
root.add_child("printer_models", vendor_node);
// Serialize the tree into JSON and return it.
return write_json_with_post_process(root);
}
static std::string get_installed_print_and_filament_profiles(const PresetBundle* preset_bundle, const Preset* printer_preset)
{
PrinterTechnology printer_technology = printer_preset->printer_technology();
pt::ptree print_profiles;
pt::ptree user_print_profiles;
const PresetWithVendorProfile printer_preset_with_vendor_profile = preset_bundle->printers.get_preset_with_vendor_profile(*printer_preset);
const PresetCollection& print_presets = printer_technology == ptFFF ? preset_bundle->prints : preset_bundle->sla_prints;
const PresetCollection& material_presets = printer_technology == ptFFF ? preset_bundle->filaments : preset_bundle->sla_materials;
const std::string material_node_name = printer_technology == ptFFF ? "filament_profiles" : "sla_material_profiles";
for (auto print_preset : print_presets) {
const PresetWithVendorProfile print_preset_with_vendor_profile = print_presets.get_preset_with_vendor_profile(print_preset);
if (is_compatible_with_printer(print_preset_with_vendor_profile, printer_preset_with_vendor_profile))
{
pt::ptree materials_profile_node;
pt::ptree user_materials_profile_node;
for (auto material_preset : material_presets) {
// ?! check visible and no-template presets only
if (!material_preset.is_visible || (material_preset.vendor && material_preset.vendor->templates_profile))
continue;
const PresetWithVendorProfile material_preset_with_vendor_profile = material_presets.get_preset_with_vendor_profile(material_preset);
if (is_compatible_with_printer(material_preset_with_vendor_profile, printer_preset_with_vendor_profile) &&
is_compatible_with_print(material_preset_with_vendor_profile, print_preset_with_vendor_profile, printer_preset_with_vendor_profile)) {
pt::ptree material_node;
material_node.put("", material_preset.name);
if (material_preset.is_user())
user_materials_profile_node.push_back(std::make_pair("", material_node));
else
materials_profile_node.push_back(std::make_pair("", material_node));
}
}
pt::ptree print_profile_node;
print_profile_node.put("name", print_preset.name);
print_profile_node.add_child(material_node_name, materials_profile_node);
if (!user_materials_profile_node.empty())
print_profile_node.add_child("user_" + material_node_name, user_materials_profile_node);
if (print_preset.is_user())
user_print_profiles.push_back(std::make_pair("", print_profile_node));
else
print_profiles.push_back(std::make_pair("", print_profile_node));
}
}
if (print_profiles.empty() && user_print_profiles.empty())
return "";
pt::ptree tree;
tree.put("printer_profile", printer_preset->name);
tree.add_child("print_profiles", print_profiles);
if (!user_print_profiles.empty())
tree.add_child("user_print_profiles", user_print_profiles);
// Serialize the tree into JSON and return it.
return write_json_with_post_process(tree);
}
std::string get_json_print_filament_profiles(const std::string& printer_profile)
{
PresetBundle preset_bundle;
if (load_preset_bundle_from_datadir(preset_bundle)) {
const Preset* preset = preset_bundle.printers.find_preset(printer_profile, false, false);
if (preset)
return get_installed_print_and_filament_profiles(&preset_bundle, preset);
}
return "";
}
// Helper function for FS
bool load_full_print_config(const std::string& print_preset_name, const std::string& filament_preset_name, const std::string& printer_preset_name, DynamicPrintConfig& config)
{
PresetBundle preset_bundle;
if (!load_preset_bundle_from_datadir(preset_bundle)){
BOOST_LOG_TRIVIAL(error) << Slic3r::format("Failed to load data from the datadir '%1%'.", data_dir());
return false;
}
config = {};
config.apply(FullPrintConfig::defaults());
bool is_failed{ false };
if (const Preset* print_preset = preset_bundle.prints.find_preset(print_preset_name))
config.apply_only(print_preset->config, print_preset->config.keys());
else {
BOOST_LOG_TRIVIAL(warning) << Slic3r::format("Print profile '%1%' wasn't found.", print_preset_name);
is_failed |= true;
}
if (const Preset* filament_preset = preset_bundle.filaments.find_preset(filament_preset_name))
config.apply_only(filament_preset->config, filament_preset->config.keys());
else {
BOOST_LOG_TRIVIAL(warning) << Slic3r::format("Filament profile '%1%' wasn't found.", filament_preset_name);
is_failed |= true;
}
if (const Preset* printer_preset = preset_bundle.printers.find_preset(printer_preset_name))
config.apply_only(printer_preset->config, printer_preset->config.keys());
else {
BOOST_LOG_TRIVIAL(warning) << Slic3r::format("Printer profile '%1%' wasn't found.", printer_preset_name);
is_failed |= true;
}
return !is_failed;
}
// Helper function for load full config from installed presets by profile names
std::string load_full_print_config(const std::string& print_preset_name,
const std::vector<std::string>& material_preset_names_in,
const std::string& printer_preset_name,
DynamicPrintConfig& config,
PrinterTechnology printer_technology /*= ptUnknown*/)
{
// check entered profile names
if (print_preset_name.empty() ||
material_preset_names_in.empty() ||
printer_preset_name.empty())
return "Request is not completed. All of Print/Material/Printer profiles have to be entered";
// check preset bundle
PresetBundle preset_bundle;
if (!load_preset_bundle_from_datadir(preset_bundle))
return Slic3r::format("Failed to load data from the datadir '%1%'.", data_dir());
// check existance of required profiles
std::string errors;
const Preset* printer_preset = preset_bundle.printers.find_preset(printer_preset_name);
if (!printer_preset)
errors += "\n" + Slic3r::format("Printer profile '%1%' wasn't found.", printer_preset_name);
else if (printer_technology == ptUnknown)
printer_technology = printer_preset->printer_technology();
else if (printer_technology != printer_preset->printer_technology())
errors += "\n" + std::string("Printer technology of the selected printer preset is differs with required printer technology");
PresetCollection& print_presets = printer_technology == ptFFF ? preset_bundle.prints : preset_bundle.sla_prints;
const Preset* print_preset = print_presets.find_preset(print_preset_name);
if (!print_preset)
errors += "\n" + Slic3r::format("Print profile '%1%' wasn't found.", print_preset_name);
PresetCollection& material_presets = printer_technology == ptFFF ? preset_bundle.filaments : preset_bundle.sla_materials;
auto check_material = [&material_presets] (const std::string& name, std::string& errors) -> void {
const Preset* material_preset = material_presets.find_preset(name);
if (!material_preset)
errors += "\n" + Slic3r::format("Material profile '%1%' wasn't found.", name);
};
check_material(material_preset_names_in.front(), errors);
if (material_preset_names_in.size() > 1) {
for (size_t idx = 1; idx < material_preset_names_in.size(); idx++) {
if (material_preset_names_in[idx] != material_preset_names_in.front())
check_material(material_preset_names_in[idx], errors);
}
}
if (!errors.empty())
return errors;
// check and update list of material presets
std::vector<std::string> material_preset_names = material_preset_names_in;
if (printer_technology == ptSLA && material_preset_names.size() > 1) {
BOOST_LOG_TRIVIAL(warning) << "Note: More than one sla material profiles were entered. Extras material profiles will be ignored.";
material_preset_names.resize(1);
}
if (printer_technology == ptFFF) {
const int extruders_count = int(static_cast<const ConfigOptionFloats*>(printer_preset->config.option("nozzle_diameter"))->values.size());
if (extruders_count > int(material_preset_names.size())) {
BOOST_LOG_TRIVIAL(warning) << "Note: Less than needed filament profiles were entered. Missed filament profiles will be filled with first material.";
material_preset_names.reserve(extruders_count);
for (int i = extruders_count - material_preset_names.size(); i > 0; i--)
material_preset_names.push_back(material_preset_names.front());
}
else if (extruders_count < int(material_preset_names.size())) {
BOOST_LOG_TRIVIAL(warning) << "Note: More than needed filament profiles were entered. Extras filament profiles will be ignored.";
material_preset_names.resize(extruders_count);
}
}
// check profiles compatibility
const PresetWithVendorProfile printer_preset_with_vendor_profile = preset_bundle.printers.get_preset_with_vendor_profile(*printer_preset);
const PresetWithVendorProfile print_preset_with_vendor_profile = print_presets.get_preset_with_vendor_profile(*print_preset);
if (!is_compatible_with_printer(print_preset_with_vendor_profile, printer_preset_with_vendor_profile))
errors += "\n" + Slic3r::format("Print profile '%1%' is not compatible with printer profile %2%.", print_preset_name, printer_preset_name);
auto check_material_preset_compatibility = [&material_presets, printer_preset_name, print_preset_name, printer_preset_with_vendor_profile, print_preset_with_vendor_profile]
(const std::string& name, std::string& errors) -> void {
const Preset* material_preset = material_presets.find_preset(name);
const PresetWithVendorProfile material_preset_with_vendor_profile = material_presets.get_preset_with_vendor_profile(*material_preset);
if (!is_compatible_with_printer(material_preset_with_vendor_profile, printer_preset_with_vendor_profile))
errors += "\n" + Slic3r::format("Material profile '%1%' is not compatible with printer profile %2%.", name, printer_preset_name);
if (!is_compatible_with_print(material_preset_with_vendor_profile, print_preset_with_vendor_profile, printer_preset_with_vendor_profile))
errors += "\n" + Slic3r::format("Material profile '%1%' is not compatible with print profile %2%.", name, print_preset_name);
};
check_material_preset_compatibility(material_preset_names.front(), errors);
if (material_preset_names.size() > 1) {
for (size_t idx = 1; idx < material_preset_names.size(); idx++) {
if (material_preset_names[idx] != material_preset_names.front())
check_material_preset_compatibility(material_preset_names[idx], errors);
}
}
if (!errors.empty())
return errors;
// get full print configuration
preset_bundle.printers.select_preset_by_name(printer_preset_name, true);
print_presets.select_preset_by_name(print_preset_name, true);
if (printer_technology == ptSLA)
material_presets.select_preset_by_name(material_preset_names.front(), true);
else if (printer_technology == ptFFF) {
auto& extruders_filaments = preset_bundle.extruders_filaments;
extruders_filaments.clear();
for (size_t i = 0; i < material_preset_names.size(); ++i)
extruders_filaments.emplace_back(ExtruderFilaments(&preset_bundle.filaments, i, material_preset_names[i]));
if (extruders_filaments.size() == 1)
preset_bundle.filaments.select_preset_by_name(material_preset_names[0], false);
}
config = preset_bundle.full_config();
return "";
}
} // namespace Slic3r

View File

@@ -0,0 +1,29 @@
#ifndef slic3r_ProfilesSharingUtils_hpp_
#define slic3r_ProfilesSharingUtils_hpp_
#include <string>
#include "libslic3r/Config.hpp"
namespace Slic3r {
std::string get_json_printer_models(PrinterTechnology printer_technology);
//std::string get_json_printer_profiles(const std::string& printer_model, const std::string& printer_variant);
std::string get_json_print_filament_profiles(const std::string& printer_profile);
class DynamicPrintConfig;
bool load_full_print_config(const std::string& print_preset, const std::string& filament_preset, const std::string& printer_preset, DynamicPrintConfig& out_config);
// Load full print config into config
// Return value is always error string if any exists
// Note, that all appearing warnings are added into BOOST_LOG
// When printer_technology is set, then it will be compared with printer technology of the printer_profile and return the error, when they aren't the same
std::string load_full_print_config( const std::string& print_preset_name,
const std::vector<std::string>& material_preset_names,
const std::string& printer_preset_name,
DynamicPrintConfig& config,
PrinterTechnology printer_technology = ptUnknown);
} // namespace Slic3r
#endif // slic3r_ProfilesSharingUtils_hpp_

54
src/CLI/Run.cpp Normal file
View File

@@ -0,0 +1,54 @@
#include "../QIDISlicer.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;
}
}

337
src/CLI/Setup.cpp Normal file
View File

@@ -0,0 +1,337 @@
#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);
::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
if (BlacklistedLibraryCheck::get_instance().perform_check()) {
std::wstring text = L"Following DLLs have been injected into the QIDISlicer process:\n\n";
text += BlacklistedLibraryCheck::get_instance().get_blacklisted_string();
text += L"\n\n"
L"QIDISlicer 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 QIDISlicer.\n"
L"For example, ASUS Sonic Studio injects a Nahimic driver, which makes QIDISlicer "
L"to crash on a secondary monitor, see QIDISlicer github issue #5573";
MessageBoxW(NULL, text.c_str(), L"Warning"/*L"Incopatible library found"*/, MB_OK);
}
#endif
// See Invoking qidi-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;
}
}