mirror of
https://github.com/QIDITECH/QIDISlicer.git
synced 2026-01-30 15:38:43 +03:00
update src and test
This commit is contained in:
@@ -95,6 +95,7 @@ PenaltyBreakString: 600
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 50
|
||||
PenaltyReturnTypeOnItsOwnLine: 300
|
||||
PenaltyIndentedWhitespace: 10
|
||||
PointerAlignment: Right
|
||||
ReflowComments: true
|
||||
SortIncludes: false
|
||||
|
||||
@@ -616,7 +616,6 @@ if(SLIC3R_BUILD_TESTS)
|
||||
endif()
|
||||
|
||||
|
||||
# Resources install target, configure fhs.hpp on UNIX
|
||||
if (WIN32)
|
||||
install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "${CMAKE_INSTALL_PREFIX}/resources")
|
||||
elseif (SLIC3R_FHS)
|
||||
@@ -636,10 +635,10 @@ elseif (SLIC3R_FHS)
|
||||
)
|
||||
endforeach()
|
||||
install(DIRECTORY ${SLIC3R_RESOURCES_DIR}/udev/ DESTINATION lib/udev/rules.d)
|
||||
target_compile_definitions(QIDISlicer PUBLIC SLIC3R_FHS SLIC3R_FHS_RESOURCES="${SLIC3R_FHS_RESOURCES}")
|
||||
else ()
|
||||
install(FILES src/platform/unix/QIDISlicer.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/resources/applications)
|
||||
install(FILES src/platform/unix/QIDIGcodeviewer.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/resources/applications)
|
||||
install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "${CMAKE_INSTALL_PREFIX}/resources")
|
||||
endif ()
|
||||
|
||||
configure_file(src/platform/unix/fhs.hpp.in ${LIBDIR_BIN}/platform/unix/fhs.hpp)
|
||||
|
||||
73
src/CLI/CLI.hpp
Normal file
73
src/CLI/CLI.hpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "CLI_DynamicPrintConfig.hpp"
|
||||
|
||||
#ifdef SLIC3R_GUI
|
||||
#include "slic3r/GUI/GUI_Init.hpp"
|
||||
#endif
|
||||
|
||||
namespace Slic3r::CLI
|
||||
{
|
||||
// struct which is filled from comand line input
|
||||
struct Data
|
||||
{
|
||||
Data();
|
||||
|
||||
CLI_DynamicPrintConfig input_config;
|
||||
CLI_DynamicPrintConfig overrides_config;
|
||||
CLI_DynamicPrintConfig transform_config;
|
||||
CLI_DynamicPrintConfig misc_config;
|
||||
CLI_DynamicPrintConfig actions_config;
|
||||
|
||||
std::vector<std::string> input_files;
|
||||
|
||||
bool empty() {
|
||||
return input_files.empty()
|
||||
&& input_config.empty()
|
||||
&& overrides_config.empty()
|
||||
&& transform_config.empty()
|
||||
&& actions_config.empty();
|
||||
}
|
||||
};
|
||||
|
||||
// Implemented in PrintHelp.cpp
|
||||
|
||||
void print_help(bool include_print_options = false, PrinterTechnology printer_technology = ptAny);
|
||||
|
||||
// Implemented in Setup.cpp
|
||||
|
||||
bool setup(Data& cli, int argc, char** argv);
|
||||
|
||||
// Implemented in LoadPrintData.cpp
|
||||
|
||||
PrinterTechnology get_printer_technology(const DynamicConfig& config);
|
||||
bool load_print_data(std::vector<Model>& models,
|
||||
DynamicPrintConfig& print_config,
|
||||
PrinterTechnology& printer_technology,
|
||||
Data& cli);
|
||||
bool is_needed_post_processing(const DynamicPrintConfig& print_config);
|
||||
|
||||
// Implemented in ProcessTransform.cpp
|
||||
|
||||
bool process_transform(Data& cli, const DynamicPrintConfig& print_config, std::vector<Model>& models);
|
||||
|
||||
// Implemented in ProcessActions.cpp
|
||||
|
||||
bool has_full_config_from_profiles(const Data& cli);
|
||||
bool process_profiles_sharing(const Data& cli);
|
||||
bool process_actions(Data& cli, const DynamicPrintConfig& print_config, std::vector<Model>& models);
|
||||
|
||||
// Implemented in GuiParams.cpp
|
||||
#ifdef SLIC3R_GUI
|
||||
// set data for init GUI parameters
|
||||
// and return state of start_gui
|
||||
bool init_gui_params(GUI::GUI_InitParams& gui_params, int argc, char** argv, Data& cli);
|
||||
int start_gui_with_params(GUI::GUI_InitParams& params);
|
||||
int start_as_gcode_viewer(GUI::GUI_InitParams& gui_params);
|
||||
#endif
|
||||
|
||||
}
|
||||
47
src/CLI/CLI_DynamicPrintConfig.hpp
Normal file
47
src/CLI/CLI_DynamicPrintConfig.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "libslic3r/Config.hpp"
|
||||
|
||||
namespace Slic3r::CLI
|
||||
{
|
||||
enum class Type
|
||||
{
|
||||
Input,
|
||||
Overrides,
|
||||
Transformations,
|
||||
Misc,
|
||||
Actions,
|
||||
Undef
|
||||
};
|
||||
|
||||
class CLI_DynamicPrintConfig : public DynamicPrintConfig
|
||||
{
|
||||
public:
|
||||
CLI_DynamicPrintConfig() {}
|
||||
CLI_DynamicPrintConfig(Type type, const ConfigDef* config_def) :
|
||||
m_type (type),
|
||||
m_config_def (config_def) {}
|
||||
CLI_DynamicPrintConfig(const CLI_DynamicPrintConfig& other) :
|
||||
DynamicPrintConfig(other),
|
||||
m_type (other.type()),
|
||||
m_config_def (other.def()) {}
|
||||
|
||||
// Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here.
|
||||
const ConfigDef* def() const override { return m_config_def; }
|
||||
|
||||
// Verify whether the opt_key has not been obsoleted or renamed.
|
||||
// Both opt_key and value may be modified by handle_legacy().
|
||||
// If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy().
|
||||
// handle_legacy() is called internally by set_deserialize().
|
||||
void handle_legacy(t_config_option_key& opt_key, std::string& value) const override {
|
||||
if (m_type == Type::Overrides)
|
||||
DynamicPrintConfig::handle_legacy(opt_key, value);
|
||||
}
|
||||
|
||||
Type type() const { return m_type; }
|
||||
|
||||
private:
|
||||
Type m_type { Type::Undef };
|
||||
const ConfigDef* m_config_def { nullptr };
|
||||
};
|
||||
}
|
||||
144
src/CLI/GuiParams.cpp
Normal file
144
src/CLI/GuiParams.cpp
Normal 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
259
src/CLI/LoadPrintData.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/nowide/args.hpp>
|
||||
#include <boost/nowide/iostream.hpp>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/GCode/PostProcessor.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/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
180
src/CLI/PrintHelp.cpp
Normal file
@@ -0,0 +1,180 @@
|
||||
#include <string>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/nowide/iostream.hpp>
|
||||
|
||||
#include "CLI.hpp"
|
||||
|
||||
namespace Slic3r::CLI {
|
||||
|
||||
static void print_help(const ConfigDef& config_def, bool show_defaults, std::function<bool(const ConfigOptionDef&)> filter = [](const ConfigOptionDef &){ return true; })
|
||||
{
|
||||
|
||||
// prepare a function for wrapping text
|
||||
auto wrap = [](const std::string& text, size_t line_length) -> std::string {
|
||||
std::istringstream words(text);
|
||||
std::ostringstream wrapped;
|
||||
std::string word;
|
||||
|
||||
if (words >> word) {
|
||||
wrapped << word;
|
||||
size_t space_left = line_length - word.length();
|
||||
while (words >> word) {
|
||||
if (space_left < word.length() + 1) {
|
||||
wrapped << '\n' << word;
|
||||
space_left = line_length - word.length();
|
||||
}
|
||||
else {
|
||||
wrapped << ' ' << word;
|
||||
space_left -= word.length() + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return wrapped.str();
|
||||
};
|
||||
|
||||
// List of opt_keys that should be hidden from the CLI help.
|
||||
const std::vector<std::string> silent_options = { "webdev", "single_instance_on_url" };
|
||||
|
||||
// get the unique categories
|
||||
std::set<std::string> categories;
|
||||
for (const auto& opt : config_def.options) {
|
||||
const ConfigOptionDef& def = opt.second;
|
||||
if (filter(def))
|
||||
categories.insert(def.category);
|
||||
}
|
||||
|
||||
for (const std::string& category : categories) {
|
||||
if (category != "") {
|
||||
boost::nowide::cout << category << ":" << std::endl;
|
||||
}
|
||||
else if (categories.size() > 1) {
|
||||
boost::nowide::cout << "Misc options:" << std::endl;
|
||||
}
|
||||
|
||||
for (const auto& opt : config_def.options) {
|
||||
const ConfigOptionDef& def = opt.second;
|
||||
if (def.category != category || def.cli == ConfigOptionDef::nocli || !filter(def))
|
||||
continue;
|
||||
|
||||
if (std::find(silent_options.begin(), silent_options.end(), opt.second.opt_key) != silent_options.end())
|
||||
continue;
|
||||
|
||||
// get all possible variations: --foo, --foobar, -f...
|
||||
std::vector<std::string> cli_args = def.cli_args(opt.first);
|
||||
if (cli_args.empty())
|
||||
continue;
|
||||
|
||||
for (auto& arg : cli_args) {
|
||||
arg.insert(0, (arg.size() == 1) ? "-" : "--");
|
||||
if (def.type == coFloat || def.type == coInt || def.type == coFloatOrPercent
|
||||
|| def.type == coFloats || def.type == coInts) {
|
||||
arg += " N";
|
||||
}
|
||||
else if (def.type == coPoint) {
|
||||
arg += " X,Y";
|
||||
}
|
||||
else if (def.type == coPoint3) {
|
||||
arg += " X,Y,Z";
|
||||
}
|
||||
else if (def.type == coString || def.type == coStrings) {
|
||||
arg += " ABCD";
|
||||
}
|
||||
}
|
||||
|
||||
// left: command line options
|
||||
const std::string cli = boost::algorithm::join(cli_args, ", ");
|
||||
boost::nowide::cout << " " << std::left << std::setw(20) << cli;
|
||||
|
||||
// right: option description
|
||||
std::string descr = def.tooltip;
|
||||
bool show_defaults_this = show_defaults || def.opt_key == "config_compatibility";
|
||||
if (show_defaults_this && def.default_value && def.type != coBool
|
||||
&& (def.type != coString || !def.default_value->serialize().empty())) {
|
||||
descr += " (";
|
||||
if (!def.sidetext.empty()) {
|
||||
descr += def.sidetext + ", ";
|
||||
}
|
||||
else if (def.enum_def && def.enum_def->has_values()) {
|
||||
descr += boost::algorithm::join(def.enum_def->values(), ", ") + "; ";
|
||||
}
|
||||
descr += "default: " + def.default_value->serialize() + ")";
|
||||
}
|
||||
|
||||
// wrap lines of description
|
||||
descr = wrap(descr, 80);
|
||||
std::vector<std::string> lines;
|
||||
boost::split(lines, descr, boost::is_any_of("\n"));
|
||||
|
||||
// if command line options are too long, print description in new line
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
if (i == 0 && cli.size() > 19)
|
||||
boost::nowide::cout << std::endl;
|
||||
if (i > 0 || cli.size() > 19)
|
||||
boost::nowide::cout << std::string(21, ' ');
|
||||
boost::nowide::cout << lines[i] << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void print_help(bool include_print_options/* = false*/, PrinterTechnology printer_technology/* = ptAny*/)
|
||||
{
|
||||
boost::nowide::cout
|
||||
<< SLIC3R_BUILD_ID << " " << "based on Slic3r"
|
||||
#ifdef SLIC3R_GUI
|
||||
<< " (with GUI support)"
|
||||
#else /* SLIC3R_GUI */
|
||||
<< " (without GUI support)"
|
||||
#endif /* SLIC3R_GUI */
|
||||
<< std::endl
|
||||
<< "https://github.com/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
421
src/CLI/ProcessActions.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
210
src/CLI/ProcessTransform.cpp
Normal file
210
src/CLI/ProcessTransform.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/nowide/args.hpp>
|
||||
#include <boost/nowide/cstdlib.hpp>
|
||||
#include <boost/nowide/iostream.hpp>
|
||||
#include <boost/nowide/filesystem.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/nowide/iostream.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <boost/dll/runtime_symbol_info.hpp>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#if !SLIC3R_OPENGL_ES
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#endif // !SLIC3R_OPENGL_ES
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/ModelProcessing.hpp"
|
||||
#include "libslic3r/CutUtils.hpp"
|
||||
#include <arrange-wrapper/ModelArrange.hpp>
|
||||
#include "libslic3r/MultipleBeds.hpp"
|
||||
|
||||
#include "CLI.hpp"
|
||||
|
||||
namespace Slic3r::CLI {
|
||||
|
||||
bool process_transform(Data& cli, const DynamicPrintConfig& print_config, std::vector<Model>& models)
|
||||
{
|
||||
DynamicPrintConfig& transform = cli.transform_config;
|
||||
DynamicPrintConfig& actions = cli.actions_config;
|
||||
|
||||
const Vec2crd gap{ s_multiple_beds.get_bed_gap() };
|
||||
arr2::ArrangeBed bed = arr2::to_arrange_bed(get_bed_shape(print_config), gap);
|
||||
arr2::ArrangeSettings arrange_cfg;
|
||||
if (transform.has("merge") || transform.has("duplicate"))
|
||||
arrange_cfg.set_distance_from_objects(min_object_distance(print_config));
|
||||
|
||||
if (transform.has("merge")) {
|
||||
Model m;
|
||||
|
||||
for (auto& model : models)
|
||||
for (ModelObject* o : model.objects)
|
||||
m.add_object(*o);
|
||||
// Rearrange instances unless --dont-arrange is supplied
|
||||
if (!transform.has("dont_arrange") && !transform.opt_bool("dont_arrange")) {
|
||||
m.add_default_instances();
|
||||
if (actions.has("slice"))
|
||||
arrange_objects(m, bed, arrange_cfg);
|
||||
else
|
||||
arrange_objects(m, arr2::InfiniteBed{}, arrange_cfg);//??????
|
||||
}
|
||||
models.clear();
|
||||
models.emplace_back(std::move(m));
|
||||
}
|
||||
|
||||
if (transform.has("duplicate")) {
|
||||
for (auto& model : models) {
|
||||
const bool all_objects_have_instances = std::none_of(
|
||||
model.objects.begin(), model.objects.end(),
|
||||
[](ModelObject* o) { return o->instances.empty(); }
|
||||
);
|
||||
|
||||
int dups = transform.opt_int("duplicate");
|
||||
if (!all_objects_have_instances) model.add_default_instances();
|
||||
|
||||
try {
|
||||
if (dups > 1) {
|
||||
// if all input objects have defined position(s) apply duplication to the whole model
|
||||
duplicate(model, size_t(dups), bed, arrange_cfg);
|
||||
}
|
||||
else {
|
||||
arrange_objects(model, bed, arrange_cfg);
|
||||
}
|
||||
}
|
||||
catch (std::exception& ex) {
|
||||
boost::nowide::cerr << "error: " << ex.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (transform.has("duplicate_grid")) {
|
||||
std::vector<int>& ints = transform.option<ConfigOptionInts>("duplicate_grid")->values;
|
||||
const int x = ints.size() > 0 ? ints.at(0) : 1;
|
||||
const int y = ints.size() > 1 ? ints.at(1) : 1;
|
||||
const double distance = print_config.opt_float("duplicate_distance");
|
||||
for (auto& model : models)
|
||||
model.duplicate_objects_grid(x, y, (distance > 0) ? distance : 6); // TODO: this is not the right place for setting a default
|
||||
}
|
||||
|
||||
if (transform.has("center")) {
|
||||
for (auto& model : models) {
|
||||
model.add_default_instances();
|
||||
// this affects instances:
|
||||
model.center_instances_around_point(transform.option<ConfigOptionPoint>("center")->value);
|
||||
// this affects volumes:
|
||||
//FIXME Vojtech: Who knows why the complete model should be aligned with Z as a single rigid body?
|
||||
//model.align_to_ground();
|
||||
BoundingBoxf3 bbox;
|
||||
for (ModelObject* model_object : model.objects)
|
||||
// We are interested into the Z span only, therefore it is sufficient to measure the bounding box of the 1st instance only.
|
||||
bbox.merge(model_object->instance_bounding_box(0, false));
|
||||
for (ModelObject* model_object : model.objects)
|
||||
for (ModelInstance* model_instance : model_object->instances)
|
||||
model_instance->set_offset(Z, model_instance->get_offset(Z) - bbox.min.z());
|
||||
}
|
||||
}
|
||||
|
||||
if (transform.has("align_xy")) {
|
||||
const Vec2d& p = transform.option<ConfigOptionPoint>("align_xy")->value;
|
||||
for (auto& model : models) {
|
||||
BoundingBoxf3 bb = model.bounding_box_exact();
|
||||
// this affects volumes:
|
||||
model.translate(-(bb.min.x() - p.x()), -(bb.min.y() - p.y()), -bb.min.z());
|
||||
}
|
||||
}
|
||||
|
||||
if (transform.has("rotate")) {
|
||||
for (auto& model : models)
|
||||
for (auto& o : model.objects)
|
||||
// this affects volumes:
|
||||
o->rotate(Geometry::deg2rad(transform.opt_float("rotate")), Z);
|
||||
}
|
||||
if (transform.has("rotate_x")) {
|
||||
for (auto& model : models)
|
||||
for (auto& o : model.objects)
|
||||
// this affects volumes:
|
||||
o->rotate(Geometry::deg2rad(transform.opt_float("rotate_x")), X);
|
||||
}
|
||||
if (transform.has("rotate_y")) {
|
||||
for (auto& model : models)
|
||||
for (auto& o : model.objects)
|
||||
// this affects volumes:
|
||||
o->rotate(Geometry::deg2rad(transform.opt_float("rotate_y")), Y);
|
||||
}
|
||||
|
||||
if (transform.has("scale")) {
|
||||
for (auto& model : models)
|
||||
for (auto& o : model.objects)
|
||||
// this affects volumes:
|
||||
o->scale(transform.get_abs_value("scale", 1));
|
||||
}
|
||||
if (transform.has("scale_to_fit")) {
|
||||
const Vec3d& opt = transform.opt<ConfigOptionPoint3>("scale_to_fit")->value;
|
||||
if (opt.x() <= 0 || opt.y() <= 0 || opt.z() <= 0) {
|
||||
boost::nowide::cerr << "--scale-to-fit requires a positive volume" << std::endl;
|
||||
return false;
|
||||
}
|
||||
for (auto& model : models)
|
||||
for (auto& o : model.objects)
|
||||
// this affects volumes:
|
||||
o->scale_to_fit(opt);
|
||||
}
|
||||
|
||||
if (transform.has("cut")) {
|
||||
std::vector<Model> new_models;
|
||||
const Vec3d plane_center = transform.opt_float("cut") * Vec3d::UnitZ();
|
||||
for (auto& model : models) {
|
||||
Model new_model;
|
||||
model.translate(0, 0, -model.bounding_box_exact().min.z()); // align to z = 0
|
||||
size_t num_objects = model.objects.size();
|
||||
for (size_t i = 0; i < num_objects; ++i) {
|
||||
ModelObject* mo = model.objects.front();
|
||||
const Vec3d cut_center_offset = plane_center - mo->instances[0]->get_offset();
|
||||
Cut cut(mo, 0, Geometry::translation_transform(cut_center_offset),
|
||||
ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::PlaceOnCutUpper);
|
||||
auto cut_objects = cut.perform_with_plane();
|
||||
for (ModelObject* obj : cut_objects)
|
||||
new_model.add_object(*obj);
|
||||
model.delete_object(size_t(0));
|
||||
}
|
||||
new_models.push_back(new_model);
|
||||
}
|
||||
|
||||
// TODO: copy less stuff around using pointers
|
||||
models = new_models;
|
||||
|
||||
if (actions.empty()) {
|
||||
// cutting transformations are setting an "export" action.
|
||||
actions.set_key_value("export_stl", new ConfigOptionBool(true));
|
||||
}
|
||||
}
|
||||
|
||||
if (transform.has("split")) {
|
||||
for (Model& model : models) {
|
||||
size_t num_objects = model.objects.size();
|
||||
for (size_t i = 0; i < num_objects; ++i) {
|
||||
ModelObjectPtrs new_objects;
|
||||
ModelProcessing::split(model.objects.front(), &new_objects);
|
||||
model.delete_object(size_t(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All transforms have been dealt with. Now ensure that the objects are on bed.
|
||||
// (Unless the user said otherwise.)
|
||||
if (!transform.has("ensure_on_bed") || transform.opt_bool("ensure_on_bed"))
|
||||
for (auto& model : models)
|
||||
for (auto& o : model.objects)
|
||||
o->ensure_on_bed();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
584
src/CLI/ProfilesSharingUtils.cpp
Normal file
584
src/CLI/ProfilesSharingUtils.cpp
Normal 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
|
||||
29
src/CLI/ProfilesSharingUtils.hpp
Normal file
29
src/CLI/ProfilesSharingUtils.hpp
Normal 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
54
src/CLI/Run.cpp
Normal 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
337
src/CLI/Setup.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 ()
|
||||
|
||||
|
||||
1147
src/QIDISlicer.cpp
1147
src/QIDISlicer.cpp
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
35
src/libseqarrange/CMakeLists.txt
Normal file
35
src/libseqarrange/CMakeLists.txt
Normal 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()
|
||||
|
||||
|
||||
|
||||
|
||||
204
src/libseqarrange/include/libseqarrange/seq_interface.hpp
Normal file
204
src/libseqarrange/include/libseqarrange/seq_interface.hpp
Normal 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__ */
|
||||
73
src/libseqarrange/src/seq_defs.hpp
Normal file
73
src/libseqarrange/src/seq_defs.hpp
Normal 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__ */
|
||||
1350
src/libseqarrange/src/seq_interface.cpp
Normal file
1350
src/libseqarrange/src/seq_interface.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1221
src/libseqarrange/src/seq_preprocess.cpp
Normal file
1221
src/libseqarrange/src/seq_preprocess.cpp
Normal file
File diff suppressed because it is too large
Load Diff
223
src/libseqarrange/src/seq_preprocess.hpp
Normal file
223
src/libseqarrange/src/seq_preprocess.hpp
Normal 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__ */
|
||||
11678
src/libseqarrange/src/seq_sequential.cpp
Normal file
11678
src/libseqarrange/src/seq_sequential.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1677
src/libseqarrange/src/seq_sequential.hpp
Normal file
1677
src/libseqarrange/src/seq_sequential.hpp
Normal file
File diff suppressed because it is too large
Load Diff
235
src/libseqarrange/src/seq_utilities.cpp
Normal file
235
src/libseqarrange/src/seq_utilities.cpp
Normal 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
|
||||
41
src/libseqarrange/src/seq_utilities.hpp
Normal file
41
src/libseqarrange/src/seq_utilities.hpp
Normal 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__ */
|
||||
415
src/libseqarrange/src/sequential_decimator.cpp
Normal file
415
src/libseqarrange/src/sequential_decimator.cpp
Normal 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 ¶meter, 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;
|
||||
}
|
||||
62
src/libseqarrange/src/sequential_decimator.hpp
Normal file
62
src/libseqarrange/src/sequential_decimator.hpp
Normal 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 ¶meter, CommandParameters ¶meters);
|
||||
int decimate_Polygons(const CommandParameters &command_parameters);
|
||||
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
#endif /* __SEQUENTIAL_DECIMATOR_HPP__ */
|
||||
5981
src/libseqarrange/test/qidiparts.cpp
Normal file
5981
src/libseqarrange/test/qidiparts.cpp
Normal file
File diff suppressed because it is too large
Load Diff
14
src/libseqarrange/test/qidiparts.hpp
Normal file
14
src/libseqarrange/test/qidiparts.hpp
Normal 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
|
||||
1241
src/libseqarrange/test/seq_test_interface.cpp
Normal file
1241
src/libseqarrange/test/seq_test_interface.cpp
Normal file
File diff suppressed because it is too large
Load Diff
7
src/libseqarrange/test/seq_test_interface.hpp
Normal file
7
src/libseqarrange/test/seq_test_interface.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef __SEQ_TEST_INTERFACE_HPP__
|
||||
#define __SEQ_TEST_INTERFACE_HPP__
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
|
||||
#endif /* __SEQ_TEST_PREPROCESS_HPP__ */
|
||||
3356
src/libseqarrange/test/seq_test_polygon.cpp
Normal file
3356
src/libseqarrange/test/seq_test_polygon.cpp
Normal file
File diff suppressed because it is too large
Load Diff
7
src/libseqarrange/test/seq_test_polygon.hpp
Normal file
7
src/libseqarrange/test/seq_test_polygon.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef __SEQ_TEST_POLYGON_HPP__
|
||||
#define __SEQ_TEST_POLYGON_HPP__
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
|
||||
#endif /* __SEQ_TEST_POLYGON_HPP__ */
|
||||
1243
src/libseqarrange/test/seq_test_preprocess.cpp
Normal file
1243
src/libseqarrange/test/seq_test_preprocess.cpp
Normal file
File diff suppressed because it is too large
Load Diff
7
src/libseqarrange/test/seq_test_preprocess.hpp
Normal file
7
src/libseqarrange/test/seq_test_preprocess.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef __SEQ_TEST_PREPROCESS_HPP__
|
||||
#define __SEQ_TEST_PREPROCESS_HPP__
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
|
||||
#endif /* __SEQ_TEST_PREPROCESS_HPP__ */
|
||||
2520
src/libseqarrange/test/seq_test_sequential.cpp
Normal file
2520
src/libseqarrange/test/seq_test_sequential.cpp
Normal file
File diff suppressed because it is too large
Load Diff
7
src/libseqarrange/test/seq_test_sequential.hpp
Normal file
7
src/libseqarrange/test/seq_test_sequential.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef __SEQ_TEST_SEQUENTIAL_HPP__
|
||||
#define __SEQ_TEST_SEQUENTIAL_HPP__
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
|
||||
#endif /* __SEQ_TEST_SEQUENTIAL_HPP__ */
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# TODO Add individual tests as executables in separate directories
|
||||
# add_subirectory(<testcase>)
|
||||
|
||||
find_package(Catch2 2.9 REQUIRED)
|
||||
find_package(Catch2 3.8 REQUIRED)
|
||||
slic3r_remap_configs(Catch2::Catch2 RelWithDebInfo Release)
|
||||
slic3r_remap_configs(Catch2::Catch2WithMain RelWithDebInfo Release)
|
||||
|
||||
include(Catch)
|
||||
|
||||
@@ -13,7 +15,7 @@ set(CATCH_EXTRA_ARGS "" CACHE STRING "Extra arguments for catch2 test suites.")
|
||||
add_library(test_common INTERFACE)
|
||||
target_include_directories(test_common INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
target_compile_definitions(test_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)" CATCH_CONFIG_FAST_COMPILE)
|
||||
target_link_libraries(test_common INTERFACE Catch2::Catch2)
|
||||
target_link_libraries(test_common INTERFACE Catch2::Catch2WithMain)
|
||||
|
||||
if (APPLE)
|
||||
target_link_libraries(test_common INTERFACE "-liconv -framework IOKit" "-framework CoreFoundation" -lc++)
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include <catch2/matchers/catch_matchers.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include "test_utils.hpp"
|
||||
|
||||
#include <libslic3r/Execution/ExecutionSeq.hpp>
|
||||
@@ -36,6 +39,8 @@
|
||||
|
||||
#include <random>
|
||||
|
||||
using namespace Catch;
|
||||
|
||||
template<class ArrItem = Slic3r::arr2::ArrangeItem>
|
||||
static std::vector<ArrItem> qidi_parts(double infl = 0.) {
|
||||
using namespace Slic3r;
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include <catch2/generators/catch_generators.hpp>
|
||||
#include <catch2/generators/catch_generators_range.hpp>
|
||||
#include <catch2/generators/catch_generators_all.hpp>
|
||||
#include <catch2/matchers/catch_matchers.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
|
||||
#include "test_utils.hpp"
|
||||
|
||||
#include <arrange-wrapper/Arrange.hpp>
|
||||
@@ -11,6 +18,8 @@
|
||||
#include "libslic3r/Geometry/ConvexHull.hpp"
|
||||
#include "libslic3r/Format/3mf.hpp"
|
||||
|
||||
using namespace Catch;
|
||||
|
||||
static Slic3r::Model get_example_model_with_20mm_cube()
|
||||
{
|
||||
using namespace Slic3r;
|
||||
|
||||
@@ -4,46 +4,53 @@
|
||||
#define CATCH_CONFIG_EXTERNAL_INTERFACES
|
||||
#define CATCH_CONFIG_MAIN
|
||||
// #define CATCH_CONFIG_DEFAULT_REPORTER "verboseconsole"
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/reporters/catch_reporter_streaming_base.hpp>
|
||||
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
namespace Catch {
|
||||
struct VerboseConsoleReporter : public ConsoleReporter {
|
||||
struct VerboseConsoleReporter : public StreamingReporterBase {
|
||||
double duration = 0.;
|
||||
using ConsoleReporter::ConsoleReporter;
|
||||
using StreamingReporterBase::StreamingReporterBase;
|
||||
|
||||
static std::string getDescription() {
|
||||
return "Verbose Console Reporter";
|
||||
}
|
||||
|
||||
|
||||
void testCaseStarting(TestCaseInfo const& _testInfo) override
|
||||
{
|
||||
Colour::use(Colour::Cyan);
|
||||
stream << "Testing ";
|
||||
Colour::use(Colour::None);
|
||||
stream << _testInfo.name << std::endl;
|
||||
ConsoleReporter::testCaseStarting(_testInfo);
|
||||
//Colour::use(Colour::Cyan);
|
||||
m_stream << "Testing ";
|
||||
//Colour::use(Colour::None);
|
||||
m_stream << _testInfo.name << std::endl;
|
||||
StreamingReporterBase::testCaseStarting(_testInfo);
|
||||
}
|
||||
|
||||
void sectionStarting(const SectionInfo &_sectionInfo) override
|
||||
{
|
||||
if (_sectionInfo.name != currentTestCaseInfo->name)
|
||||
stream << _sectionInfo.name << std::endl;
|
||||
m_stream << _sectionInfo.name << std::endl;
|
||||
|
||||
ConsoleReporter::sectionStarting(_sectionInfo);
|
||||
StreamingReporterBase::sectionStarting(_sectionInfo);
|
||||
}
|
||||
|
||||
void sectionEnded(const SectionStats &_sectionStats) override {
|
||||
duration += _sectionStats.durationInSeconds;
|
||||
ConsoleReporter::sectionEnded(_sectionStats);
|
||||
StreamingReporterBase::sectionEnded(_sectionStats);
|
||||
}
|
||||
|
||||
void testCaseEnded(TestCaseStats const& stats) override
|
||||
{
|
||||
if (stats.totals.assertions.allOk()) {
|
||||
Colour::use(Colour::BrightGreen);
|
||||
stream << "Passed";
|
||||
Colour::use(Colour::None);
|
||||
stream << " in " << duration << " [seconds]\n" << std::endl;
|
||||
//Colour::use(Colour::BrightGreen);
|
||||
m_stream << "Passed";
|
||||
//Colour::use(Colour::None);
|
||||
m_stream << " in " << duration << " [seconds]\n" << std::endl;
|
||||
}
|
||||
|
||||
duration = 0.;
|
||||
ConsoleReporter::testCaseEnded(stats);
|
||||
StreamingReporterBase::testCaseEnded(stats);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
5
tests/data/sla_islands/SPE-2674.svg
Normal file
5
tests/data/sla_islands/SPE-2674.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 22 KiB |
11
tests/data/sla_islands/SPE-2674_2.svg
Normal file
11
tests/data/sla_islands/SPE-2674_2.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg height="20.860270" width="22.311550" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<marker id="endArrow" markerHeight="8" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="1" refY="5" viewBox="0 0 10 10">
|
||||
<polyline fill="darkblue" points="0,0 10,5 0,10 1,5" />
|
||||
</marker>
|
||||
<rect fill='white' stroke='none' x='0' y='0' width='22.311550' height='20.860270'/>
|
||||
<path d="M 1716256 -11015182 1795391 -11035439 1568498 -10955916 1631011 -10984708 1564236 -10960836 1564236 -10960837 1674546 -11011825 1791112 -11041943 z " style="fill: none; stroke: black; stroke-width: 0.000010; fill-type: evenodd" fill-opacity="0.000000" />
|
||||
<path d="M 11.5202 10.5927 12.3116 10.7952 10.0426 10 10.6677 10.2879 10 10.0492 10 10.0492 11.1031 10.5591 12.2688 10.8603 z " style="fill: lightgray; stroke: black; stroke-width: 0.000000; fill-type: evenodd" fill-opacity="1.000000" />
|
||||
<path d="M 10 10.0492 12.0868 10.7952 11.5202 10.5927 12.3116 10.7952 z " style="fill: gray; stroke: black; stroke-width: 0.000000; fill-type: evenodd" fill-opacity="1.000000" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1176
tests/data/sla_islands/SPE-2709.svg
Normal file
1176
tests/data/sla_islands/SPE-2709.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 189 KiB |
11
tests/data/sla_islands/lm_issue.svg
Normal file
11
tests/data/sla_islands/lm_issue.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 50 KiB |
@@ -14,6 +14,7 @@ add_executable(${_TEST_NAME}_tests
|
||||
test_gaps.cpp
|
||||
test_gcode.cpp
|
||||
test_gcode_travels.cpp
|
||||
test_infill_above_bridges.cpp
|
||||
test_seam_perimeters.cpp
|
||||
test_seam_shells.cpp
|
||||
test_seam_geometry.cpp
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/benchmark/catch_benchmark_all.hpp>
|
||||
#include "test_data.hpp"
|
||||
|
||||
#include "libslic3r/GCode/SeamGeometry.hpp"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "test_data.hpp"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <libslic3r/BridgeDetector.hpp>
|
||||
#include <libslic3r/Geometry.hpp>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
|
||||
@@ -7,6 +8,7 @@
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Test;
|
||||
using namespace Catch;
|
||||
|
||||
constexpr bool debug_files{false};
|
||||
|
||||
@@ -168,7 +170,7 @@ TEST_CASE_METHOD(CancelObjectFixture, "Single extruder", "[CancelObject]") {
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(CancelObjectFixture, "Sequential print", "[CancelObject]") {
|
||||
config.set_deserialize_strict({{"complete_objects", 1}});
|
||||
config.set_deserialize_strict({{"complete_objects", 1} });
|
||||
|
||||
Print print;
|
||||
print.apply(two_cubes, config);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "test_data.hpp"
|
||||
#include "libslic3r/ClipperZUtils.hpp"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <exception>
|
||||
#include <numeric>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include "test_data.hpp"
|
||||
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
@@ -399,7 +400,6 @@ bool contains_regex(const std::string &data, const std::string &pattern)
|
||||
|
||||
} } // namespace Slic3r::Test
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
SCENARIO("init_print functionality", "[test_data]") {
|
||||
GIVEN("A default config") {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "libslic3r/GCode/SeamPlacer.hpp"
|
||||
#include "libslic3r/GCode/SeamAligned.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Slic3r { namespace Test {
|
||||
@@ -159,7 +159,7 @@ std::string slice(
|
||||
bool contains(const std::string &data, const std::string &pattern);
|
||||
bool contains_regex(const std::string &data, const std::string &pattern);
|
||||
|
||||
inline std::unique_ptr<Print> process_3mf(const std::filesystem::path &path) {
|
||||
inline std::unique_ptr<Print> process_3mf(const boost::filesystem::path &path) {
|
||||
DynamicPrintConfig config;
|
||||
auto print{std::make_unique<Print>()};
|
||||
Model model;
|
||||
@@ -176,7 +176,7 @@ inline std::unique_ptr<Print> process_3mf(const std::filesystem::path &path) {
|
||||
|
||||
static std::map<std::string, std::unique_ptr<Print>> prints_3mfs;
|
||||
// Lazy getter, to avoid processing the 3mf multiple times, it already takes ages.
|
||||
inline Print *get_print(const std::filesystem::path &file_path) {
|
||||
inline Print *get_print(const boost::filesystem::path &file_path) {
|
||||
if (!prints_3mfs.count(file_path.string())) {
|
||||
prints_3mfs[file_path.string()] = process_3mf(file_path.string());
|
||||
}
|
||||
@@ -204,8 +204,8 @@ inline void serialize_seam(std::ostream &output, const std::vector<std::vector<S
|
||||
|
||||
struct SeamsFixture
|
||||
{
|
||||
const std::filesystem::path file_3mf{
|
||||
std::filesystem::path{TEST_DATA_DIR} / std::filesystem::path{"seam_test_object.3mf"}};
|
||||
const boost::filesystem::path file_3mf{
|
||||
boost::filesystem::path{TEST_DATA_DIR} / boost::filesystem::path{"seam_test_object.3mf"}};
|
||||
const Print *print{Test::get_print(file_3mf)};
|
||||
const PrintObject *print_object{print->objects()[0]};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
@@ -11,6 +12,7 @@
|
||||
#include "test_data.hpp"
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Catch;
|
||||
|
||||
static inline Slic3r::Point random_point(float LO=-50, float HI=50)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
@@ -12,6 +13,7 @@
|
||||
|
||||
using namespace Slic3r::Test;
|
||||
using namespace Slic3r;
|
||||
using namespace Catch;
|
||||
|
||||
SCENARIO("Extrusion width specifics", "[Flow]") {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/GCodeReader.hpp"
|
||||
#include "libslic3r/Geometry/ConvexHull.hpp"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
@@ -10,6 +11,7 @@
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Test;
|
||||
using namespace Catch;
|
||||
|
||||
constexpr bool debug_files = false;
|
||||
|
||||
@@ -61,7 +63,7 @@ TEST_CASE("Wiping speeds", "[GCode]") {
|
||||
INFO("Wipe moves don\'t retract faster than configured speed");
|
||||
CHECK(retract_speed < expected_retract_speed);
|
||||
}
|
||||
INFO("No wiping after layer change")
|
||||
INFO("No wiping after layer change");
|
||||
CHECK(!wiping_on_new_layer);
|
||||
}
|
||||
|
||||
@@ -115,12 +117,12 @@ std::optional<double> parse_axis(const std::string& line, const std::string& axi
|
||||
* - no travel moves go outside skirt
|
||||
* - temperatures are set correctly
|
||||
*/
|
||||
TEST_CASE("Extrusion, travels, temeperatures", "[GCode]") {
|
||||
TEST_CASE("Extrusion, travels, temperatures", "[GCode]") {
|
||||
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||
config.set_deserialize_strict({
|
||||
{ "gcode_comments", 1 },
|
||||
{ "complete_objects", 1 },
|
||||
{ "extrusion_axis", 'A' },
|
||||
{ "extrusion_axis", "A" },
|
||||
{ "start_gcode", "" }, // prevent any default extra Z move
|
||||
{ "layer_height", 0.4 },
|
||||
{ "first_layer_height", 0.4 },
|
||||
@@ -170,6 +172,11 @@ TEST_CASE("Extrusion, travels, temeperatures", "[GCode]") {
|
||||
}
|
||||
});
|
||||
|
||||
// Remove last travel_moves returning to origin
|
||||
if (travel_moves.back().x() == 0 && travel_moves.back().y() == 0) {
|
||||
travel_moves.pop_back();
|
||||
}
|
||||
|
||||
const unsigned layer_count = 20 / 0.4;
|
||||
INFO("Complete_objects generates the correct number of Z moves.");
|
||||
CHECK(z_moves.size() == layer_count * 2);
|
||||
@@ -188,20 +195,30 @@ TEST_CASE("Extrusion, travels, temeperatures", "[GCode]") {
|
||||
|
||||
|
||||
TEST_CASE("Used filament", "[GCode]") {
|
||||
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||
config.set_deserialize_strict({
|
||||
{ "retract_length", "1000000" },
|
||||
DynamicPrintConfig config1 = Slic3r::DynamicPrintConfig::full_print_config();
|
||||
config1.set_deserialize_strict({
|
||||
{ "retract_length", "0" },
|
||||
{ "use_relative_e_distances", 1 },
|
||||
{ "layer_gcode", "G92 E0\n" },
|
||||
});
|
||||
GCodeReader parser;
|
||||
Print print;
|
||||
Model model;
|
||||
Test::init_print({TestMesh::cube_20x20x20}, print, model, config);
|
||||
Test::gcode(print);
|
||||
Print print1;
|
||||
Model model1;
|
||||
Test::init_print({TestMesh::cube_20x20x20}, print1, model1, config1);
|
||||
Test::gcode(print1);
|
||||
|
||||
DynamicPrintConfig config2 = Slic3r::DynamicPrintConfig::full_print_config();
|
||||
config2.set_deserialize_strict({
|
||||
{ "retract_length", "999" },
|
||||
{ "use_relative_e_distances", 1 },
|
||||
{ "layer_gcode", "G92 E0\n" },
|
||||
});
|
||||
Print print2;
|
||||
Model model2;
|
||||
Test::init_print({TestMesh::cube_20x20x20}, print2, model2, config2);
|
||||
Test::gcode(print2);
|
||||
|
||||
INFO("Final retraction is not considered in total used filament");
|
||||
CHECK(print.print_statistics().total_used_filament > 0);
|
||||
CHECK(print1.print_statistics().total_used_filament == print2.print_statistics().total_used_filament);
|
||||
}
|
||||
|
||||
void check_m73s(Print& print){
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/matchers/catch_matchers_all.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <libslic3r/GCode/Travels.hpp>
|
||||
#include <libslic3r/ExPolygon.hpp>
|
||||
#include <libslic3r/GCode.hpp>
|
||||
@@ -6,8 +8,9 @@
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Slic3r::GCode::Impl::Travels;
|
||||
using namespace Catch;
|
||||
|
||||
struct ApproxEqualsPoints : public Catch::MatcherBase<Points> {
|
||||
struct ApproxEqualsPoints : public Catch::Matchers::MatcherBase<Points> {
|
||||
ApproxEqualsPoints(const Points& expected, unsigned tolerance): expected(expected), tolerance(tolerance) {}
|
||||
bool match(const Points& points) const override {
|
||||
if (points.size() != expected.size()) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/matchers/catch_matchers.hpp>
|
||||
#include <catch2/matchers/catch_matchers_string.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "libslic3r/GCode/GCodeWriter.hpp"
|
||||
#include "libslic3r/GCodeReader.hpp"
|
||||
|
||||
using namespace Slic3r;
|
||||
using Catch::Approx;
|
||||
|
||||
SCENARIO("set_speed emits values with fixed-point output.", "[GCodeWriter]") {
|
||||
|
||||
@@ -12,23 +17,523 @@ SCENARIO("set_speed emits values with fixed-point output.", "[GCodeWriter]") {
|
||||
GCodeWriter writer;
|
||||
WHEN("set_speed is called to set speed to 99999.123") {
|
||||
THEN("Output string is G1 F99999.123") {
|
||||
REQUIRE_THAT(writer.set_speed(99999.123), Catch::Equals("G1 F99999.123\n"));
|
||||
REQUIRE_THAT(writer.set_speed(99999.123), Catch::Matchers::Equals("G1 F99999.123\n"));
|
||||
}
|
||||
}
|
||||
WHEN("set_speed is called to set speed to 1") {
|
||||
THEN("Output string is G1 F1") {
|
||||
REQUIRE_THAT(writer.set_speed(1.0), Catch::Equals("G1 F1\n"));
|
||||
REQUIRE_THAT(writer.set_speed(1.0), Catch::Matchers::Equals("G1 F1\n"));
|
||||
}
|
||||
}
|
||||
WHEN("set_speed is called to set speed to 203.200022") {
|
||||
THEN("Output string is G1 F203.2") {
|
||||
REQUIRE_THAT(writer.set_speed(203.200022), Catch::Equals("G1 F203.2\n"));
|
||||
REQUIRE_THAT(writer.set_speed(203.200022), Catch::Matchers::Equals("G1 F203.2\n"));
|
||||
}
|
||||
}
|
||||
WHEN("set_speed is called to set speed to 203.200522") {
|
||||
THEN("Output string is G1 F203.201") {
|
||||
REQUIRE_THAT(writer.set_speed(203.200522), Catch::Equals("G1 F203.201\n"));
|
||||
REQUIRE_THAT(writer.set_speed(203.200522), Catch::Matchers::Equals("G1 F203.201\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void check_gcode_feedrate(const std::string& gcode, const GCodeConfig& config, double expected_speed) {
|
||||
GCodeReader parser;
|
||||
parser.parse_buffer(gcode, [&] (Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line) {
|
||||
|
||||
const double travel_speed = config.opt_float("travel_speed");
|
||||
|
||||
const double feedrate = line.has_f() ? line.f() : self.f();
|
||||
CHECK(feedrate == Approx(expected_speed * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
|
||||
|
||||
if (line.dist_Z(self) != 0) {
|
||||
// lift move or lift + change layer
|
||||
const double travel_speed_z = config.opt_float("travel_speed_z");
|
||||
if (travel_speed_z) {
|
||||
Vec3d move{line.dist_X(self), line.dist_Y(self), line.dist_Z(self)};
|
||||
double move_u_z = move.z() / move.norm();
|
||||
double travel_speed_ = std::abs(travel_speed_z / move_u_z);
|
||||
INFO("move Z feedrate Z component is less than or equal to travel_speed_z");
|
||||
CHECK(feedrate * std::abs(move_u_z) <= Approx(travel_speed_z * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
|
||||
if (travel_speed_ < travel_speed) {
|
||||
INFO("move Z at travel speed Z");
|
||||
CHECK(feedrate == Approx(travel_speed_ * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
|
||||
INFO("move Z feedrate Z component is equal to travel_speed_z");
|
||||
CHECK(feedrate * std::abs(move_u_z) == Approx(travel_speed_z * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
|
||||
} else {
|
||||
INFO("move Z at travel speed");
|
||||
CHECK(feedrate == Approx(travel_speed * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
|
||||
}
|
||||
} else {
|
||||
INFO("move Z at travel speed");
|
||||
CHECK(feedrate == Approx(travel_speed * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
|
||||
}
|
||||
} else if (not line.extruding(self)) {
|
||||
// normal move
|
||||
INFO("move XY at travel speed");
|
||||
CHECK(feedrate == Approx(travel_speed * 60));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SCENARIO("travel_speed_z is zero should use travel_speed.", "[GCodeWriter]") {
|
||||
GIVEN("GCodeWriter instance") {
|
||||
GCodeWriter writer;
|
||||
WHEN("travel_speed_z is set to 0") {
|
||||
writer.config.travel_speed.value = 1000;
|
||||
writer.config.travel_speed_z.value = 0;
|
||||
THEN("XYZ move feed rate should be equal to travel_speed") {
|
||||
const Vec3d move{10, 10, 10};
|
||||
const double speed = writer.config.travel_speed.value;
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("travel_speed_z is respected in Z speed component.", "[GCodeWriter]") {
|
||||
GIVEN("GCodeWriter instance") {
|
||||
GCodeWriter writer;
|
||||
WHEN("travel_speed_z is set to 10") {
|
||||
writer.config.travel_speed.value = 1000;
|
||||
writer.config.travel_speed_z.value = 10;
|
||||
THEN("Z move feed rate should be equal to travel_speed_z") {
|
||||
const Vec3d move{0, 0, 10};
|
||||
const double speed = writer.config.travel_speed_z.value;
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("-Z move feed rate should be equal to travel_speed_z") {
|
||||
const Vec3d move{0, 0, -10};
|
||||
const double speed = writer.config.travel_speed_z.value;
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("XY move feed rate should be equal to travel_speed") {
|
||||
const Vec3d move{10, 10, 0};
|
||||
const double speed = writer.config.travel_speed.value;
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("-XY move feed rate should be equal to travel_speed") {
|
||||
const Vec3d move{-10, 10, 0};
|
||||
const double speed = writer.config.travel_speed.value;
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("X-Y move feed rate should be equal to travel_speed") {
|
||||
const Vec3d move{10, -10, 0};
|
||||
const double speed = writer.config.travel_speed.value;
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("-X-Y move feed rate should be equal to travel_speed") {
|
||||
const Vec3d move{-10, -10, 0};
|
||||
const double speed = writer.config.travel_speed.value;
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("XZ move feed rate Z component should be equal to travel_speed_z") {
|
||||
const Vec3d move{10, 0, 10};
|
||||
const Vec3d move_u = move / move.norm();
|
||||
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
|
||||
Vec3d p1 = writer.get_position();
|
||||
Vec3d p2 = p1 + move;
|
||||
std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("-XZ move feed rate Z component should be equal to travel_speed_z") {
|
||||
const Vec3d move{-10, 0, 10};
|
||||
const Vec3d move_u = move / move.norm();
|
||||
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("X-Z move feed rate Z component should be equal to travel_speed_z") {
|
||||
const Vec3d move{10, 0, -10};
|
||||
const Vec3d move_u = move / move.norm();
|
||||
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("-X-Z move feed rate Z component should be equal to travel_speed_z") {
|
||||
const Vec3d move{-10, 0, -10};
|
||||
const Vec3d move_u = move / move.norm();
|
||||
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("YZ move feed rate Z component should be equal to travel_speed_z") {
|
||||
const Vec3d move{0, 10, 10};
|
||||
const Vec3d move_u = move / move.norm();
|
||||
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("-YZ move feed rate Z component should be equal to travel_speed_z") {
|
||||
const Vec3d move{0, -10, 10};
|
||||
const Vec3d move_u = move / move.norm();
|
||||
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("Y-Z move feed rate Z component should be equal to travel_speed_z") {
|
||||
const Vec3d move{0, 10, -10};
|
||||
const Vec3d move_u = move / move.norm();
|
||||
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("-Y-Z move feed rate Z component should be equal to travel_speed_z") {
|
||||
const Vec3d move{0, -10, -10};
|
||||
const Vec3d move_u = move / move.norm();
|
||||
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("XYZ move feed rate Z component should be equal to travel_speed_z") {
|
||||
const Vec3d move{10, 10, 10};
|
||||
const Vec3d move_u = move / move.norm();
|
||||
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("-XYZ move feed rate Z component should be equal to travel_speed_z") {
|
||||
const Vec3d move{-10, 10, 10};
|
||||
const Vec3d move_u = move / move.norm();
|
||||
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("X-YZ move feed rate Z component should be equal to travel_speed_z") {
|
||||
const Vec3d move{10, -10, 10};
|
||||
const Vec3d move_u = move / move.norm();
|
||||
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("-X-YZ move feed rate Z component should be equal to travel_speed_z") {
|
||||
const Vec3d move{-10, -10, 10};
|
||||
const Vec3d move_u = move / move.norm();
|
||||
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("XY-Z move feed rate Z component should be equal to travel_speed_z") {
|
||||
const Vec3d move{10, 10, -10};
|
||||
const Vec3d move_u = move / move.norm();
|
||||
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("-XY-Z move feed rate Z component should be equal to travel_speed_z") {
|
||||
const Vec3d move{-10, 10, -10};
|
||||
const Vec3d move_u = move / move.norm();
|
||||
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("X-Y-Z move feed rate Z component should be equal to travel_speed_z") {
|
||||
const Vec3d move{10, -10, -10};
|
||||
const Vec3d move_u = move / move.norm();
|
||||
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
THEN("-X-Y-Z move feed rate Z component should be equal to travel_speed_z") {
|
||||
const Vec3d move{-10, -10, -10};
|
||||
const Vec3d move_u = move / move.norm();
|
||||
const double speed = std::abs(writer.config.travel_speed_z.value / move_u.z());
|
||||
const Vec3d p1 = writer.get_position();
|
||||
const Vec3d p2 = p1 + move;
|
||||
const std::string result = writer.travel_to_xyz(p2);
|
||||
check_gcode_feedrate(result, writer.config, speed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("GCodeWriter emits G1 code correctly according to XYZF_EXPORT_DIGITS", "[GCodeWriter]") {
|
||||
GCodeWriter writer;
|
||||
|
||||
SECTION("Check quantize") {
|
||||
CHECK(GCodeFormatter::quantize(1.0,0) == 1.);
|
||||
CHECK(GCodeFormatter::quantize(0.0,0) == 0.);
|
||||
CHECK(GCodeFormatter::quantize(0.1,0) == 0);
|
||||
|
||||
CHECK(GCodeFormatter::quantize(1.0,1) == 1.);
|
||||
CHECK(GCodeFormatter::quantize(0.0,1) == 0.);
|
||||
CHECK(GCodeFormatter::quantize(0.1,1) == Approx(0.1));
|
||||
CHECK(GCodeFormatter::quantize(0.01,1) == 0.);
|
||||
|
||||
CHECK(GCodeFormatter::quantize(1.0,2) == 1.);
|
||||
CHECK(GCodeFormatter::quantize(0.0,2) == 0.);
|
||||
CHECK(GCodeFormatter::quantize(0.1,2) == Approx(0.1));
|
||||
CHECK(GCodeFormatter::quantize(0.01,2) == Approx(0.01));
|
||||
CHECK(GCodeFormatter::quantize(0.001,2) == 0.);
|
||||
|
||||
CHECK(GCodeFormatter::quantize(1.0,3) == 1.);
|
||||
CHECK(GCodeFormatter::quantize(0.0,3) == 0.);
|
||||
CHECK(GCodeFormatter::quantize(0.1,3) == Approx(0.1));
|
||||
CHECK(GCodeFormatter::quantize(0.01,3) == Approx(0.01));
|
||||
CHECK(GCodeFormatter::quantize(0.001,3) == Approx(0.001));
|
||||
CHECK(GCodeFormatter::quantize(0.0001,3) == 0.);
|
||||
|
||||
CHECK(GCodeFormatter::quantize(1.0,4) == 1.);
|
||||
CHECK(GCodeFormatter::quantize(0.0,4) == 0.);
|
||||
CHECK(GCodeFormatter::quantize(0.1,4) == Approx(0.1));
|
||||
CHECK(GCodeFormatter::quantize(0.01,4) == Approx(0.01));
|
||||
CHECK(GCodeFormatter::quantize(0.001,4) == Approx(0.001));
|
||||
CHECK(GCodeFormatter::quantize(0.0001,4) == Approx(0.0001));
|
||||
CHECK(GCodeFormatter::quantize(0.00001,4) == 0.);
|
||||
|
||||
CHECK(GCodeFormatter::quantize(1.0,5) == 1.);
|
||||
CHECK(GCodeFormatter::quantize(0.0,5) == 0.);
|
||||
CHECK(GCodeFormatter::quantize(0.1,5) == Approx(0.1));
|
||||
CHECK(GCodeFormatter::quantize(0.01,5) == Approx(0.01));
|
||||
CHECK(GCodeFormatter::quantize(0.001,5) == Approx(0.001));
|
||||
CHECK(GCodeFormatter::quantize(0.0001,5) == Approx(0.0001));
|
||||
CHECK(GCodeFormatter::quantize(0.00001,5) == Approx(0.00001));
|
||||
CHECK(GCodeFormatter::quantize(0.000001,5) == 0.);
|
||||
|
||||
CHECK(GCodeFormatter::quantize(1.0,6) == 1.);
|
||||
CHECK(GCodeFormatter::quantize(0.0,6) == 0.);
|
||||
CHECK(GCodeFormatter::quantize(0.1,6) == Approx(0.1));
|
||||
CHECK(GCodeFormatter::quantize(0.01,6) == Approx(0.01));
|
||||
CHECK(GCodeFormatter::quantize(0.001,6) == Approx(0.001));
|
||||
CHECK(GCodeFormatter::quantize(0.0001,6) == Approx(0.0001));
|
||||
CHECK(GCodeFormatter::quantize(0.00001,6) == Approx(0.00001));
|
||||
CHECK(GCodeFormatter::quantize(0.000001,6) == Approx(0.000001));
|
||||
CHECK(GCodeFormatter::quantize(0.0000001,6) == 0.);
|
||||
}
|
||||
|
||||
SECTION("Check pow_10") {
|
||||
// IEEE 754 floating point numbers can represent these numbers EXACTLY.
|
||||
CHECK(GCodeFormatter::pow_10[0] == 1.);
|
||||
CHECK(GCodeFormatter::pow_10[1] == 10.);
|
||||
CHECK(GCodeFormatter::pow_10[2] == 100.);
|
||||
CHECK(GCodeFormatter::pow_10[3] == 1000.);
|
||||
CHECK(GCodeFormatter::pow_10[4] == 10000.);
|
||||
CHECK(GCodeFormatter::pow_10[5] == 100000.);
|
||||
CHECK(GCodeFormatter::pow_10[6] == 1000000.);
|
||||
CHECK(GCodeFormatter::pow_10[7] == 10000000.);
|
||||
CHECK(GCodeFormatter::pow_10[8] == 100000000.);
|
||||
CHECK(GCodeFormatter::pow_10[9] == 1000000000.);
|
||||
}
|
||||
|
||||
SECTION("Check pow_10_inv") {
|
||||
// IEEE 754 floating point numbers can NOT represent these numbers exactly.
|
||||
CHECK(GCodeFormatter::pow_10_inv[0] == 1.);
|
||||
CHECK(GCodeFormatter::pow_10_inv[1] == 0.1);
|
||||
CHECK(GCodeFormatter::pow_10_inv[2] == 0.01);
|
||||
CHECK(GCodeFormatter::pow_10_inv[3] == 0.001);
|
||||
CHECK(GCodeFormatter::pow_10_inv[4] == 0.0001);
|
||||
CHECK(GCodeFormatter::pow_10_inv[5] == 0.00001);
|
||||
CHECK(GCodeFormatter::pow_10_inv[6] == 0.000001);
|
||||
CHECK(GCodeFormatter::pow_10_inv[7] == 0.0000001);
|
||||
CHECK(GCodeFormatter::pow_10_inv[8] == 0.00000001);
|
||||
CHECK(GCodeFormatter::pow_10_inv[9] == 0.000000001);
|
||||
}
|
||||
|
||||
SECTION("travel_to_z Emit G1 code for very significant movement") {
|
||||
double z1 = 10.0;
|
||||
std::string result1{ writer.travel_to_z(z1) };
|
||||
CHECK(result1 == "G1 Z10 F7800\n");
|
||||
|
||||
double z2 = z1 * 2;
|
||||
std::string result2{ writer.travel_to_z(z2) };
|
||||
CHECK(result2 == "G1 Z20 F7800\n");
|
||||
}
|
||||
|
||||
SECTION("travel_to_z Emit G1 code for significant movement") {
|
||||
double z1 = 10.0;
|
||||
std::string result1{ writer.travel_to_z(z1) };
|
||||
CHECK(result1 == "G1 Z10 F7800\n");
|
||||
|
||||
// This should test with XYZ_EPSILON exactly,
|
||||
// but IEEE 754 floating point numbers cannot pass the test.
|
||||
double z2 = z1 + GCodeFormatter::XYZ_EPSILON * 1.001;
|
||||
std::string result2{ writer.travel_to_z(z2) };
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "G1 Z"
|
||||
<< GCodeFormatter::quantize_xyzf(z2)
|
||||
<< " F7800\n";
|
||||
|
||||
CHECK(result2 == oss.str());
|
||||
}
|
||||
|
||||
SECTION("travel_to_z Do not emit G1 code for insignificant movement") {
|
||||
double z1 = 10.0;
|
||||
std::string result1{ writer.travel_to_z(z1) };
|
||||
CHECK(result1 == "G1 Z10 F7800\n");
|
||||
|
||||
// Movement smaller than XYZ_EPSILON
|
||||
double z2 = z1 + (GCodeFormatter::XYZ_EPSILON * 0.999);
|
||||
std::string result2{ writer.travel_to_z(z2) };
|
||||
CHECK(result2 == "");
|
||||
|
||||
double z3 = z1 + (GCodeFormatter::XYZ_EPSILON * 0.1);
|
||||
std::string result3{ writer.travel_to_z(z3) };
|
||||
CHECK(result3 == "");
|
||||
}
|
||||
|
||||
SECTION("travel_to_xyz Emit G1 code for very significant movement") {
|
||||
Vec3d v1{10.0, 10.0, 10.0};
|
||||
std::string result1{ writer.travel_to_xyz(v1) };
|
||||
CHECK(result1 == "G1 X10 Y10 Z10 F7800\n");
|
||||
|
||||
Vec3d v2 = v1 * 2;
|
||||
std::string result2{ writer.travel_to_xyz(v2) };
|
||||
CHECK(result2 == "G1 X20 Y20 Z20 F7800\n");
|
||||
}
|
||||
|
||||
SECTION("travel_to_xyz Emit G1 code for significant XYZ movement") {
|
||||
Vec3d v1{10.0, 10.0, 10.0};
|
||||
std::string result1{ writer.travel_to_xyz(v1) };
|
||||
CHECK(result1 == "G1 X10 Y10 Z10 F7800\n");
|
||||
|
||||
Vec3d v2 = v1;
|
||||
// This should test with XYZ_EPSILON exactly,
|
||||
// but IEEE 754 floating point numbers cannot pass the test.
|
||||
v2.array() += GCodeFormatter::XYZ_EPSILON * 1.001;
|
||||
std::string result2{ writer.travel_to_xyz(v2) };
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "G1 X"
|
||||
<< GCodeFormatter::quantize_xyzf(v2.x())
|
||||
<< " Y"
|
||||
<< GCodeFormatter::quantize_xyzf(v2.y())
|
||||
<< " Z"
|
||||
<< GCodeFormatter::quantize_xyzf(v2.z())
|
||||
<< " F7800\n";
|
||||
|
||||
CHECK(result2 == oss.str());
|
||||
}
|
||||
|
||||
SECTION("travel_to_xyz Emit G1 code for significant X movement") {
|
||||
Vec3d v1{10.0, 10.0, 10.0};
|
||||
std::string result1{ writer.travel_to_xyz(v1) };
|
||||
CHECK(result1 == "G1 X10 Y10 Z10 F7800\n");
|
||||
|
||||
Vec3d v2 = v1;
|
||||
// This should test with XYZ_EPSILON exactly,
|
||||
// but IEEE 754 floating point numbers cannot pass the test.
|
||||
v2.x() += GCodeFormatter::XYZ_EPSILON * 1.001;
|
||||
std::string result2{ writer.travel_to_xyz(v2) };
|
||||
|
||||
std::ostringstream oss;
|
||||
// Only X needs to be emitted in this case,
|
||||
// but this is how the code currently works.
|
||||
oss << "G1 X"
|
||||
<< GCodeFormatter::quantize_xyzf(v2.x())
|
||||
<< " Y"
|
||||
<< GCodeFormatter::quantize_xyzf(v2.y())
|
||||
<< " F7800\n";
|
||||
|
||||
CHECK(result2 == oss.str());
|
||||
}
|
||||
|
||||
SECTION("travel_to_xyz Emit G1 code for significant Y movement") {
|
||||
Vec3d v1{10.0, 10.0, 10.0};
|
||||
std::string result1{ writer.travel_to_xyz(v1) };
|
||||
CHECK(result1 == "G1 X10 Y10 Z10 F7800\n");
|
||||
|
||||
Vec3d v2 = v1;
|
||||
// This should test with XYZ_EPSILON exactly,
|
||||
// but IEEE 754 floating point numbers cannot pass the test.
|
||||
v2.y() += GCodeFormatter::XYZ_EPSILON * 1.001;
|
||||
std::string result2{ writer.travel_to_xyz(v2) };
|
||||
|
||||
std::ostringstream oss;
|
||||
// Only Y needs to be emitted in this case,
|
||||
// but this is how the code currently works.
|
||||
oss << "G1 X"
|
||||
<< GCodeFormatter::quantize_xyzf(v2.x())
|
||||
<< " Y"
|
||||
<< GCodeFormatter::quantize_xyzf(v2.y())
|
||||
<< " F7800\n";
|
||||
|
||||
CHECK(result2 == oss.str());
|
||||
}
|
||||
|
||||
SECTION("travel_to_xyz Emit G1 code for significant Z movement") {
|
||||
Vec3d v1{10.0, 10.0, 10.0};
|
||||
std::string result1{ writer.travel_to_xyz(v1) };
|
||||
CHECK(result1 == "G1 X10 Y10 Z10 F7800\n");
|
||||
|
||||
Vec3d v2 = v1;
|
||||
// This should test with XYZ_EPSILON exactly,
|
||||
// but IEEE 754 floating point numbers cannot pass the test.
|
||||
v2.z() += GCodeFormatter::XYZ_EPSILON * 1.001;
|
||||
std::string result2{ writer.travel_to_xyz(v2) };
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "G1 Z"
|
||||
<< GCodeFormatter::quantize_xyzf(v2.z())
|
||||
<< " F7800\n";
|
||||
|
||||
CHECK(result2 == oss.str());
|
||||
}
|
||||
|
||||
SECTION("travel_to_xyz Do not emit G1 code for insignificant movement") {
|
||||
Vec3d v1{10.0, 10.0, 10.0};
|
||||
std::string result1{ writer.travel_to_xyz(v1) };
|
||||
CHECK(result1 == "G1 X10 Y10 Z10 F7800\n");
|
||||
|
||||
// Movement smaller than XYZ_EPSILON
|
||||
Vec3d v2 = v1;
|
||||
v2.array() += GCodeFormatter::XYZ_EPSILON * 0.999;
|
||||
std::string result2{ writer.travel_to_xyz(v2) };
|
||||
CHECK(result2 == "");
|
||||
|
||||
Vec3d v3 = v1;
|
||||
v3.array() += GCodeFormatter::XYZ_EPSILON * 0.1;
|
||||
std::string result3{ writer.travel_to_xyz(v3) };
|
||||
CHECK(result3 == "");
|
||||
}
|
||||
}
|
||||
83
tests/fff_print/test_infill_above_bridges.cpp
Normal file
83
tests/fff_print/test_infill_above_bridges.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <libslic3r/InfillAboveBridges.hpp>
|
||||
|
||||
using namespace Slic3r;
|
||||
using Catch::Approx;
|
||||
|
||||
const ExPolygon square{
|
||||
Point::new_scale(0, 0),
|
||||
Point::new_scale(10, 0),
|
||||
Point::new_scale(10, 10),
|
||||
Point::new_scale(0, 10)
|
||||
};
|
||||
|
||||
ExPolygon translate(const ExPolygon &polygon, const Point &offset) {
|
||||
ExPolygons result{polygon};
|
||||
translate(result, offset);
|
||||
return result.front();
|
||||
}
|
||||
|
||||
constexpr bool debug_files{false};
|
||||
|
||||
void draw_surfaces(const PrepareInfill::SurfaceRefsByRegion &surfaces, std::string_view file_name) {
|
||||
using PrepareInfill::SurfaceCollectionRef;
|
||||
|
||||
SurfaceCollection to_display;
|
||||
for (const SurfaceCollectionRef &surface_collection : surfaces) {
|
||||
to_display.append(surface_collection.get());
|
||||
}
|
||||
to_display.export_to_svg(file_name.data(), false);
|
||||
}
|
||||
|
||||
TEST_CASE("Separate infill above bridges", "[PrepareInfill]") {
|
||||
ExPolygons layer_0_region_0_bridge{
|
||||
square
|
||||
};
|
||||
ExPolygons layer_0_region_0_internal{
|
||||
translate(square, Point::new_scale(10, 0))
|
||||
};
|
||||
ExPolygons layer_0_region_1_internal{
|
||||
translate(square, Point::new_scale(0, 10))
|
||||
};
|
||||
ExPolygons layer_0_region_1_bridge{
|
||||
translate(square, Point::new_scale(10, 10))
|
||||
};
|
||||
SurfaceCollection layer_0_region_0;
|
||||
layer_0_region_0.append(layer_0_region_0_bridge, stBottomBridge);
|
||||
layer_0_region_0.append(layer_0_region_0_internal, stInternal);
|
||||
SurfaceCollection layer_0_region_1;
|
||||
layer_0_region_1.append(layer_0_region_1_bridge, stBottomBridge);
|
||||
layer_0_region_1.append(layer_0_region_1_internal, stInternal);
|
||||
|
||||
PrepareInfill::SurfaceRefsByRegion layer_0{layer_0_region_0, layer_0_region_1};
|
||||
|
||||
ExPolygons layer_1_region_0_solid{
|
||||
translate(square, Point::new_scale(5, 5))
|
||||
};
|
||||
SurfaceCollection layer_1_region_0;
|
||||
layer_1_region_0.append(layer_1_region_0_solid, stInternalSolid);
|
||||
PrepareInfill::SurfaceRefsByRegion layer_1{layer_1_region_0};
|
||||
|
||||
if constexpr (debug_files) {
|
||||
draw_surfaces(layer_0, "layer_0.svg");
|
||||
}
|
||||
|
||||
PrepareInfill::separate_infill_above_bridges({layer_0, layer_1}, 0);
|
||||
|
||||
if constexpr (debug_files) {
|
||||
draw_surfaces(layer_1, "layer_1.svg");
|
||||
}
|
||||
|
||||
const Surfaces &result{layer_1.front().get().surfaces};
|
||||
REQUIRE(result.size() == 4);
|
||||
const double expected_area{scale_(5.0) * scale_(5.0)};
|
||||
CHECK(result[0].expolygon.contour.area() == Approx(expected_area));
|
||||
CHECK(result[0].surface_type == stInternalSolid);
|
||||
CHECK(result[1].expolygon.contour.area() == Approx(expected_area));
|
||||
CHECK(result[1].surface_type == stInternalSolid);
|
||||
CHECK(result[2].expolygon.contour.area() == Approx(expected_area));
|
||||
CHECK(result[2].surface_type == stSolidOverBridge);
|
||||
CHECK(result[3].expolygon.contour.area() == Approx(expected_area));
|
||||
CHECK(result[3].surface_type == stSolidOverBridge);
|
||||
}
|
||||
@@ -2,11 +2,13 @@
|
||||
* Ported from t/layers.t
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include "test_data.hpp"
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Slic3r::Test;
|
||||
using namespace Catch;
|
||||
|
||||
void check_layers(const DynamicPrintConfig& config) {
|
||||
GCodeReader parser;
|
||||
@@ -27,10 +29,10 @@ void check_layers(const DynamicPrintConfig& config) {
|
||||
const double layer_height = config.opt_float("layer_height");
|
||||
INFO("Correct first layer height.");
|
||||
CHECK(z.at(0) == Approx(first_layer_height + z_offset));
|
||||
INFO("Correct second layer height")
|
||||
INFO("Correct second layer height");
|
||||
CHECK(z.at(1) == Approx(first_layer_height + layer_height + z_offset));
|
||||
|
||||
INFO("Correct layer height")
|
||||
INFO("Correct layer height");
|
||||
for (const double increment : tcb::span{increments}.subspan(1)) {
|
||||
CHECK(increment == Approx(layer_height));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Model.hpp"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Print.hpp"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/GCodeReader.hpp"
|
||||
@@ -10,6 +11,7 @@
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Slic3r::Test;
|
||||
using namespace Catch;
|
||||
|
||||
boost::regex perimeters_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; perimeter");
|
||||
boost::regex infill_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; infill");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Print.hpp"
|
||||
@@ -8,6 +9,7 @@
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Slic3r::Test;
|
||||
using namespace Catch;
|
||||
|
||||
SCENARIO("PrintObject: object layer heights", "[PrintObject]") {
|
||||
GIVEN("20mm cube and default initial config, initial layer height of 2mm") {
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
* Ported from t/retraction.t
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
|
||||
#include <libslic3r/GCodeReader.hpp>
|
||||
#include <libslic3r/Config.hpp>
|
||||
#include "libslic3r/GCodeReader.hpp"
|
||||
#include "libslic3r/GCode/GCodeWriter.hpp"
|
||||
#include "libslic3r/Config.hpp"
|
||||
|
||||
#include "test_data.hpp"
|
||||
#include <regex>
|
||||
@@ -13,6 +15,7 @@
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Test;
|
||||
using namespace Catch;
|
||||
|
||||
constexpr bool debug_files {false};
|
||||
|
||||
@@ -59,6 +62,10 @@ void check_gcode(std::initializer_list<TestMesh> meshes, const DynamicPrintConfi
|
||||
const double retract_restart_extra = config.option<ConfigOptionFloats>("retract_restart_extra")->get_at(tool);
|
||||
const double retract_restart_extra_toolchange = config.option<ConfigOptionFloats>("retract_restart_extra_toolchange")->get_at(tool);
|
||||
|
||||
const double travel_speed = config.opt_float("travel_speed");
|
||||
|
||||
const double feedrate = line.has_f() ? line.f() : self.f();
|
||||
|
||||
if (line.dist_Z(self) != 0) {
|
||||
// lift move or lift + change layer
|
||||
const double retract_lift = config.option<ConfigOptionFloats>("retract_lift")->get_at(tool);
|
||||
@@ -77,7 +84,7 @@ void check_gcode(std::initializer_list<TestMesh> meshes, const DynamicPrintConfi
|
||||
lift_dist = line.dist_Z(self);
|
||||
}
|
||||
if (line.dist_Z(self) < 0) {
|
||||
INFO("Must be lifted before going down.")
|
||||
INFO("Must be lifted before going down.");
|
||||
CHECK(lifted);
|
||||
INFO("Going down by the same amount of the lift or by the amount needed to get to next layer");
|
||||
CHECK((
|
||||
@@ -87,9 +94,26 @@ void check_gcode(std::initializer_list<TestMesh> meshes, const DynamicPrintConfi
|
||||
lift_dist = 0;
|
||||
lifted = false;
|
||||
}
|
||||
const double feedrate = line.has_f() ? line.f() : self.f();
|
||||
INFO("move Z at travel speed");
|
||||
CHECK(feedrate == Approx(config.opt_float("travel_speed") * 60));
|
||||
const double travel_speed_z = config.opt_float("travel_speed_z");
|
||||
if (travel_speed_z) {
|
||||
Vec3d move{line.dist_X(self), line.dist_Y(self), line.dist_Z(self)};
|
||||
const double move_u_z = move.z() / move.norm();
|
||||
const double travel_speed_ = std::abs(travel_speed_z / move_u_z);
|
||||
INFO("move Z feedrate Z component is less than or equal to travel_speed_z");
|
||||
CHECK(feedrate * std::abs(move_u_z) <= Approx(travel_speed_z * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
|
||||
if (travel_speed_ < travel_speed) {
|
||||
INFO("move Z at travel speed Z");
|
||||
CHECK(feedrate == Approx(travel_speed_ * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
|
||||
INFO("move Z feedrate Z component is equal to travel_speed_z");
|
||||
CHECK(feedrate * std::abs(move_u_z) == Approx(travel_speed_z * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
|
||||
} else {
|
||||
INFO("move Z at travel speed");
|
||||
CHECK(feedrate == Approx(travel_speed * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
|
||||
}
|
||||
} else {
|
||||
INFO("move Z at travel speed");
|
||||
CHECK(feedrate == Approx(travel_speed * 60).epsilon(GCodeFormatter::XYZ_EPSILON));
|
||||
}
|
||||
}
|
||||
if (line.retracting(self)) {
|
||||
retracted[tool] = true;
|
||||
@@ -135,11 +159,6 @@ void test_slicing(std::initializer_list<TestMesh> meshes, DynamicPrintConfig& co
|
||||
check_gcode(meshes, config, duplicate);
|
||||
}
|
||||
|
||||
SECTION("Negative restart extra length") {
|
||||
config.set_deserialize_strict({{ "retract_restart_extra", "-1" }});
|
||||
check_gcode(meshes, config, duplicate);
|
||||
}
|
||||
|
||||
SECTION("Retract_lift") {
|
||||
config.set_deserialize_strict({{ "retract_lift", "1,2" }});
|
||||
check_gcode(meshes, config, duplicate);
|
||||
@@ -147,7 +166,7 @@ void test_slicing(std::initializer_list<TestMesh> meshes, DynamicPrintConfig& co
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("Slicing with retraction and lifing", "[retraction]") {
|
||||
TEST_CASE("Slicing with retraction and lifting", "[retraction]") {
|
||||
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||
config.set_deserialize_strict({
|
||||
{ "nozzle_diameter", "0.6,0.6,0.6,0.6" },
|
||||
@@ -176,6 +195,37 @@ TEST_CASE("Slicing with retraction and lifing", "[retraction]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Slicing with retraction and lifting with travel_speed_z=10", "[retraction]") {
|
||||
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||
config.set_deserialize_strict({
|
||||
{ "nozzle_diameter", "0.6,0.6,0.6,0.6" },
|
||||
{ "first_layer_height", config.opt_float("layer_height") },
|
||||
{ "first_layer_speed", "100%" },
|
||||
{ "start_gcode", "" }, // To avoid dealing with the nozzle lift in start G-code
|
||||
{ "retract_length", "1.5" },
|
||||
{ "retract_before_travel", "3" },
|
||||
{ "retract_layer_change", "1" },
|
||||
{ "only_retract_when_crossing_perimeters", 0 },
|
||||
{ "travel_speed", "600" },
|
||||
{ "travel_speed_z", "10" },
|
||||
});
|
||||
|
||||
SECTION("Standard run") {
|
||||
test_slicing({TestMesh::cube_20x20x20}, config);
|
||||
}
|
||||
SECTION("With duplicate cube") {
|
||||
test_slicing({TestMesh::cube_20x20x20}, config, 2);
|
||||
}
|
||||
SECTION("Dual extruder with multiple skirt layers") {
|
||||
config.set_deserialize_strict({
|
||||
{"infill_extruder", 2},
|
||||
{"skirts", 4},
|
||||
{"skirt_height", 3},
|
||||
});
|
||||
test_slicing({TestMesh::cube_20x20x20}, config);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Z moves", "[retraction]") {
|
||||
|
||||
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#include <libslic3r/Point.hpp>
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <libslic3r/GCode/SeamAligned.hpp>
|
||||
#include "test_data.hpp"
|
||||
#include <fstream>
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Slic3r::Seams;
|
||||
using namespace Catch;
|
||||
|
||||
constexpr bool debug_files{false};
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#include <libslic3r/Point.hpp>
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/matchers/catch_matchers_vector.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <libslic3r/GCode/SeamGeometry.hpp>
|
||||
#include <libslic3r/Geometry.hpp>
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Catch;
|
||||
|
||||
TEST_CASE("Lists mapping", "[Seams][SeamGeometry]") {
|
||||
// clang-format off
|
||||
@@ -123,50 +126,3 @@ TEST_CASE("Vertex angle is rotation agnostic", "[Seams][SeamGeometry]") {
|
||||
std::vector<double> rotated_angles = Seams::Geometry::get_vertex_angles(points, 0.1);
|
||||
CHECK(rotated_angles[1] == Approx(angles[1]));
|
||||
}
|
||||
|
||||
TEST_CASE("Calculate overhangs", "[Seams][SeamGeometry]") {
|
||||
const ExPolygon square{
|
||||
scaled(Vec2d{0.0, 0.0}),
|
||||
scaled(Vec2d{1.0, 0.0}),
|
||||
scaled(Vec2d{1.0, 1.0}),
|
||||
scaled(Vec2d{0.0, 1.0})
|
||||
};
|
||||
const std::vector<Vec2d> points{Seams::Geometry::unscaled(square.contour.points)};
|
||||
ExPolygon previous_layer{square};
|
||||
previous_layer.translate(scaled(Vec2d{-0.5, 0}));
|
||||
AABBTreeLines::LinesDistancer<Linef> previous_layer_distancer{
|
||||
to_unscaled_linesf({previous_layer})};
|
||||
const std::vector<double> overhangs{
|
||||
Seams::Geometry::get_overhangs(points, previous_layer_distancer, 0.5)};
|
||||
REQUIRE(overhangs.size() == points.size());
|
||||
CHECK_THAT(overhangs, Catch::Matchers::Approx(std::vector<double>{
|
||||
0.0, M_PI / 4.0, M_PI / 4.0, 0.0
|
||||
}));
|
||||
}
|
||||
|
||||
const Linesf lines{to_unscaled_linesf({ExPolygon{
|
||||
scaled(Vec2d{0.0, 0.0}),
|
||||
scaled(Vec2d{1.0, 0.0}),
|
||||
scaled(Vec2d{1.0, 1.0}),
|
||||
scaled(Vec2d{0.0, 1.0})
|
||||
}})};
|
||||
|
||||
TEST_CASE("Offset along loop lines forward", "[Seams][SeamGeometry]") {
|
||||
const std::optional<Seams::Geometry::PointOnLine> result{Seams::Geometry::offset_along_lines(
|
||||
{0.5, 0.0}, 0, lines, 3.9, Seams::Geometry::Direction1D::forward
|
||||
)};
|
||||
REQUIRE(result);
|
||||
const auto &[point, line_index] = *result;
|
||||
CHECK((scaled(point) - Point::new_scale(0.4, 0.0)).norm() < scaled(EPSILON));
|
||||
CHECK(line_index == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Offset along loop lines backward", "[Seams][SeamGeometry]") {
|
||||
const std::optional<Seams::Geometry::PointOnLine> result{Seams::Geometry::offset_along_lines(
|
||||
{1.0, 0.5}, 1, lines, 1.8, Seams::Geometry::Direction1D::backward
|
||||
)};
|
||||
REQUIRE(result);
|
||||
const auto &[point, line_index] = *result;
|
||||
CHECK((scaled(point) - Point::new_scale(0.0, 0.3)).norm() < scaled(EPSILON));
|
||||
CHECK(line_index == 3);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
#include "libslic3r/GCode/SeamPerimeters.hpp"
|
||||
#include "libslic3r/Layer.hpp"
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <libslic3r/GCode/SeamGeometry.hpp>
|
||||
#include <libslic3r/Geometry.hpp>
|
||||
#include <fstream>
|
||||
@@ -11,26 +12,29 @@
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Slic3r::Seams;
|
||||
using namespace Catch;
|
||||
|
||||
constexpr bool debug_files{false};
|
||||
|
||||
const ExPolygon square{
|
||||
scaled(Vec2d{0.0, 0.0}), scaled(Vec2d{1.0, 0.0}), scaled(Vec2d{1.0, 1.0}),
|
||||
scaled(Vec2d{0.0, 1.0})};
|
||||
|
||||
TEST_CASE("Oversample painted", "[Seams][SeamPerimeters]") {
|
||||
Perimeters::PerimeterPoints square(4);
|
||||
square[0].position = Vec2d{0.0, 0.0};
|
||||
square[1].position = Vec2d{1.0, 0.0};
|
||||
square[2].position = Vec2d{1.0, 1.0};
|
||||
square[3].position = Vec2d{0.0, 1.0};
|
||||
|
||||
auto is_painted{[](const Vec3f &position, float radius) {
|
||||
return (position - Vec3f{0.5, 0.0, 1.0}).norm() < radius;
|
||||
}};
|
||||
std::vector<Vec2d> points{Perimeters::Impl::oversample_painted(
|
||||
Seams::Geometry::unscaled(square.contour.points), is_painted, 1.0, 0.2
|
||||
Perimeters::PerimeterPoints points{Perimeters::Impl::oversample_painted(
|
||||
square, is_painted, 1.0, 0.2
|
||||
)};
|
||||
|
||||
REQUIRE(points.size() == 8);
|
||||
CHECK((points[1] - Vec2d{0.2, 0.0}).norm() == Approx(0.0));
|
||||
CHECK((points[1].position - Vec2d{0.2, 0.0}).norm() == Approx(0.0));
|
||||
|
||||
points = Perimeters::Impl::oversample_painted(
|
||||
Seams::Geometry::unscaled(square.contour.points), is_painted, 1.0, 0.199
|
||||
square, is_painted, 1.0, 0.199
|
||||
);
|
||||
CHECK(points.size() == 9);
|
||||
}
|
||||
@@ -39,24 +43,35 @@ TEST_CASE("Remove redundant points", "[Seams][SeamPerimeters]") {
|
||||
using Perimeters::PointType;
|
||||
using Perimeters::PointClassification;
|
||||
|
||||
std::vector<Vec2d> points{{0.0, 0.0}, {1.0, 0.0}, {2.0, 0.0}, {3.0, 0.0},
|
||||
{3.0, 1.0}, {3.0, 2.0}, {0.0, 2.0}};
|
||||
std::vector<PointType> point_types{PointType::common,
|
||||
PointType::enforcer, // Should keep this.
|
||||
PointType::enforcer, // Should keep this.
|
||||
PointType::blocker,
|
||||
PointType::blocker, // Should remove this.
|
||||
PointType::blocker, PointType::common};
|
||||
Perimeters::PerimeterPoints points(9);
|
||||
points[0].position = {0.0, 0.0};
|
||||
points[0].type = PointType::common;
|
||||
points[1].position = {1.0, 0.0};
|
||||
points[1].type = PointType::enforcer; // Should keep
|
||||
points[2].position = {2.0, 0.0};
|
||||
points[2].type = PointType::enforcer; // Should keep
|
||||
points[3].position = {3.0, 0.0};
|
||||
points[3].type = PointType::blocker;
|
||||
points[4].position = {3.0, 1.0};
|
||||
points[4].type = PointType::blocker; // Should remove
|
||||
points[5].position = {3.0, 1.1};
|
||||
points[5].type = PointType::blocker;
|
||||
points[6].position = {3.0, 1.2};
|
||||
points[6].type = PointType::blocker;
|
||||
points[6].classification = PointClassification::overhang; // Should keep
|
||||
points[7].position = {3.0, 2.0};
|
||||
points[7].type = PointType::blocker;
|
||||
points[8].position = {0.0, 2.0};
|
||||
points[8].type = PointType::common;
|
||||
|
||||
const auto [resulting_points, resulting_point_types]{
|
||||
Perimeters::Impl::remove_redundant_points(points, point_types, 0.1)};
|
||||
Perimeters::PerimeterPoints result{
|
||||
Perimeters::Impl::remove_redundant_points(points, 0.1)};
|
||||
|
||||
REQUIRE(resulting_points.size() == 6);
|
||||
REQUIRE(resulting_point_types.size() == 6);
|
||||
CHECK((resulting_points[3] - Vec2d{3.0, 0.0}).norm() == Approx(0.0));
|
||||
CHECK((resulting_points[4] - Vec2d{3.0, 2.0}).norm() == Approx(0.0));
|
||||
CHECK(resulting_point_types[3] == PointType::blocker);
|
||||
CHECK(resulting_point_types[4] == PointType::blocker);
|
||||
REQUIRE(result.size() == 8);
|
||||
CHECK((result[3].position - Vec2d{3.0, 0.0}).norm() == Approx(0.0));
|
||||
CHECK((result[4].position - Vec2d{3.0, 1.1}).norm() == Approx(0.0));
|
||||
CHECK(result[3].type == PointType::blocker);
|
||||
CHECK(result[4].type == PointType::blocker);
|
||||
}
|
||||
|
||||
TEST_CASE("Perimeter constructs KD trees", "[Seams][SeamPerimeters]") {
|
||||
@@ -89,8 +104,6 @@ TEST_CASE("Perimeter constructs KD trees", "[Seams][SeamPerimeters]") {
|
||||
CHECK(perimeter.common_points.embedded_points);
|
||||
}
|
||||
|
||||
using std::filesystem::path;
|
||||
|
||||
constexpr const char *to_string(Perimeters::PointType point_type) {
|
||||
using Perimeters::PointType;
|
||||
|
||||
@@ -182,3 +195,57 @@ TEST_CASE_METHOD(Test::SeamsFixture, "Create perimeters", "[Seams][SeamPerimeter
|
||||
serialize_shells(csv, shells);
|
||||
}
|
||||
}
|
||||
|
||||
using Dir = Seams::Geometry::Direction1D;
|
||||
|
||||
Perimeters::Perimeter get_perimeter(){
|
||||
Perimeters::Perimeter perimeter;
|
||||
perimeter.positions = {
|
||||
Vec2d{0.0, 0.0},
|
||||
Vec2d{1.0, 0.0},
|
||||
Vec2d{1.0, 1.0},
|
||||
Vec2d{0.0, 1.0}
|
||||
};
|
||||
return perimeter;
|
||||
}
|
||||
|
||||
TEST_CASE("Offset along perimeter forward", "[Seams][SeamPerimeters]") {
|
||||
const std::optional<Perimeters::PointOnPerimeter> result{Perimeters::offset_along_perimeter(
|
||||
{0, 1, {0.5, 0.0}}, get_perimeter(), 3.9, Dir::forward,
|
||||
[](const Perimeters::Perimeter &, const std::size_t) { return false; }
|
||||
)};
|
||||
REQUIRE(result);
|
||||
const auto &[previous_index, next_index, point] = *result;
|
||||
CHECK((scaled(point) - Point::new_scale(0.4, 0.0)).norm() < scaled(EPSILON));
|
||||
CHECK(previous_index == 0);
|
||||
CHECK(next_index == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("Offset along perimeter backward", "[Seams][SeamPerimeters]") {
|
||||
const std::optional<Perimeters::PointOnPerimeter> result{Perimeters::offset_along_perimeter(
|
||||
{1, 2, {1.0, 0.5}}, get_perimeter(), 1.8, Dir::backward,
|
||||
[](const Perimeters::Perimeter &, const std::size_t) { return false; }
|
||||
)};
|
||||
REQUIRE(result);
|
||||
const auto &[previous_index, next_index, point] = *result;
|
||||
CHECK((scaled(point) - Point::new_scale(0.0, 0.3)).norm() < scaled(EPSILON));
|
||||
CHECK(previous_index == 3);
|
||||
CHECK(next_index == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Offset along perimeter forward respects stop condition", "[Seams][SeamPerimeters]") {
|
||||
Perimeters::Perimeter perimeter{get_perimeter()};
|
||||
perimeter.point_types = std::vector<Perimeters::PointType>(perimeter.positions.size(), Perimeters::PointType::common);
|
||||
perimeter.point_types[2] = Perimeters::PointType::blocker;
|
||||
const std::optional<Perimeters::PointOnPerimeter> result{Perimeters::offset_along_perimeter(
|
||||
{0, 1, {0.5, 0.0}}, perimeter, 3.9, Dir::forward,
|
||||
[](const Perimeters::Perimeter &perimeter, const std::size_t index) {
|
||||
return perimeter.point_types[index] == Perimeters::PointType::blocker;
|
||||
}
|
||||
)};
|
||||
REQUIRE(result);
|
||||
const auto &[previous_index, next_index, point] = *result;
|
||||
CHECK((scaled(point) - Point::new_scale(1.0, 0.0)).norm() < scaled(EPSILON));
|
||||
CHECK(previous_index == 1);
|
||||
CHECK(next_index == 1);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <libslic3r/Point.hpp>
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <libslic3r/GCode/SeamRandom.hpp>
|
||||
#include "test_data.hpp"
|
||||
#include <fstream>
|
||||
@@ -32,7 +32,7 @@ Perimeters::Perimeter get_perimeter() {
|
||||
}
|
||||
} // namespace RandomTest
|
||||
|
||||
double get_chi2_uniform(const std::vector<double> &data, double min, double max, const std::size_t bin_count) {
|
||||
double get_chi2_uniform(const std::vector<double> &data, const double min, const double max, const std::size_t bin_count) {
|
||||
std::vector<std::size_t> bins(bin_count);
|
||||
const double bin_size{(max - min) / bin_count};
|
||||
const double expected_frequncy{static_cast<double>(data.size()) / bin_count};
|
||||
@@ -62,7 +62,7 @@ TEST_CASE("Random is uniform", "[Seams][SeamRandom]") {
|
||||
return choice->position.x();
|
||||
});
|
||||
const std::size_t degrees_of_freedom{10};
|
||||
const double critical{18.307}; // dof 10, significance 0.05
|
||||
const double critical{29.588}; // dof 10, significance 0.001
|
||||
|
||||
CHECK(get_chi2_uniform(x_positions, 0.0, 1.0, degrees_of_freedom + 1) < critical);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <libslic3r/Point.hpp>
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <libslic3r/GCode/SeamRear.hpp>
|
||||
#include "test_data.hpp"
|
||||
#include <fstream>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <libslic3r/GCode/SeamScarf.hpp>
|
||||
#include <libslic3r/GCode/SmoothPath.hpp>
|
||||
|
||||
using namespace Slic3r;
|
||||
using Seams::Scarf::Impl::PathPoint;
|
||||
using namespace Catch;
|
||||
|
||||
TEST_CASE("Get path point", "[Seams][Scarf]") {
|
||||
using Seams::Scarf::Impl::get_path_point;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
@@ -9,6 +10,7 @@
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Slic3r::Seams;
|
||||
using namespace Catch;
|
||||
|
||||
struct ProjectionFixture
|
||||
{
|
||||
@@ -21,7 +23,12 @@ struct ProjectionFixture
|
||||
double extrusion_width{0.2};
|
||||
|
||||
ProjectionFixture() {
|
||||
extrusions.emplace_back(Polygon{extrusion_path}, extrusion_path.bounding_box(), extrusion_width, island_boundary);
|
||||
extrusions.emplace_back(
|
||||
Polygon{extrusion_path},
|
||||
extrusion_path.bounding_box(),
|
||||
extrusion_width, island_boundary,
|
||||
Seams::Geometry::Overhangs{}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/GCodeReader.hpp"
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
|
||||
#include "libslic3r/GCodeReader.hpp"
|
||||
#include "libslic3r/Config.hpp"
|
||||
@@ -10,6 +11,7 @@
|
||||
|
||||
using namespace Slic3r::Test;
|
||||
using namespace Slic3r;
|
||||
using namespace Catch;
|
||||
|
||||
/// Helper method to find the tool used for the brim (always the first extrusion)
|
||||
static int get_brim_tool(const std::string &gcode)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/GCodeReader.hpp"
|
||||
#include "libslic3r/Layer.hpp"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "test_data.hpp" // get access to init_print, etc
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
#include "libslic3r/TriangleMeshSlicer.hpp"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/Format/3mf.hpp"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <algorithm>
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <test_utils.hpp>
|
||||
|
||||
#include <libslic3r/TriangleMesh.hpp>
|
||||
@@ -7,6 +8,7 @@
|
||||
#include <libslic3r/AABBTreeLines.hpp>
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Catch;
|
||||
|
||||
TEST_CASE("Building a tree over a box, ray caster and closest query", "[AABBIndirect]")
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <libslic3r/libslic3r.h>
|
||||
#include <libslic3r/AnyPtr.hpp>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "libslic3r/Arachne/WallToolPaths.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <test_utils.hpp>
|
||||
|
||||
#include <random>
|
||||
@@ -12,6 +13,7 @@
|
||||
#include <libslic3r/libslic3r.h>
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Catch;
|
||||
|
||||
TEST_CASE("arc basics", "[ArcWelder]") {
|
||||
using namespace Slic3r::Geometry;
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/AStar.hpp"
|
||||
@@ -6,6 +10,7 @@
|
||||
#include "libslic3r/PointGrid.hpp"
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Catch;
|
||||
|
||||
TEST_CASE("Testing basic invariants of AStar", "[AStar]") {
|
||||
struct DummyTracer {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <boost/filesystem.hpp>
|
||||
@@ -8,6 +9,7 @@
|
||||
#include "libslic3r/SVG.hpp"
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Catch;
|
||||
|
||||
// #define TESTS_EXPORT_SVGS
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <iostream>
|
||||
@@ -9,6 +10,7 @@
|
||||
#include "libslic3r/SVG.hpp"
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Catch;
|
||||
|
||||
SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
|
||||
// CCW oriented contour
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include "libslic3r/libslic3r.h"
|
||||
|
||||
#include "libslic3r/Color.hpp"
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/matchers/catch_matchers.hpp>
|
||||
#include <catch2/matchers/catch_matchers_vector.hpp>
|
||||
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include <test_utils.hpp>
|
||||
|
||||
#include <libslic3r/Geometry/Curves.hpp>
|
||||
#include <libslic3r/Utils.hpp>
|
||||
#include <libslic3r/SVG.hpp>
|
||||
|
||||
using namespace Catch;
|
||||
|
||||
TEST_CASE("Curves: cubic b spline fit test", "[Curves]") {
|
||||
using namespace Slic3r;
|
||||
using namespace Slic3r::Geometry;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <libslic3r/CutSurface.hpp>
|
||||
#include <libslic3r/TriangleMesh.hpp> // its_make_cube + its_merge
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user