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

View File

@@ -18,10 +18,12 @@ if (SLIC3R_ENABLE_FORMAT_STEP)
add_subdirectory(occt_wrapper)
endif ()
add_subdirectory(slic3r-arrange)
add_subdirectory(slic3r-arrange-wrapper)
add_subdirectory(libseqarrange)
if (SLIC3R_GUI)
add_subdirectory(libvgcode)
add_subdirectory(slic3r-arrange)
add_subdirectory(slic3r-arrange-wrapper)
if(WIN32)
message(STATUS "WXWIN environment set to: $ENV{WXWIN}")
@@ -95,12 +97,38 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/msw/QIDISlicer.rc.in ${CMAKE
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/msw/QIDISlicer-gcodeviewer.rc.in ${CMAKE_CURRENT_BINARY_DIR}/QIDISlicer-gcodeviewer.rc @ONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/msw/QIDISlicer.manifest.in ${CMAKE_CURRENT_BINARY_DIR}/QIDISlicer.manifest @ONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/osx/Info.plist.in ${CMAKE_CURRENT_BINARY_DIR}/Info.plist @ONLY)
if (WIN32)
add_library(QIDISlicer SHARED QIDISlicer.cpp QIDISlicer.hpp)
else ()
add_executable(QIDISlicer QIDISlicer.cpp QIDISlicer.hpp)
set(SLIC3R_CLI_SOURCES
QIDISlicer.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
CLI/ProfilesSharingUtils.cpp
CLI/ProfilesSharingUtils.hpp
)
if (SLIC3R_GUI)
list(APPEND SLIC3R_CLI_SOURCES
CLI/GuiParams.cpp
)
endif ()
if (WIN32)
add_library(QIDISlicer SHARED QIDISlicer.cpp ${SLIC3R_CLI_SOURCES})
else ()
add_executable(QIDISlicer QIDISlicer.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(QIDISlicer PUBLIC "-Wl,-allow-multiple-definition")
set_target_properties(QIDISlicer PROPERTIES PREFIX "")
@@ -111,39 +139,38 @@ if (NOT WIN32 AND NOT APPLE)
set_target_properties(QIDISlicer PROPERTIES OUTPUT_NAME "qidi-slicer")
endif ()
target_link_libraries(QIDISlicer libslic3r libcereal slic3r-arrange-wrapper)
target_link_libraries(QIDISlicer PRIVATE libslic3r libcereal slic3r-arrange-wrapper libseqarrange stb_image)
if (APPLE)
# add_compile_options(-stdlib=libc++)
# add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE)
# -liconv: boost links to libiconv by default
target_link_libraries(QIDISlicer "-liconv -framework IOKit" "-framework CoreFoundation" -lc++)
target_link_libraries(QIDISlicer PRIVATE "-liconv -framework IOKit" "-framework CoreFoundation" -lc++)
elseif (MSVC)
# Manifest is provided through QIDISlicer.rc, don't generate your own.
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:NO")
else ()
# Boost on Raspberry-Pi does not link to pthreads explicitely.
target_link_libraries(QIDISlicer ${CMAKE_DL_LIBS} -lstdc++ Threads::Threads)
target_link_libraries(QIDISlicer PRIVATE ${CMAKE_DL_LIBS} -lstdc++ Threads::Threads)
endif ()
# Add the Slic3r GUI library, libcurl, OpenGL and GLU libraries.
if (SLIC3R_GUI)
# target_link_libraries(QIDISlicer ws2_32 uxtheme setupapi libslic3r_gui ${wxWidgets_LIBRARIES})
target_link_libraries(QIDISlicer libslic3r_gui)
target_link_libraries(QIDISlicer PRIVATE libslic3r_gui)
if (MSVC)
# Generate debug symbols even in release mode.
target_link_options(QIDISlicer PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>")
target_link_libraries(QIDISlicer user32.lib Setupapi.lib)
target_link_libraries(QIDISlicer PRIVATE user32.lib Setupapi.lib)
elseif (MINGW)
target_link_libraries(QIDISlicer ws2_32 uxtheme setupapi)
target_link_libraries(QIDISlicer PRIVATE ws2_32 uxtheme setupapi)
elseif (APPLE)
target_link_libraries(QIDISlicer "-framework OpenGL")
target_link_libraries(QIDISlicer PRIVATE "-framework OpenGL")
else ()
target_link_libraries(QIDISlicer -ldl)
target_link_libraries(QIDISlicer PRIVATE -ldl)
endif ()
if (WIN32)
find_library(PSAPI_LIB NAMES Psapi)
target_link_libraries(QIDISlicer ${PSAPI_LIB})
target_link_libraries(QIDISlicer PRIVATE ${PSAPI_LIB})
endif ()
endif ()

File diff suppressed because it is too large Load Diff

View File

@@ -1,51 +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

View File

@@ -0,0 +1,35 @@
find_package(Z3 REQUIRED)
slic3r_remap_configs("z3::libz3" RelWithDebInfo Release)
add_library(libseqarrange STATIC src/seq_interface.cpp src/seq_preprocess.cpp src/seq_sequential.cpp src/seq_utilities.cpp)
target_include_directories(libseqarrange PUBLIC include PRIVATE src )
target_link_libraries(libseqarrange PUBLIC libslic3r PRIVATE z3::libz3)
add_executable(sequential_decimator src/sequential_decimator.cpp)
target_include_directories(sequential_decimator PRIVATE include)
target_link_libraries(sequential_decimator PRIVATE libseqarrange)
if (SLIC3R_BUILD_TESTS)
find_package(Catch2 3.8 REQUIRED)
add_executable(libseqarrange_tests test/qidiparts.cpp test/seq_test_polygon.cpp test/seq_test_sequential.cpp test/seq_test_preprocess.cpp test/seq_test_interface.cpp)
target_include_directories(libseqarrange_tests PRIVATE src )
target_link_libraries(libseqarrange_tests PRIVATE Catch2::Catch2WithMain libseqarrange)
set(_catch_args "exclude:[NotWorking] exclude:[Slow]")
list(APPEND _catch_args "${CATCH_EXTRA_ARGS}")
add_test(NAME libseqarrange_tests
COMMAND libseqarrange_tests ${_catch_args}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
endif()

View File

@@ -0,0 +1,204 @@
#ifndef __SEQ_INTERFACE_HPP__
#define __SEQ_INTERFACE_HPP__
/*----------------------------------------------------------------*/
#include "libslic3r/Polygon.hpp"
#include "libslic3r/Geometry/ConvexHull.hpp"
/*----------------------------------------------------------------*/
using namespace Slic3r;
/*----------------------------------------------------------------*/
namespace Sequential
{
class ObjectTooLargeException : public std::runtime_error { public: explicit ObjectTooLargeException(const std::string& msg) : std::runtime_error(msg) {}};
/*----------------------------------------------------------------*/
struct PrinterGeometry
{
// must be convex; for best performance a rectangle is recommended
Slic3r::Polygon plate;
// at least height 0 (corresponding to nozzle) must be present in convex_heights
std::set<coord_t> convex_heights;
std::set<coord_t> box_heights;
// <height, polygons at given height>, at least one polygon must be present for height 0
std::map<coord_t, std::vector<Slic3r::Polygon> > extruder_slices;
bool convert_Geometry2PlateBounds(Slic3r::BoundingBox &plate_bounding_box, Slic3r::Polygon &plate_bounding_polygon) const;
};
/*----------------------------------------------------------------*/
enum DecimationPrecision
{
SEQ_DECIMATION_PRECISION_UNDEFINED,
SEQ_DECIMATION_PRECISION_LOW,
SEQ_DECIMATION_PRECISION_HIGH
};
/*----------------------------------------------------------------*/
struct SolverConfiguration
{
SolverConfiguration();
SolverConfiguration(const PrinterGeometry &printer_geometry);
void set_DecimationPrecision(DecimationPrecision decimation_precision);
void set_ObjectGroupSize(int object_group_size);
void setup(const PrinterGeometry &printer_geometry);
static double convert_DecimationPrecision2Tolerance(DecimationPrecision decimation_precision);
int bounding_box_size_optimization_step;
int minimum_bounding_box_size;
Slic3r::BoundingBox plate_bounding_box;
Slic3r::Polygon plate_bounding_polygon;
int max_refines;
int object_group_size;
int fixed_object_grouping_limit;
int temporal_spread;
DecimationPrecision decimation_precision;
std::string optimization_timeout;
};
/*----------------------------------------------------------------*/
struct ObjectToPrint
{
int id = 0;
bool glued_to_next = false; /* the next object must be scheduled right after this object */
coord_t total_height = 0;
std::vector<std::pair<coord_t, Slic3r::Polygon>> pgns_at_height;
};
struct ScheduledObject {
ScheduledObject(int _id, coord_t _x, coord_t _y)
: id(_id)
, x(_x)
, y(_y) { /* */ }
int id = 0;
coord_t x, y;
};
struct ScheduledPlate {
std::vector<ScheduledObject> scheduled_objects;
};
/*----------------------------------------------------------------*/
/*
This is the recommended interface for checking sequential printability.
Returns true if objects are sequentially printable according to their
ordering in the input vector and the arrangement on the plate specified
by the schedule. Printable means that the extruder never hits printed
objects during printing. Otherwise returns false.
Please see the corresponding example of usage (seq_test_interface.cpp)
Note: The function always succeeds, does not throw any exception.
*/
bool check_ScheduledObjectsForSequentialPrintability(const SolverConfiguration &solver_configuration,
const PrinterGeometry &printer_geometry,
const std::vector<ObjectToPrint> &objects_to_print,
const std::vector<ScheduledPlate> &scheduled_plates);
/*
This is a variant of the interface for checking sequential printability.
If not sequentially printable returns a pair of object IDs that are in conflict,
that is, when the second object is printed the extruder will collide with the
first object. The returned conflict is not necessarily the first collision to
occur when printing the object according to the given input schedule.
Note: The function always succeeds, does not throw any exception.
*/
std::optional<std::pair<int, int> > check_ScheduledObjectsForSequentialConflict(const SolverConfiguration &solver_configuration,
const PrinterGeometry &printer_geometry,
const std::vector<ObjectToPrint> &objects_to_print,
const std::vector<ScheduledPlate> &scheduled_plates);
/*----------------------------------------------------------------*/
/*
This is the recommended interface for sequential scheduling/arranging.
Please see the corresponding example of usage (seq_test_interface.cpp)
Note: The function should succeed except the case when there is an
object that does not fit on the plate and in the case when the solver
is unable to scedule even single object on the plate. The latter case
is detected by timeout and should not normally happen. These failures
are reported via exceptions.
The trans_bed_glue parameter should be set to false when scheduling
all objects. If only objects on a separate bed are scheduled, then
trans_bed_glue should be set to true when there is an object on the
previous bed that is temporally glued to the first scheduled object.
In such a case, the first object will be scheduled as first temporally.
*/
std::vector<ScheduledPlate> schedule_ObjectsForSequentialPrint(const SolverConfiguration &solver_configuration,
const PrinterGeometry &printer_geometry,
const std::vector<ObjectToPrint> &objects_to_print,
std::function<void(int)> progress_callback = [](int progress){});
void schedule_ObjectsForSequentialPrint(const SolverConfiguration &solver_configuration,
const PrinterGeometry &printer_geometry,
const std::vector<ObjectToPrint> &objects_to_print,
std::vector<ScheduledPlate> &scheduled_plates,
std::function<void(int)> progress_callback = [](int progress){});
/*----------------------------------------------------------------*/
/*
The following interface is for more internal use.
*/
int schedule_ObjectsForSequentialPrint(const SolverConfiguration &solver_configuration,
const std::vector<ObjectToPrint> &objects_to_print,
std::vector<ScheduledPlate> &scheduled_plates,
std::function<void(int)> progress_callback = [](int progress){});
void setup_ExtruderUnreachableZones(const SolverConfiguration &solver_configuration,
std::vector<std::vector<Slic3r::Polygon> > &convex_unreachable_zones,
std::vector<std::vector<Slic3r::Polygon> > &box_unreachable_zones);
int schedule_ObjectsForSequentialPrint(const SolverConfiguration &solver_configuration,
const std::vector<ObjectToPrint> &objects_to_print,
const std::vector<std::vector<Slic3r::Polygon> > &convex_unreachable_zones,
const std::vector<std::vector<Slic3r::Polygon> > &box_unreachable_zones,
std::vector<ScheduledPlate> &scheduled_plates,
std::function<void(int)> progress_callback = [](int progress){});
/*----------------------------------------------------------------*/
} // namespace Sequential
/*----------------------------------------------------------------*/
#endif /* __SEQ_INTERFACE_HPP__ */

View File

@@ -0,0 +1,73 @@
/*================================================================*/
/*
* Author: Pavel Surynek, 2023 - 2025
*
* File: seq_defs.h
*
* Definitions of useful macros.
*/
/*================================================================*/
#ifndef __SEQ_DEFS_HPP__
#define __SEQ_DEFS_HPP__
/*----------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <set>
/*----------------------------------------------------------------*/
using namespace std;
#define SEQ_UNUSED(x)
//#define DEBUG
//#define PROFILE
typedef wchar_t wchar;
typedef std::basic_string<char> string;
typedef std::vector<string> strings_vector;
typedef std::set<string> strings_set;
/*----------------------------------------------------------------*/
extern const string INDENT;
/*----------------------------------------------------------------*/
#define MIN(x,y) (((x) < (y)) ? (x) : (y))
#define MAX(x,y) (((x) > (y)) ? (x) : (y))
#define DFR(x,y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y)))
#define ABS(x) (((x) < 0) ? -(x) : (x))
#define SGN(x) (((x) < 0) ? -(-1) : ((x) > 0) ? 1 : 0)
/*----------------------------------------------------------------*/
#ifdef DEBUG
#define ASSERT(condition) \
{ \
if (!(condition)) \
{ \
printf("ASSERT: assertion failed (file: %s, line:%d).\n", __FILE__, __LINE__); \
fflush(NULL); \
exit(-1); \
} \
}
#else
#define ASSERT(condition)
#endif /* DEBUG */
/*----------------------------------------------------------------*/
#endif /* __SEQ_DEFS_HPP__ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,223 @@
#ifndef __SEQ_PREPROCESS_HPP__
#define __SEQ_PREPROCESS_HPP__
/*----------------------------------------------------------------*/
#include "seq_sequential.hpp"
/*----------------------------------------------------------------*/
namespace Sequential
{
/*----------------------------------------------------------------*/
const coord_t SEQ_SLICER_SCALE_FACTOR = 100000;
const double SEQ_POLYGON_DECIMATION_GROW_FACTOR = 1.005;
/*----------------------------------------------------------------*/
struct ObjectToPrint;
/*----------------------------------------------------------------*/
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_NOZZLE_LEVEL_MK3S;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_EXTRUDER_LEVEL_MK3S;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_HOSE_LEVEL_MK3S;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_GANTRY_LEVEL_MK3S;
extern const std::vector<std::vector<Slic3r::Polygon> > SEQ_UNREACHABLE_POLYGON_ALL_LEVELS_MK3S;
extern const std::vector<std::vector<Slic3r::Polygon> > SEQ_UNREACHABLE_POLYGON_CONVEX_LEVELS_MK3S;
extern const std::vector<std::vector<Slic3r::Polygon> > SEQ_UNREACHABLE_POLYGON_BOX_LEVELS_MK3S;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_NOZZLE_LEVEL_MK4;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_EXTRUDER_LEVEL_MK4;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_HOSE_LEVEL_MK4;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_GANTRY_LEVEL_MK4;
extern const std::vector<std::vector<Slic3r::Polygon> > SEQ_UNREACHABLE_POLYGON_ALL_LEVELS_MK4;
extern const std::vector<std::vector<Slic3r::Polygon> > SEQ_UNREACHABLE_POLYGON_CONVEX_LEVELS_MK4;
extern const std::vector<std::vector<Slic3r::Polygon> > SEQ_UNREACHABLE_POLYGON_BOX_LEVELS_MK4;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_NOZZLE_LEVEL_XL;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_EXTRUDER_LEVEL_XL;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_HOSE_LEVEL_XL;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_GANTRY_LEVEL_XL;
extern const std::vector<std::vector<Slic3r::Polygon> > SEQ_UNREACHABLE_POLYGON_ALL_LEVELS_XL;
extern const std::vector<std::vector<Slic3r::Polygon> > SEQ_UNREACHABLE_POLYGON_CONVEX_LEVELS_XL;
extern const std::vector<std::vector<Slic3r::Polygon> > SEQ_UNREACHABLE_POLYGON_BOX_LEVELS_XL;
/*----------------------------------------------------------------*/
Rational scaleDown_CoordinateForSequentialSolver(coord_t x);
void scaleDown_PolygonForSequentialSolver(const Slic3r::Polygon &polygon,
Slic3r::Polygon &scaled_polygon);
void scaleDown_PolygonForSequentialSolver(coord_t scale_factor,
const Slic3r::Polygon &polygon,
Slic3r::Polygon &scaled_polygon);
Slic3r::Polygon scaleDown_PolygonForSequentialSolver(coord_t scale_factor, const Slic3r::Polygon &polygon);
void scaleUp_PositionForSlicer(const Rational &position_X,
const Rational &position_Y,
coord_t &scaled_position_X,
coord_t &scaled_position_Y);
void scaleUp_PositionForSlicer(coord_t scale_factor,
const Rational &position_X,
const Rational &position_Y,
coord_t &scaled_position_X,
coord_t &scaled_position_Y);
void scaleUp_PositionForSlicer(double position_X,
double position_Y,
coord_t &scaled_position_X,
coord_t &scaled_position_Y);
void scaleUp_PositionForSlicer(coord_t scale_factor,
double position_X,
double position_Y,
coord_t &scaled_position_X,
coord_t &scaled_position_Y);
Slic3r::Polygon scaleUp_PolygonForSlicer(const Slic3r::Polygon &polygon);
Slic3r::Polygon scaleUp_PolygonForSlicer(coord_t scale_factor, const Slic3r::Polygon &polygon);
Slic3r::Polygon scaleUp_PolygonForSlicer(const Slic3r::Polygon &polygon, double x_pos, double y_pos);
Slic3r::Polygon scaleUp_PolygonForSlicer(coord_t scale_factor, const Slic3r::Polygon &polygon, double x_pos, double y_pos);
void ground_PolygonByBoundingBox(Slic3r::Polygon &polygon);
void ground_PolygonByFirstPoint(Slic3r::Polygon &polygon);
void shift_Polygon(Slic3r::Polygon &polygon, coord_t x_offset, coord_t y_offset);
void shift_Polygon(Slic3r::Polygon &polygon, const Slic3r::Point &offset);
/*----------------------------------------------------------------*/
Slic3r::Polygon transform_UpsideDown(const SolverConfiguration &solver_configuration, const Slic3r::Polygon &polygon);
Slic3r::Polygon transform_UpsideDown(const SolverConfiguration &solver_configuration, coord_t scale_factor, const Slic3r::Polygon &polygon);
void transform_UpsideDown(const SolverConfiguration &solver_configuration,
const coord_t &scaled_x_pos,
const coord_t &scaled_y_pos,
coord_t &transformed_x_pos,
coord_t &transformed_y_pos);
void transform_UpsideDown(const SolverConfiguration &solver_configuration,
coord_t scale_factor,
const coord_t &scaled_x_pos,
const coord_t &scaled_y_pos,
coord_t &transformed_x_pos,
coord_t &transformed_y_pos);
/*----------------------------------------------------------------*/
void grow_PolygonForContainedness(coord_t center_x, coord_t center_y, Slic3r::Polygon &polygon);
void decimate_PolygonForSequentialSolver(const SolverConfiguration &solver_configuration,
const Slic3r::Polygon &polygon,
Slic3r::Polygon &scale_down_polygon,
bool extra_safety);
void decimate_PolygonForSequentialSolver(double DP_tolerance,
const Slic3r::Polygon &polygon,
Slic3r::Polygon &decimated_polygon,
bool extra_safety);
void extend_PolygonConvexUnreachableZone(const SolverConfiguration &solver_configuration,
const Slic3r::Polygon &polygon,
const std::vector<Slic3r::Polygon> &extruder_polygons,
std::vector<Slic3r::Polygon> &unreachable_polygons);
void extend_PolygonBoxUnreachableZone(const SolverConfiguration &solver_configuration,
const Slic3r::Polygon &polygon,
const std::vector<Slic3r::Polygon> &extruder_polygons,
std::vector<Slic3r::Polygon> &unreachable_polygons);
void extend_PolygonBoxUnreachableZone(const SolverConfiguration &solver_configuration,
const Slic3r::Polygon &polygon,
const std::vector<Slic3r::Polygon> &extruder_polygons,
std::vector<Slic3r::Polygon> &unreachable_polygons);
void prepare_ExtruderPolygons(const SolverConfiguration &solver_configuration,
const PrinterGeometry &printer_geometry,
const ObjectToPrint &object_to_print,
std::vector<Slic3r::Polygon> &convex_level_polygons,
std::vector<Slic3r::Polygon> &box_level_polygons,
std::vector<std::vector<Slic3r::Polygon> > &extruder_convex_level_polygons,
std::vector<std::vector<Slic3r::Polygon> > &extruder_box_level_polygons,
bool extra_safety);
void prepare_ObjectPolygons(const SolverConfiguration &solver_configuration,
const std::vector<Slic3r::Polygon> &convex_level_polygons,
const std::vector<Slic3r::Polygon> &box_level_polygons,
const std::vector<std::vector<Slic3r::Polygon> > &extruder_convex_level_polygons,
const std::vector<std::vector<Slic3r::Polygon> > &extruder_box_level_polygons,
Slic3r::Polygon &object_polygon,
std::vector<Slic3r::Polygon> &unreachable_polygons);
void prepare_UnreachableZonePolygons(const SolverConfiguration &solver_configuration,
const Slic3r::Polygon &polygon,
const std::vector<std::vector<Slic3r::Polygon> > &extruder_convex_level_polygons,
const std::vector<std::vector<Slic3r::Polygon> > &extruder_box_level_polygons,
std::vector<Slic3r::Polygon> &unreachable_polygons);
void prepare_UnreachableZonePolygons(const SolverConfiguration &solver_configuration,
const std::vector<Slic3r::Polygon> &convex_level_polygons,
const std::vector<Slic3r::Polygon> &box_level_polygons,
const std::vector<std::vector<Slic3r::Polygon> > &extruder_convex_level_polygons,
const std::vector<std::vector<Slic3r::Polygon> > &extruder_box_level_polygons,
std::vector<Slic3r::Polygon> &unreachable_polygons);
bool check_PolygonSizeFitToPlate(const SolverConfiguration &solver_configuration, const Slic3r::Polygon &polygon);
bool check_PolygonPositionWithinPlate(const SolverConfiguration &solver_configuration, coord_t x, coord_t y, const Slic3r::Polygon &polygon);
bool check_PolygonSizeFitToPlate(const SolverConfiguration &solver_configuration, coord_t scale_factor, const Slic3r::Polygon &polygon);
bool check_PolygonPositionWithinPlate(const SolverConfiguration &solver_configuration, coord_t scale_factor, coord_t x, coord_t y, const Slic3r::Polygon &polygon);
/*----------------------------------------------------------------*/
bool check_PolygonConsumation(const std::vector<Slic3r::Polygon> &polygons, const std::vector<Slic3r::Polygon> &consumer_polygons);
std::vector<std::vector<Slic3r::Polygon> > simplify_UnreachableZonePolygons(const std::vector<std::vector<Slic3r::Polygon> > &unreachable_polygons);
void glue_LowObjects(std::vector<SolvableObject> &solvable_ojects);
/*----------------------------------------------------------------*/
double calc_PolygonArea(const Slic3r::Polygon &polygon);
double calc_PolygonUnreachableZoneArea(const std::vector<Slic3r::Polygon> &unreachable_polygons);
double calc_PolygonUnreachableZoneArea(const Slic3r::Polygon &polygon,
const std::vector<Slic3r::Polygon> &unreachable_polygons);
double calc_PolygonArea(const std::vector<Slic3r::Polygon> &polygons);
double calc_PolygonArea(const std::vector<int> &fixed,
const std::vector<int> &undecided,
const std::vector<Slic3r::Polygon> &polygons);
double calc_PolygonUnreachableZoneArea(const std::vector<Slic3r::Polygon> &polygons,
const std::vector<std::vector<Slic3r::Polygon> > &unreachable_polygons);
/*----------------------------------------------------------------*/
} // namespace Sequential
/*----------------------------------------------------------------*/
#endif /* __SEQ_PREPROCESS_HPP__ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,235 @@
#include <sstream>
#include <iostream>
#include <fstream>
#include "seq_defs.hpp"
#include "libslic3r/Geometry.hpp"
#include "libslic3r/ClipperUtils.hpp"
#include "seq_utilities.hpp"
#include "seq_preprocess.hpp"
/*----------------------------------------------------------------*/
using namespace std;
using namespace Slic3r;
/*----------------------------------------------------------------*/
namespace Sequential
{
/*----------------------------------------------------------------*/
bool find_and_remove(std::string &src, const std::string &key)
{
size_t pos = src.find(key);
if (pos != std::string::npos)
{
src.erase(pos, key.length());
return true;
}
return false;
}
std::vector<ObjectToPrint> load_exported_data_from_file(const std::string &filename)
{
std::ifstream in(filename);
if (!in)
{
throw std::runtime_error("NO EXPORTED FILE WAS FOUND");
}
return load_exported_data_from_stream(in);
}
std::vector<ObjectToPrint> load_exported_data_from_text(const std::string &data_text)
{
std::istringstream iss(data_text);
return load_exported_data_from_stream(iss);
}
std::vector<ObjectToPrint> load_exported_data_from_stream(std::istream &data_stream)
{
std::vector<ObjectToPrint> objects_to_print;
std::string line;
while (data_stream)
{
std::getline(data_stream, line);
if (find_and_remove(line, "OBJECT_ID")) {
objects_to_print.push_back(ObjectToPrint());
objects_to_print.back().id = std::stoi(line);
}
if (find_and_remove(line, "TOTAL_HEIGHT"))
{
objects_to_print.back().total_height = std::stoi(line);
}
if (find_and_remove(line, "POLYGON_AT_HEIGHT"))
{
objects_to_print.back().pgns_at_height.emplace_back(std::make_pair(std::stoi(line), Polygon()));
}
if (find_and_remove(line, "POINT"))
{
std::stringstream ss(line);
std::string val;
ss >> val;
Point pt(std::stoi(val), 0);
ss >> val;
pt.y() = std::stoi(val);
objects_to_print.back().pgns_at_height.back().second.append(pt);
}
}
return objects_to_print;
}
int load_printer_geometry_from_file(const std::string &filename, PrinterGeometry &printer_geometry)
{
std::ifstream in(filename);
if (!in)
{
throw std::runtime_error("NO PRINTER GEOMETRY FILE WAS FOUND");
}
return load_printer_geometry_from_stream(in, printer_geometry);
}
int load_printer_geometry_from_text(const std::string &geometry_text, PrinterGeometry &printer_geometry)
{
std::istringstream iss(geometry_text);
return load_printer_geometry_from_stream(iss, printer_geometry);
}
int load_printer_geometry_from_stream(std::istream &geometry_stream, PrinterGeometry &printer_geometry)
{
Polygon *current_polygon = NULL;
std::string line;
coord_t x_size = -1;
coord_t y_size = -1;
while (geometry_stream)
{
std::getline(geometry_stream, line);
if (find_and_remove(line, "POLYGON_AT_HEIGHT"))
{
coord_t height = std::stoi(line);
std::map<coord_t, std::vector<Polygon> >::iterator extruder_slice = printer_geometry.extruder_slices.find(height);
if (extruder_slice != printer_geometry.extruder_slices.end())
{
extruder_slice->second.push_back(Polygon());
current_polygon = &extruder_slice->second.back();
}
else
{
vector<Polygon> polygons;
polygons.push_back(Polygon());
current_polygon = &printer_geometry.extruder_slices.insert(std::pair(height, polygons)).first->second.back();
}
}
else if (find_and_remove(line, "POINT"))
{
std::stringstream ss(line);
std::string val;
ss >> val;
Point pt(std::stoi(val), 0);
ss >> val;
pt.y() = std::stoi(val);
assert(current_polygon != NULL);
current_polygon->append(pt);
}
else if (find_and_remove(line, "CONVEX_HEIGHT"))
{
std::stringstream ss(line);
std::string val;
ss >> val;
coord_t height = std::stoi(val);
printer_geometry.convex_heights.insert(height);
}
else if (find_and_remove(line, "BOX_HEIGHT"))
{
std::stringstream ss(line);
std::string val;
ss >> val;
coord_t height = std::stoi(val);
printer_geometry.box_heights.insert(height);
}
else if (find_and_remove(line, "X_SIZE"))
{
std::stringstream ss(line);
std::string val;
ss >> val;
x_size = std::stoi(val);
}
else if (find_and_remove(line, "Y_SIZE"))
{
std::stringstream ss(line);
std::string val;
ss >> val;
y_size = std::stoi(val);
}
}
assert(x_size > 0 && y_size > 0);
printer_geometry.plate = { {0, 0}, {x_size, 0}, {x_size, y_size}, {0, y_size} };
return 0;
}
void save_import_data_to_file(const std::string &filename,
const std::map<double, int> &scheduled_polygons,
const map<int, int> &original_index_map,
const vector<Rational> &poly_positions_X,
const vector<Rational> &poly_positions_Y)
{
std::ofstream out(filename);
if (!out)
{
throw std::runtime_error("CANNOT CREATE IMPORT FILE");
}
for (const auto& scheduled_polygon: scheduled_polygons)
{
coord_t X, Y;
scaleUp_PositionForSlicer(poly_positions_X[scheduled_polygon.second],
poly_positions_Y[scheduled_polygon.second],
X,
Y);
const auto& original_index = original_index_map.find(scheduled_polygon.second);
out << original_index->second << " " << X << " " << Y << endl;
}
}
/*----------------------------------------------------------------*/
} // namespace Sequential

View File

@@ -0,0 +1,41 @@
#ifndef __SEQ_UTILITIES_HPP__
#define __SEQ_UTILITIES_HPP__
/*----------------------------------------------------------------*/
#include "seq_sequential.hpp"
#include "libseqarrange/seq_interface.hpp"
/*----------------------------------------------------------------*/
namespace Sequential
{
bool find_and_remove(std::string &src, const std::string &key);
std::vector<ObjectToPrint> load_exported_data_from_file(const std::string &filename);
std::vector<ObjectToPrint> load_exported_data_from_text(const std::string &data_text);
std::vector<ObjectToPrint> load_exported_data_from_stream(std::istream &data_stream);
int load_printer_geometry_from_file(const std::string& filename, PrinterGeometry &printer_geometry);
int load_printer_geometry_from_text(const std::string& geometry_text, PrinterGeometry &printer_geometry);
int load_printer_geometry_from_stream(std::istream& geometry_stream, PrinterGeometry &printer_geometry);
void save_import_data_to_file(const std::string &filename,
const std::map<double, int> &scheduled_polygons,
const map<int, int> &original_index_map,
const vector<Rational> &poly_positions_X,
const vector<Rational> &poly_positions_Y);
/*----------------------------------------------------------------*/
} // namespace Sequential
/*----------------------------------------------------------------*/
#endif /* __SEQ_UTILITIES_HPP__ */

View File

@@ -0,0 +1,415 @@
#include <sstream>
#include <iostream>
#include <fstream>
#include "libslic3r/Polygon.hpp"
#include "libslic3r/Geometry/ConvexHull.hpp"
#include "libslic3r/SVG.hpp"
#include "seq_utilities.hpp"
#include "sequential_decimator.hpp"
/*----------------------------------------------------------------*/
using namespace Slic3r;
using namespace Sequential;
/*----------------------------------------------------------------*/
#define SCALE_FACTOR 50000.0
/*----------------------------------------------------------------*/
//const Point polygon_offset_0(28000000, -16000000); // body
//const Point polygon_offset_1(-3000000, -60000000); // hose
//const Point polygon_offset_2(28000000, -16000000); // fan
//const Point polygon_offset_3(0,-24000000); // gantry
//const Point polygon_offset_4(0,0); // nozzle
const int SEQ_QIDI_MK3S_X_SIZE = 2500;
const int SEQ_QIDI_MK3S_Y_SIZE = 2100;
/*----------------------------------------------------------------*/
void print_IntroductoryMessage(void)
{
printf("----------------------------------------------------------------\n");
printf("Polygon decimation utility\n");
printf("(C) 2024 QIDI Tech \n");
printf("================================================================\n");
}
void print_ConcludingMessage(void)
{
printf("----------------------------------------------------------------\n");
}
void print_Help(void)
{
printf("Usage:\n");
printf("sequential_decimator [--input-file=<string>]\n");
printf(" [--output-file=<string>]\n");
printf(" [--tolerance=<double>]\n");
printf(" [--x-pos=<double> (in mm)]\n");
printf(" [--y-pos=<double> (in mm)]\n");
printf(" [--x-nozzle=<int> (in coord_t)]\n");
printf(" [--y-nozzle=<int> (in coord_t)]\n");
printf(" [--help]\n");
printf("\n");
printf("\n");
printf("Defaults: --input-file=arrange_data_export.txt\n");
printf(" --output-file=arrange_data_import.txt\n");
printf(" --x-pos='random'\n");
printf(" --y-pos='random'\n");
printf(" --x-nozzle=0\n");
printf(" --y-nozzle=0\n");
printf(" --tolerance=400000 \n");
printf("\n");
}
int parse_CommandLineParameter(const string &parameter, CommandParameters &command_parameters)
{
if (parameter.find("--input-file=") == 0)
{
command_parameters.input_filename = parameter.substr(13, parameter.size());
}
else if (parameter.find("--output-file=") == 0)
{
command_parameters.output_filename = parameter.substr(14, parameter.size());
}
else if (parameter.find("--tolerance=") == 0)
{
command_parameters.tolerance = std::atof(parameter.substr(12, parameter.size()).c_str());
}
else if (parameter.find("--x-pos=") == 0)
{
command_parameters.x_position = std::atof(parameter.substr(8, parameter.size()).c_str());
command_parameters.random_position = false;
}
else if (parameter.find("--y-pos=") == 0)
{
command_parameters.y_position = std::atof(parameter.substr(8, parameter.size()).c_str());
command_parameters.random_position = false;
}
else if (parameter.find("--x-nozzle=") == 0)
{
command_parameters.x_nozzle = std::atoi(parameter.substr(11, parameter.size()).c_str());
}
else if (parameter.find("--y-nozzle=") == 0)
{
command_parameters.y_nozzle = std::atoi(parameter.substr(11, parameter.size()).c_str());
}
else if (parameter.find("--help") == 0)
{
command_parameters.help = true;
}
else
{
return -1;
}
return 0;
}
void save_DecimatedPolygons(const CommandParameters &command_parameters,
const std::vector<Slic3r::Polygon> &decimated_polygons)
{
std::ofstream out(command_parameters.output_filename);
if (!out)
throw std::runtime_error("CANNOT CREATE OUTPUT FILE");
Point nozzle_offset(-command_parameters.x_nozzle, -command_parameters.y_nozzle);
for (unsigned int i = 0; i < decimated_polygons.size(); ++i)
{
out << "[" << i << "]" << endl;
out << "{" << endl;
Slic3r::Polygon shift_polygon = scaleUp_PolygonForSlicer(1,
decimated_polygons[i],
(command_parameters.x_position * SEQ_SLICER_SCALE_FACTOR) * 10,
(command_parameters.y_position * SEQ_SLICER_SCALE_FACTOR) * 10);
shift_Polygon(shift_polygon, nozzle_offset);
for (const auto& point: shift_polygon.points)
{
out << " { " << point.x() << ", " << point.y() << "}," << endl;
}
out << "}" << endl;
}
}
int decimate_Polygons(const CommandParameters &command_parameters)
{
clock_t start, finish;
printf("Decimation ...\n");
start = clock();
SolverConfiguration solver_configuration;
std::vector<ObjectToPrint> objects_to_print = load_exported_data_from_file(command_parameters.input_filename);
std::vector<Slic3r::Polygon> decimated_polygons;
std::vector<std::vector<Slic3r::Polygon> > unreachable_polygons;
printf(" Decimating objects (polygons) ...\n");
for (unsigned int i = 0; i < objects_to_print.size(); ++i)
{
for (unsigned int j = 0; j < objects_to_print[i].pgns_at_height.size(); ++j)
{
//coord_t height = objects_to_print[i].pgns_at_height[j].first;
if (!objects_to_print[i].pgns_at_height[j].second.points.empty())
{
Polygon decimated_polygon;
//ground_PolygonByFirstPoint(objects_to_print[i].pgns_at_height[j].second);
decimate_PolygonForSequentialSolver(command_parameters.tolerance,
objects_to_print[i].pgns_at_height[j].second,
decimated_polygon,
false);
decimated_polygons.push_back(decimated_polygon);
}
}
}
printf(" Decimating objects (polygons) ... finished\n");
Point nozzle_offset(-command_parameters.x_nozzle, -command_parameters.y_nozzle);
for (unsigned int i = 0; i < decimated_polygons.size(); ++i)
{
printf(" [%d]\n", i);
Slic3r::Polygon shift_polygon = decimated_polygons[i];
shift_Polygon(shift_polygon, nozzle_offset);
shift_polygon = scaleUp_PolygonForSlicer(1,
shift_polygon,
(command_parameters.x_position * SEQ_SLICER_SCALE_FACTOR) * 10,
(command_parameters.y_position * SEQ_SLICER_SCALE_FACTOR) * 10);
for (const auto &point: shift_polygon.points)
{
cout << " " << point.x() << " " << point.y() << endl;
}
BoundingBox bounding_box = get_extents(shift_polygon);
cout << " BB" << endl;
cout << " " << bounding_box.min.x() << " " << bounding_box.min.y() << endl;
cout << " " << bounding_box.max.x() << " " << bounding_box.max.y() << endl;
cout << endl;
}
if (command_parameters.output_filename != "")
{
save_DecimatedPolygons(command_parameters, decimated_polygons);
}
string svg_filename = "sequential_decimator.svg";
SVG preview_svg(svg_filename);
solver_configuration.plate_bounding_box = BoundingBox({0,0}, {SEQ_QIDI_MK3S_X_SIZE, SEQ_QIDI_MK3S_Y_SIZE});
printf(" Generating output SVG ...\n");
for (unsigned int i = 0; i < decimated_polygons.size(); ++i)
{
Polygon transformed_polygon;
Polygon shift_polygon = decimated_polygons[i];
shift_Polygon(shift_polygon, nozzle_offset);
if (command_parameters.random_position)
{
transformed_polygon = transform_UpsideDown(solver_configuration,
scaleUp_PolygonForSlicer(1,
shift_polygon,
(solver_configuration.plate_bounding_box.min.x() + rand() % (solver_configuration.plate_bounding_box.max.x() - solver_configuration.plate_bounding_box.min.x())) * SEQ_SLICER_SCALE_FACTOR,
(solver_configuration.plate_bounding_box.min.y() + rand() % (solver_configuration.plate_bounding_box.max.y() - solver_configuration.plate_bounding_box.min.y()) * SEQ_SLICER_SCALE_FACTOR)));
}
else
{
transformed_polygon = transform_UpsideDown(solver_configuration,
scaleUp_PolygonForSlicer(1,
shift_polygon,
(command_parameters.x_position * SEQ_SLICER_SCALE_FACTOR) * 10,
(command_parameters.y_position * SEQ_SLICER_SCALE_FACTOR) * 10));
}
Polygon display_polygon = scaleDown_PolygonForSequentialSolver(2, transformed_polygon);
string color;
switch(i % 16)
{
case 0:
{
color = "green";
break;
}
case 1:
{
color = "blue";
break;
}
case 2:
{
color = "red";
break;
}
case 3:
{
color = "grey";
break;
}
case 4:
{
color = "cyan";
break;
}
case 5:
{
color = "magenta";
break;
}
case 6:
{
color = "yellow";
break;
}
case 7:
{
color = "black";
break;
}
case 8:
{
color = "indigo";
break;
}
case 9:
{
color = "olive";
break;
}
case 10:
{
color = "firebrick";
break;
}
case 11:
{
color = "violet";
break;
}
case 12:
{
color = "midnightblue";
break;
}
case 13:
{
color = "khaki";
break;
}
case 14:
{
color = "darkslategrey";
break;
}
case 15:
{
color = "hotpink";
break;
}
default:
{
break;
}
}
preview_svg.draw(display_polygon, color);
}
// general plate polygons are currently not supported
assert(solver_configuration.plate_bounding_polygon.points.size() == 0);
Polygon bed_polygon({ { solver_configuration.plate_bounding_box.min.x(), solver_configuration.plate_bounding_box.min.y() },
{ solver_configuration.plate_bounding_box.max.x(), solver_configuration.plate_bounding_box.min.y() },
{ solver_configuration.plate_bounding_box.max.x(), solver_configuration.plate_bounding_box.max.y() },
{ solver_configuration.plate_bounding_box.min.x(), solver_configuration.plate_bounding_box.max.y() } });
Polygon display_bed_polygon = scaleUp_PolygonForSlicer(SEQ_SVG_SCALE_FACTOR,
bed_polygon,
0,
0);
preview_svg.draw_outline(display_bed_polygon, "black");
preview_svg.Close();
printf(" Generating output SVG ... finised\n");
finish = clock();
printf("Decimation ... finished\n");
printf("Total CPU time: %.3f\n", (finish - start) / (double)CLOCKS_PER_SEC);
return 0;
}
/*----------------------------------------------------------------------------*/
// main program
int main(int argc, char **argv)
{
int result;
CommandParameters command_parameters;
print_IntroductoryMessage();
if (argc >= 1 && argc <= 10)
{
for (int i = 1; i < argc; ++i)
{
result = parse_CommandLineParameter(argv[i], command_parameters);
if (result < 0)
{
printf("Error: Cannot parse command line parameters (code = %d).\n", result);
print_Help();
return result;
}
}
if (command_parameters.help)
{
print_Help();
}
else
{
result = decimate_Polygons(command_parameters);
if (result < 0)
{
return result;
}
}
}
else
{
print_Help();
}
print_ConcludingMessage();
return 0;
}

View File

@@ -0,0 +1,62 @@
#ifndef __SEQUENTIAL_DECIMATOR_HPP__
#define __SEQUENTIAL_DECIMATOR_HPP__
/*----------------------------------------------------------------*/
#include "seq_sequential.hpp"
#include "seq_preprocess.hpp"
#include "libseqarrange/seq_interface.hpp"
/*----------------------------------------------------------------*/
const double SEQ_DECIMATION_TOLERANCE = 400000.0;
/*----------------------------------------------------------------*/
struct CommandParameters
{
CommandParameters()
: tolerance(SEQ_DECIMATION_TOLERANCE)
, input_filename("arrange_data_export.txt")
, output_filename("arrange_data_import.txt")
, x_position(0)
, y_position(0)
, random_position(true)
, help(false)
, x_nozzle(0)
, y_nozzle(0)
{
/* nothing */
}
double tolerance;
string input_filename;
string output_filename;
double x_position;
double y_position;
bool random_position;
coord_t x_nozzle;
coord_t y_nozzle;
bool help;
};
/*----------------------------------------------------------------------------*/
void print_IntroductoryMessage(void);
void print_ConcludingMessage(void);
void print_Help(void);
int parse_CommandLineParameter(const string &parameter, CommandParameters &parameters);
int decimate_Polygons(const CommandParameters &command_parameters);
/*----------------------------------------------------------------*/
#endif /* __SEQUENTIAL_DECIMATOR_HPP__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
#ifndef QIDIPARTS_H
#define QIDIPARTS_H
#include <vector>
#include "libslic3r/ExPolygon.hpp"
using TestData = std::vector<Slic3r::Polygon>;
using TestDataEx = std::vector<Slic3r::ExPolygons>;
extern const TestData QIDI_PART_POLYGONS;
extern const TestData QIDI_STEGOSAUR_POLYGONS;
extern const TestDataEx QIDI_PART_POLYGONS_EX;
#endif // QIDIPARTS_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
#ifndef __SEQ_TEST_INTERFACE_HPP__
#define __SEQ_TEST_INTERFACE_HPP__
/*----------------------------------------------------------------*/
#endif /* __SEQ_TEST_PREPROCESS_HPP__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
#ifndef __SEQ_TEST_POLYGON_HPP__
#define __SEQ_TEST_POLYGON_HPP__
/*----------------------------------------------------------------*/
#endif /* __SEQ_TEST_POLYGON_HPP__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
#ifndef __SEQ_TEST_PREPROCESS_HPP__
#define __SEQ_TEST_PREPROCESS_HPP__
/*----------------------------------------------------------------*/
#endif /* __SEQ_TEST_PREPROCESS_HPP__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
#ifndef __SEQ_TEST_SEQUENTIAL_HPP__
#define __SEQ_TEST_SEQUENTIAL_HPP__
/*----------------------------------------------------------------*/
#endif /* __SEQ_TEST_SEQUENTIAL_HPP__ */

View File

@@ -1,7 +1,7 @@
project(slic3r-arrange-wrapper)
cmake_minimum_required(VERSION 3.13)
add_library(slic3r-arrange-wrapper
add_library(slic3r-arrange-wrapper STATIC
include/arrange-wrapper/Arrange.hpp
include/arrange-wrapper/ArrangeSettingsDb_AppCfg.hpp
include/arrange-wrapper/ArrangeSettingsView.hpp

View File

@@ -1,7 +1,7 @@
project(slic3r-arrange)
cmake_minimum_required(VERSION 3.13)
add_library(slic3r-arrange
add_library(slic3r-arrange STATIC
include/arrange/Beds.hpp
include/arrange/ArrangeItemTraits.hpp
include/arrange/PackingContext.hpp

View File

@@ -67,7 +67,7 @@ template<class T, class TT = T> using WritableDataStoreOnly = std::enable_if_t<I
template<class T, class ArrItem>
void set_data(ArrItem &itm, const std::string &key, T &&data)
{
WritableDataStoreTraits<ArrItem>::template set(itm, key, std::forward<T>(data));
WritableDataStoreTraits<ArrItem>::template set<>(itm, key, std::forward<T>(data));
}
template<class T> constexpr bool IsReadWritableDataStore = IsDataStore<T> && IsWritableDataStore<T>;