mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-24 09:11:23 -06:00
Merge branch 'master' into pa_reprap_g10_temperature_support
This commit is contained in:
commit
541c31afb0
323 changed files with 28621 additions and 8886 deletions
|
|
@ -92,6 +92,7 @@ endif()
|
|||
# Create a slic3r executable
|
||||
# Process mainfests for various platforms.
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/msw/PrusaSlicer.rc.in ${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer.rc @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/msw/PrusaSlicer-gcodeviewer.rc.in ${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer-gcodeviewer.rc @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/msw/PrusaSlicer.manifest.in ${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer.manifest @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/osx/Info.plist.in ${CMAKE_CURRENT_BINARY_DIR}/Info.plist @ONLY)
|
||||
if (WIN32)
|
||||
|
|
@ -105,9 +106,9 @@ if (MINGW)
|
|||
set_target_properties(PrusaSlicer PROPERTIES PREFIX "")
|
||||
endif (MINGW)
|
||||
|
||||
if (NOT WIN32)
|
||||
# Binary name on unix like systems (OSX, Linux)
|
||||
set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer")
|
||||
if (NOT WIN32 AND NOT APPLE)
|
||||
# Binary name on unix like systems (Linux, Unix)
|
||||
set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer")
|
||||
endif ()
|
||||
|
||||
target_link_libraries(PrusaSlicer libslic3r cereal)
|
||||
|
|
@ -161,12 +162,22 @@ if (WIN32)
|
|||
add_executable(PrusaSlicer_app_console PrusaSlicer_app_msvc.cpp ${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer.rc)
|
||||
# Generate debug symbols even in release mode.
|
||||
if (MSVC)
|
||||
target_link_options(PrusaSlicer_app_console PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>")
|
||||
target_link_options(PrusaSlicer_app_console PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>")
|
||||
endif ()
|
||||
target_compile_definitions(PrusaSlicer_app_console PRIVATE -DSLIC3R_WRAPPER_CONSOLE)
|
||||
add_dependencies(PrusaSlicer_app_console PrusaSlicer)
|
||||
set_target_properties(PrusaSlicer_app_console PROPERTIES OUTPUT_NAME "prusa-slicer-console")
|
||||
target_link_libraries(PrusaSlicer_app_console PRIVATE boost_headeronly)
|
||||
|
||||
add_executable(PrusaSlicer_app_gcodeviewer WIN32 PrusaSlicer_app_msvc.cpp ${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer-gcodeviewer.rc)
|
||||
# Generate debug symbols even in release mode.
|
||||
if (MSVC)
|
||||
target_link_options(PrusaSlicer_app_gcodeviewer PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>")
|
||||
endif ()
|
||||
target_compile_definitions(PrusaSlicer_app_gcodeviewer PRIVATE -DSLIC3R_WRAPPER_NOCONSOLE -DSLIC3R_WRAPPER_GCODEVIEWER)
|
||||
add_dependencies(PrusaSlicer_app_gcodeviewer PrusaSlicer)
|
||||
set_target_properties(PrusaSlicer_app_gcodeviewer PROPERTIES OUTPUT_NAME "prusa-gcodeviewer")
|
||||
target_link_libraries(PrusaSlicer_app_gcodeviewer PRIVATE boost_headeronly)
|
||||
endif ()
|
||||
|
||||
# Link the resources dir to where Slic3r GUI expects it
|
||||
|
|
@ -198,20 +209,34 @@ if (WIN32)
|
|||
add_custom_target(PrusaSlicerDllsCopy ALL DEPENDS PrusaSlicer)
|
||||
prusaslicer_copy_dlls(PrusaSlicerDllsCopy)
|
||||
|
||||
elseif (XCODE)
|
||||
# Because of Debug/Release/etc. configurations (similar to MSVC) the slic3r binary is located in an extra level
|
||||
add_custom_command(TARGET PrusaSlicer POST_BUILD
|
||||
COMMAND ln -sfn "${SLIC3R_RESOURCES_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/resources"
|
||||
COMMENT "Symlinking the resources directory into the build tree"
|
||||
VERBATIM
|
||||
)
|
||||
else ()
|
||||
if (APPLE)
|
||||
# On OSX, the name of the binary matches the name of the Application.
|
||||
add_custom_command(TARGET PrusaSlicer POST_BUILD
|
||||
COMMAND ln -sf PrusaSlicer prusa-slicer
|
||||
COMMAND ln -sf PrusaSlicer prusa-gcodeviewer
|
||||
COMMAND ln -sf PrusaSlicer PrusaGCodeViewer
|
||||
WORKING_DIRECTORY "$<TARGET_FILE_DIR:PrusaSlicer>"
|
||||
COMMENT "Symlinking the G-code viewer to PrusaSlicer, symlinking to prusa-slicer and prusa-gcodeviewer"
|
||||
VERBATIM)
|
||||
else ()
|
||||
add_custom_command(TARGET PrusaSlicer POST_BUILD
|
||||
COMMAND ln -sf prusa-slicer prusa-gcodeviewer
|
||||
WORKING_DIRECTORY "$<TARGET_FILE_DIR:PrusaSlicer>"
|
||||
COMMENT "Symlinking the G-code viewer to PrusaSlicer"
|
||||
VERBATIM)
|
||||
endif ()
|
||||
if (XCODE)
|
||||
# Because of Debug/Release/etc. configurations (similar to MSVC) the slic3r binary is located in an extra level
|
||||
set(BIN_RESOURCES_DIR "${CMAKE_CURRENT_BINARY_DIR}/resources")
|
||||
else ()
|
||||
set(BIN_RESOURCES_DIR "${CMAKE_CURRENT_BINARY_DIR}/../resources")
|
||||
endif ()
|
||||
add_custom_command(TARGET PrusaSlicer POST_BUILD
|
||||
COMMAND ln -sfn "${SLIC3R_RESOURCES_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/../resources"
|
||||
COMMAND ln -sfn "${SLIC3R_RESOURCES_DIR}" "${BIN_RESOURCES_DIR}"
|
||||
COMMENT "Symlinking the resources directory into the build tree"
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
VERBATIM)
|
||||
endif ()
|
||||
|
||||
# Slic3r binary install target
|
||||
if (WIN32)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/nowide/args.hpp>
|
||||
#include <boost/nowide/cenv.hpp>
|
||||
|
|
@ -44,6 +45,7 @@
|
|||
#include "libslic3r/Format/OBJ.hpp"
|
||||
#include "libslic3r/Format/SL1.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
|
||||
#include "PrusaSlicer.hpp"
|
||||
|
||||
|
|
@ -52,7 +54,6 @@
|
|||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/3DScene.hpp"
|
||||
#include "slic3r/GUI/InstanceCheck.hpp"
|
||||
#include "slic3r/GUI/AppConfig.hpp"
|
||||
#include "slic3r/GUI/MainFrame.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#endif /* SLIC3R_GUI */
|
||||
|
|
@ -101,7 +102,14 @@ int CLI::run(int argc, char **argv)
|
|||
std::find(m_transforms.begin(), m_transforms.end(), "cut") == m_transforms.end() &&
|
||||
std::find(m_transforms.begin(), m_transforms.end(), "cut_x") == m_transforms.end() &&
|
||||
std::find(m_transforms.begin(), m_transforms.end(), "cut_y") == m_transforms.end();
|
||||
|
||||
bool start_as_gcodeviewer =
|
||||
#ifdef _WIN32
|
||||
false;
|
||||
#else
|
||||
// On Unix systems, the prusa-slicer binary may be symlinked to give the application a different meaning.
|
||||
boost::algorithm::iends_with(boost::filesystem::path(argv[0]).filename().string(), "gcodeviewer");
|
||||
#endif // _WIN32
|
||||
|
||||
const std::vector<std::string> &load_configs = m_config.option<ConfigOptionStrings>("load", true)->values;
|
||||
|
||||
// load config files supplied via --load
|
||||
|
|
@ -132,37 +140,57 @@ int CLI::run(int argc, char **argv)
|
|||
m_print_config.apply(config);
|
||||
}
|
||||
|
||||
// Read input file(s) if any.
|
||||
for (const std::string &file : m_input_files) {
|
||||
if (! boost::filesystem::exists(file)) {
|
||||
boost::nowide::cerr << "No such file: " << file << std::endl;
|
||||
exit(1);
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
// are we starting as gcodeviewer ?
|
||||
for (auto it = m_actions.begin(); it != m_actions.end(); ++it) {
|
||||
if (*it == "gcodeviewer") {
|
||||
start_gui = true;
|
||||
start_as_gcodeviewer = true;
|
||||
m_actions.erase(it);
|
||||
break;
|
||||
}
|
||||
Model model;
|
||||
try {
|
||||
// When loading an AMF or 3MF, config is imported as well, including the printer technology.
|
||||
DynamicPrintConfig config;
|
||||
model = Model::read_from_file(file, &config, true);
|
||||
PrinterTechnology other_printer_technology = Slic3r::printer_technology(config);
|
||||
if (printer_technology == ptUnknown) {
|
||||
printer_technology = other_printer_technology;
|
||||
} else if (printer_technology != other_printer_technology && other_printer_technology != ptUnknown) {
|
||||
boost::nowide::cerr << "Mixing configurations for FFF and SLA technologies" << std::endl;
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
// Read input file(s) if any.
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
if (!start_as_gcodeviewer) {
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
for (const std::string& file : m_input_files) {
|
||||
if (!boost::filesystem::exists(file)) {
|
||||
boost::nowide::cerr << "No such file: " << file << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
Model model;
|
||||
try {
|
||||
// When loading an AMF or 3MF, config is imported as well, including the printer technology.
|
||||
DynamicPrintConfig config;
|
||||
model = Model::read_from_file(file, &config, true);
|
||||
PrinterTechnology other_printer_technology = Slic3r::printer_technology(config);
|
||||
if (printer_technology == ptUnknown) {
|
||||
printer_technology = other_printer_technology;
|
||||
}
|
||||
else if (printer_technology != other_printer_technology && other_printer_technology != ptUnknown) {
|
||||
boost::nowide::cerr << "Mixing configurations for FFF and SLA technologies" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
// config is applied to m_print_config before the current m_config values.
|
||||
config += std::move(m_print_config);
|
||||
m_print_config = std::move(config);
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
boost::nowide::cerr << file << ": " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
// config is applied to m_print_config before the current m_config values.
|
||||
config += std::move(m_print_config);
|
||||
m_print_config = std::move(config);
|
||||
} catch (std::exception &e) {
|
||||
boost::nowide::cerr << file << ": " << e.what() << std::endl;
|
||||
return 1;
|
||||
if (model.objects.empty()) {
|
||||
boost::nowide::cerr << "Error: file is empty: " << file << std::endl;
|
||||
continue;
|
||||
}
|
||||
m_models.push_back(model);
|
||||
}
|
||||
if (model.objects.empty()) {
|
||||
boost::nowide::cerr << "Error: file is empty: " << file << std::endl;
|
||||
continue;
|
||||
}
|
||||
m_models.push_back(model);
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
// Apply command line options to a more specific DynamicPrintConfig which provides normalize()
|
||||
// (command line options override --load files)
|
||||
|
|
@ -469,7 +497,11 @@ int CLI::run(int argc, char **argv)
|
|||
print->process();
|
||||
if (printer_technology == ptFFF) {
|
||||
// The outfile is processed by a PlaceholderParser.
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
outfile = fff_print.export_gcode(outfile, nullptr, nullptr);
|
||||
#else
|
||||
outfile = fff_print.export_gcode(outfile, nullptr);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
outfile_final = fff_print.print_statistics().finalize_output_path(outfile);
|
||||
} else {
|
||||
outfile = sla_print.output_filepath(outfile);
|
||||
|
|
@ -517,6 +549,11 @@ int CLI::run(int argc, char **argv)
|
|||
<< " (" << print.total_extruded_volume()/1000 << "cm3)" << std::endl;
|
||||
*/
|
||||
}
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
} else if (opt_key == "gcodeviewer") {
|
||||
start_gui = true;
|
||||
start_as_gcodeviewer = true;
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
} else {
|
||||
boost::nowide::cerr << "error: option not supported yet: " << opt_key << std::endl;
|
||||
return 1;
|
||||
|
|
@ -526,7 +563,11 @@ int CLI::run(int argc, char **argv)
|
|||
if (start_gui) {
|
||||
#ifdef SLIC3R_GUI
|
||||
// #ifdef USE_WX
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
GUI::GUI_App* gui = new GUI::GUI_App(start_as_gcodeviewer ? GUI::GUI_App::EAppMode::GCodeViewer : GUI::GUI_App::EAppMode::Editor);
|
||||
#else
|
||||
GUI::GUI_App *gui = new GUI::GUI_App();
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
bool gui_single_instance_setting = gui->app_config->get("single_instance") == "1";
|
||||
if (Slic3r::instance_check(argc, argv, gui_single_instance_setting)) {
|
||||
|
|
@ -536,28 +577,42 @@ int CLI::run(int argc, char **argv)
|
|||
|
||||
// gui->autosave = m_config.opt_string("autosave");
|
||||
GUI::GUI_App::SetInstance(gui);
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
gui->CallAfter([gui, this, &load_configs, start_as_gcodeviewer] {
|
||||
#else
|
||||
gui->CallAfter([gui, this, &load_configs] {
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
if (!gui->initialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
if (start_as_gcodeviewer) {
|
||||
if (!m_input_files.empty())
|
||||
gui->plater()->load_gcode(wxString::FromUTF8(m_input_files[0].c_str()));
|
||||
} else {
|
||||
#endif // ENABLE_GCODE_VIEWER_AS
|
||||
#if 0
|
||||
// Load the cummulative config over the currently active profiles.
|
||||
//FIXME if multiple configs are loaded, only the last one will have an effect.
|
||||
// We need to decide what to do about loading of separate presets (just print preset, just filament preset etc).
|
||||
// As of now only the full configs are supported here.
|
||||
if (!m_print_config.empty())
|
||||
gui->mainframe->load_config(m_print_config);
|
||||
// Load the cummulative config over the currently active profiles.
|
||||
//FIXME if multiple configs are loaded, only the last one will have an effect.
|
||||
// We need to decide what to do about loading of separate presets (just print preset, just filament preset etc).
|
||||
// As of now only the full configs are supported here.
|
||||
if (!m_print_config.empty())
|
||||
gui->mainframe->load_config(m_print_config);
|
||||
#endif
|
||||
if (! load_configs.empty())
|
||||
// Load the last config to give it a name at the UI. The name of the preset may be later
|
||||
// changed by loading an AMF or 3MF.
|
||||
//FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config.
|
||||
gui->mainframe->load_config_file(load_configs.back());
|
||||
// If loading a 3MF file, the config is loaded from the last one.
|
||||
if (! m_input_files.empty())
|
||||
gui->plater()->load_files(m_input_files, true, true);
|
||||
if (! m_extra_config.empty())
|
||||
gui->mainframe->load_config(m_extra_config);
|
||||
if (!load_configs.empty())
|
||||
// Load the last config to give it a name at the UI. The name of the preset may be later
|
||||
// changed by loading an AMF or 3MF.
|
||||
//FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config.
|
||||
gui->mainframe->load_config_file(load_configs.back());
|
||||
// If loading a 3MF file, the config is loaded from the last one.
|
||||
if (!m_input_files.empty())
|
||||
gui->plater()->load_files(m_input_files, true, true);
|
||||
if (!m_extra_config.empty())
|
||||
gui->mainframe->load_config(m_extra_config);
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
});
|
||||
int result = wxEntry(argc, argv);
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -221,6 +221,11 @@ int wmain(int argc, wchar_t **argv)
|
|||
std::vector<wchar_t*> argv_extended;
|
||||
argv_extended.emplace_back(argv[0]);
|
||||
|
||||
#ifdef SLIC3R_WRAPPER_GCODEVIEWER
|
||||
wchar_t gcodeviewer_param[] = L"--gcodeviewer";
|
||||
argv_extended.emplace_back(gcodeviewer_param);
|
||||
#endif /* SLIC3R_WRAPPER_GCODEVIEWER */
|
||||
|
||||
#ifdef SLIC3R_GUI
|
||||
// Here one may push some additional parameters based on the wrapper type.
|
||||
bool force_mesa = false;
|
||||
|
|
|
|||
|
|
@ -48,9 +48,19 @@
|
|||
#include <ostream>
|
||||
#include <functional>
|
||||
#include <assert.h>
|
||||
#include <Shiny/Shiny.h>
|
||||
#include <libslic3r/Int128.hpp>
|
||||
|
||||
// Profiling support using the Shiny intrusive profiler
|
||||
//#define CLIPPERLIB_PROFILE
|
||||
#if defined(SLIC3R_PROFILE) && defined(CLIPPERLIB_PROFILE)
|
||||
#include <Shiny/Shiny.h>
|
||||
#define CLIPPERLIB_PROFILE_FUNC() PROFILE_FUNC()
|
||||
#define CLIPPERLIB_PROFILE_BLOCK(name) PROFILE_BLOCK(name)
|
||||
#else
|
||||
#define CLIPPERLIB_PROFILE_FUNC()
|
||||
#define CLIPPERLIB_PROFILE_BLOCK(name)
|
||||
#endif
|
||||
|
||||
#ifdef use_xyz
|
||||
namespace ClipperLib_Z {
|
||||
#else /* use_xyz */
|
||||
|
|
@ -263,7 +273,7 @@ int PointInPolygon (const IntPoint &pt, OutPt *op)
|
|||
// This is potentially very expensive! O(n^2)!
|
||||
bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERLIB_PROFILE_FUNC();
|
||||
OutPt* op = OutPt1;
|
||||
do
|
||||
{
|
||||
|
|
@ -714,7 +724,7 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward)
|
|||
|
||||
bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERLIB_PROFILE_FUNC();
|
||||
// Remove duplicate end point from a closed input path.
|
||||
// Remove duplicate points from the end of the input path.
|
||||
int highI = (int)pg.size() -1;
|
||||
|
|
@ -738,7 +748,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
|
|||
|
||||
bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERLIB_PROFILE_FUNC();
|
||||
std::vector<int> num_edges(ppg.size(), 0);
|
||||
int num_edges_total = 0;
|
||||
for (size_t i = 0; i < ppg.size(); ++ i) {
|
||||
|
|
@ -780,7 +790,7 @@ bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed)
|
|||
|
||||
bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, bool Closed, TEdge* edges)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERLIB_PROFILE_FUNC();
|
||||
#ifdef use_lines
|
||||
if (!Closed && PolyTyp == ptClip)
|
||||
throw clipperException("AddPath: Open paths must be subject.");
|
||||
|
|
@ -954,7 +964,7 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b
|
|||
|
||||
void ClipperBase::Clear()
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERLIB_PROFILE_FUNC();
|
||||
m_MinimaList.clear();
|
||||
m_edges.clear();
|
||||
m_UseFullRange = false;
|
||||
|
|
@ -966,7 +976,7 @@ void ClipperBase::Clear()
|
|||
// Sort the LML entries, initialize the left / right bound edges of each Local Minima.
|
||||
void ClipperBase::Reset()
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERLIB_PROFILE_FUNC();
|
||||
if (m_MinimaList.empty()) return; //ie nothing to process
|
||||
std::sort(m_MinimaList.begin(), m_MinimaList.end(), [](const LocalMinimum& lm1, const LocalMinimum& lm2){ return lm1.Y < lm2.Y; });
|
||||
|
||||
|
|
@ -995,7 +1005,7 @@ void ClipperBase::Reset()
|
|||
// Returns (0,0,0,0) for an empty rectangle.
|
||||
IntRect ClipperBase::GetBounds()
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERLIB_PROFILE_FUNC();
|
||||
IntRect result;
|
||||
auto lm = m_MinimaList.begin();
|
||||
if (lm == m_MinimaList.end())
|
||||
|
|
@ -1056,7 +1066,7 @@ Clipper::Clipper(int initOptions) :
|
|||
|
||||
void Clipper::Reset()
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERLIB_PROFILE_FUNC();
|
||||
ClipperBase::Reset();
|
||||
m_Scanbeam = std::priority_queue<cInt>();
|
||||
m_Maxima.clear();
|
||||
|
|
@ -1071,7 +1081,7 @@ void Clipper::Reset()
|
|||
bool Clipper::Execute(ClipType clipType, Paths &solution,
|
||||
PolyFillType subjFillType, PolyFillType clipFillType)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERLIB_PROFILE_FUNC();
|
||||
if (m_HasOpenPaths)
|
||||
throw clipperException("Error: PolyTree struct is needed for open path clipping.");
|
||||
solution.resize(0);
|
||||
|
|
@ -1089,7 +1099,7 @@ bool Clipper::Execute(ClipType clipType, Paths &solution,
|
|||
bool Clipper::Execute(ClipType clipType, PolyTree& polytree,
|
||||
PolyFillType subjFillType, PolyFillType clipFillType)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERLIB_PROFILE_FUNC();
|
||||
m_SubjFillType = subjFillType;
|
||||
m_ClipFillType = clipFillType;
|
||||
m_ClipType = clipType;
|
||||
|
|
@ -1103,10 +1113,10 @@ bool Clipper::Execute(ClipType clipType, PolyTree& polytree,
|
|||
|
||||
bool Clipper::ExecuteInternal()
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERLIB_PROFILE_FUNC();
|
||||
bool succeeded = true;
|
||||
try {
|
||||
PROFILE_BLOCK(Clipper_ExecuteInternal_Process);
|
||||
CLIPPERLIB_PROFILE_BLOCK(Clipper_ExecuteInternal_Process);
|
||||
Reset();
|
||||
if (m_MinimaList.empty()) return true;
|
||||
cInt botY = m_Scanbeam.top();
|
||||
|
|
@ -1131,13 +1141,13 @@ bool Clipper::ExecuteInternal()
|
|||
|
||||
if (succeeded)
|
||||
{
|
||||
PROFILE_BLOCK(Clipper_ExecuteInternal_Fix);
|
||||
CLIPPERLIB_PROFILE_BLOCK(Clipper_ExecuteInternal_Fix);
|
||||
|
||||
//fix orientations ...
|
||||
//FIXME Vojtech: Does it not invalidate the loop hierarchy maintained as OutRec::FirstLeft pointers?
|
||||
//FIXME Vojtech: The area is calculated with floats, it may not be numerically stable!
|
||||
{
|
||||
PROFILE_BLOCK(Clipper_ExecuteInternal_Fix_orientations);
|
||||
CLIPPERLIB_PROFILE_BLOCK(Clipper_ExecuteInternal_Fix_orientations);
|
||||
for (OutRec *outRec : m_PolyOuts)
|
||||
if (outRec->Pts && !outRec->IsOpen && (outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0))
|
||||
ReversePolyPtLinks(outRec->Pts);
|
||||
|
|
@ -1147,7 +1157,7 @@ bool Clipper::ExecuteInternal()
|
|||
|
||||
//unfortunately FixupOutPolygon() must be done after JoinCommonEdges()
|
||||
{
|
||||
PROFILE_BLOCK(Clipper_ExecuteInternal_Fix_fixup);
|
||||
CLIPPERLIB_PROFILE_BLOCK(Clipper_ExecuteInternal_Fix_fixup);
|
||||
for (OutRec *outRec : m_PolyOuts)
|
||||
if (outRec->Pts) {
|
||||
if (outRec->IsOpen)
|
||||
|
|
@ -1401,7 +1411,7 @@ bool Clipper::IsContributing(const TEdge& edge) const
|
|||
// Called from Clipper::InsertLocalMinimaIntoAEL() and Clipper::IntersectEdges().
|
||||
OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERLIB_PROFILE_FUNC();
|
||||
OutPt* result;
|
||||
TEdge *e, *prevE;
|
||||
if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx ))
|
||||
|
|
@ -1493,7 +1503,7 @@ void Clipper::CopyAELToSEL()
|
|||
// Called from Clipper::ExecuteInternal()
|
||||
void Clipper::InsertLocalMinimaIntoAEL(const cInt botY)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERLIB_PROFILE_FUNC();
|
||||
while (!m_MinimaList.empty() && m_MinimaList.back().Y == botY)
|
||||
{
|
||||
TEdge* lb = m_MinimaList.back().LeftBound;
|
||||
|
|
@ -2043,7 +2053,7 @@ OutPt* Clipper::GetLastOutPt(TEdge *e)
|
|||
|
||||
void Clipper::ProcessHorizontals()
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERLIB_PROFILE_FUNC();
|
||||
TEdge* horzEdge = m_SortedEdges;
|
||||
while(horzEdge)
|
||||
{
|
||||
|
|
@ -2414,7 +2424,7 @@ void Clipper::UpdateEdgeIntoAEL(TEdge *&e)
|
|||
|
||||
bool Clipper::ProcessIntersections(const cInt topY)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERLIB_PROFILE_FUNC();
|
||||
if( !m_ActiveEdges ) return true;
|
||||
try {
|
||||
BuildIntersectList(topY);
|
||||
|
|
@ -2569,7 +2579,7 @@ void Clipper::DoMaxima(TEdge *e)
|
|||
|
||||
void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERLIB_PROFILE_FUNC();
|
||||
TEdge* e = m_ActiveEdges;
|
||||
while( e )
|
||||
{
|
||||
|
|
@ -3177,7 +3187,7 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2)
|
|||
// This is potentially very expensive! O(n^3)!
|
||||
void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERLIB_PROFILE_FUNC();
|
||||
//tests if NewOutRec contains the polygon before reassigning FirstLeft
|
||||
for (OutRec *outRec : m_PolyOuts)
|
||||
{
|
||||
|
|
@ -3201,7 +3211,7 @@ void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const
|
|||
|
||||
void Clipper::JoinCommonEdges()
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERLIB_PROFILE_FUNC();
|
||||
for (Join &join : m_Joins)
|
||||
{
|
||||
OutRec *outRec1 = GetOutRec(join.OutPt1->Idx);
|
||||
|
|
@ -3771,7 +3781,7 @@ void ClipperOffset::DoRound(int j, int k)
|
|||
// http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Properties/StrictlySimple.htm
|
||||
void Clipper::DoSimplePolygons()
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERLIB_PROFILE_FUNC();
|
||||
size_t i = 0;
|
||||
while (i < m_PolyOuts.size())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -108,12 +108,19 @@ namespace ImGui
|
|||
const char ColorMarkerEnd = 0x3; // ETX
|
||||
|
||||
// Special ASCII characters are used here as an ikons markers
|
||||
const char PrintIconMarker = 0x4;
|
||||
const char PrinterIconMarker = 0x5;
|
||||
const char PrinterSlaIconMarker = 0x6;
|
||||
const char FilamentIconMarker = 0x7;
|
||||
const char MaterialIconMarker = 0x8;
|
||||
|
||||
const char PrintIconMarker = 0x4;
|
||||
const char PrinterIconMarker = 0x5;
|
||||
const char PrinterSlaIconMarker = 0x6;
|
||||
const char FilamentIconMarker = 0x7;
|
||||
const char MaterialIconMarker = 0x8;
|
||||
const char CloseIconMarker = 0xB;
|
||||
const char CloseIconHoverMarker = 0xC;
|
||||
// const char TimerDotMarker = 0xE;
|
||||
// const char TimerDotEmptyMarker = 0xF;
|
||||
const char MinimalizeMarker = 0xE;
|
||||
const char MinimalizeHoverMarker = 0xF;
|
||||
const char WarningMarker = 0x10;
|
||||
const char ErrorMarker = 0x11;
|
||||
// void MyFunction(const char* name, const MyMatrix44& v);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,661 +1,165 @@
|
|||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
|
|
@ -692,6 +692,40 @@ inline typename VectorType::Scalar squared_distance_to_indexed_triangle_set(
|
|||
detail::squared_distance_to_indexed_triangle_set_recursive(distancer, size_t(0), Scalar(0), std::numeric_limits<Scalar>::infinity(), hit_idx_out, hit_point_out);
|
||||
}
|
||||
|
||||
// Decides if exists some triangle in defined radius on a 3D indexed triangle set using a pre-built AABBTreeIndirect::Tree.
|
||||
// Closest point to triangle test will be performed with the accuracy of VectorType::Scalar
|
||||
// even if the triangle mesh and the AABB Tree are built with floats.
|
||||
// Returns true if exists some triangle in defined radius, false otherwise.
|
||||
template<typename VertexType, typename IndexedFaceType, typename TreeType, typename VectorType>
|
||||
inline bool is_any_triangle_in_radius(
|
||||
// Indexed triangle set - 3D vertices.
|
||||
const std::vector<VertexType> &vertices,
|
||||
// Indexed triangle set - triangular faces, references to vertices.
|
||||
const std::vector<IndexedFaceType> &faces,
|
||||
// AABBTreeIndirect::Tree over vertices & faces, bounding boxes built with the accuracy of vertices.
|
||||
const TreeType &tree,
|
||||
// Point to which the closest point on the indexed triangle set is searched for.
|
||||
const VectorType &point,
|
||||
// Maximum distance in which triangle is search for
|
||||
typename VectorType::Scalar &max_distance)
|
||||
{
|
||||
using Scalar = typename VectorType::Scalar;
|
||||
auto distancer = detail::IndexedTriangleSetDistancer<VertexType, IndexedFaceType, TreeType, VectorType>
|
||||
{ vertices, faces, tree, point };
|
||||
|
||||
size_t hit_idx;
|
||||
VectorType hit_point = VectorType::Ones() * (std::nan(""));
|
||||
|
||||
if(tree.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
detail::squared_distance_to_indexed_triangle_set_recursive(distancer, size_t(0), Scalar(0), max_distance, hit_idx, hit_point);
|
||||
|
||||
return hit_point.allFinite();
|
||||
}
|
||||
|
||||
} // namespace AABBTreeIndirect
|
||||
} // namespace Slic3r
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "AppConfig.hpp"
|
||||
#include "Exception.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
|
@ -15,8 +16,8 @@
|
|||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/format/format_fwd.hpp>
|
||||
|
||||
#include <wx/string.h>
|
||||
#include "I18N.hpp"
|
||||
//#include <wx/string.h>
|
||||
//#include "I18N.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
|
@ -101,6 +102,9 @@ void AppConfig::set_defaults()
|
|||
if (get("use_inches").empty())
|
||||
set("use_inches", "0");
|
||||
|
||||
if (get("show_splash_screen").empty())
|
||||
set("show_splash_screen", "1");
|
||||
|
||||
// Remove legacy window positions/sizes
|
||||
erase("", "main_frame_maximized");
|
||||
erase("", "main_frame_pos");
|
||||
|
|
@ -110,7 +114,7 @@ void AppConfig::set_defaults()
|
|||
erase("", "object_settings_size");
|
||||
}
|
||||
|
||||
void AppConfig::load()
|
||||
std::string AppConfig::load()
|
||||
{
|
||||
// 1) Read the complete config file into a boost::property_tree.
|
||||
namespace pt = boost::property_tree;
|
||||
|
|
@ -120,10 +124,15 @@ void AppConfig::load()
|
|||
pt::read_ini(ifs, tree);
|
||||
} catch (pt::ptree_error& ex) {
|
||||
// Error while parsing config file. We'll customize the error message and rethrow to be displayed.
|
||||
throw std::runtime_error(
|
||||
// ! But to avoid the use of _utf8 (related to use of wxWidgets)
|
||||
// we will rethrow this exception from the place of load() call, if returned value wouldn't be empty
|
||||
/*
|
||||
throw Slic3r::RuntimeError(
|
||||
_utf8(L("Error parsing PrusaSlicer 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\n" + AppConfig::config_path() + "\n\n" + ex.what());
|
||||
*/
|
||||
return ex.what();
|
||||
}
|
||||
|
||||
// 2) Parse the property_tree, extract the sections and key / value pairs.
|
||||
|
|
@ -169,10 +178,16 @@ void AppConfig::load()
|
|||
// Override missing or keys with their defaults.
|
||||
this->set_defaults();
|
||||
m_dirty = false;
|
||||
return "";
|
||||
}
|
||||
|
||||
void AppConfig::save()
|
||||
{
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
if (!m_save_enabled)
|
||||
return;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
// The config is first written to a file with a PID suffix and then moved
|
||||
// to avoid race conditions with multiple instances of Slic3r
|
||||
const auto path = config_path();
|
||||
|
|
@ -18,6 +18,9 @@ public:
|
|||
AppConfig() :
|
||||
m_dirty(false),
|
||||
m_orig_version(Semver::invalid()),
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
m_save_enabled(true),
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
m_legacy_datadir(false)
|
||||
{
|
||||
this->reset();
|
||||
|
|
@ -29,7 +32,8 @@ public:
|
|||
void set_defaults();
|
||||
|
||||
// Load the slic3r.ini from a user profile directory (or a datadir, if configured).
|
||||
void load();
|
||||
// return error string or empty strinf
|
||||
std::string load();
|
||||
// Store the slic3r.ini into a user profile directory (or a datadir, if configured).
|
||||
void save();
|
||||
|
||||
|
|
@ -156,6 +160,10 @@ public:
|
|||
bool get_mouse_device_swap_yz(const std::string& name, bool& swap) const
|
||||
{ return get_3dmouse_device_numeric_value(name, "swap_yz", swap); }
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
void enable_save(bool enable) { m_save_enabled = enable; }
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
static const std::string SECTION_FILAMENTS;
|
||||
static const std::string SECTION_MATERIALS;
|
||||
|
||||
|
|
@ -182,6 +190,10 @@ private:
|
|||
bool m_dirty;
|
||||
// Original version found in the ini file before it was overwritten
|
||||
Semver m_orig_version;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
// Whether or not calls to save() should take effect
|
||||
bool m_save_enabled;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
// Whether the existing version is before system profiles & configuration updating
|
||||
bool m_legacy_datadir;
|
||||
};
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
#define slic3r_BoundingBox_hpp_
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "Exception.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "Polygon.hpp"
|
||||
|
||||
|
|
@ -22,7 +23,7 @@ public:
|
|||
{
|
||||
if (points.empty()) {
|
||||
this->defined = false;
|
||||
// throw std::invalid_argument("Empty point set supplied to BoundingBoxBase constructor");
|
||||
// throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBoxBase constructor");
|
||||
} else {
|
||||
typename std::vector<PointClass>::const_iterator it = points.begin();
|
||||
this->min = *it;
|
||||
|
|
@ -68,7 +69,7 @@ public:
|
|||
BoundingBox3Base(const std::vector<PointClass>& points)
|
||||
{
|
||||
if (points.empty())
|
||||
throw std::invalid_argument("Empty point set supplied to BoundingBox3Base constructor");
|
||||
throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBox3Base constructor");
|
||||
typename std::vector<PointClass>::const_iterator it = points.begin();
|
||||
this->min = *it;
|
||||
this->max = *it;
|
||||
|
|
@ -192,6 +193,20 @@ inline BoundingBox3 scaled(const BoundingBoxf3 &bb) { return {scaled(bb.min), sc
|
|||
inline BoundingBoxf unscaled(const BoundingBox &bb) { return {unscaled(bb.min), unscaled(bb.max)}; }
|
||||
inline BoundingBoxf3 unscaled(const BoundingBox3 &bb) { return {unscaled(bb.min), unscaled(bb.max)}; }
|
||||
|
||||
template<class Tout, class Tin>
|
||||
auto cast(const BoundingBoxBase<Tin> &b)
|
||||
{
|
||||
return BoundingBoxBase<Vec<3, Tout>>{b.min.template cast<Tout>(),
|
||||
b.max.template cast<Tout>()};
|
||||
}
|
||||
|
||||
template<class Tout, class Tin>
|
||||
auto cast(const BoundingBox3Base<Tin> &b)
|
||||
{
|
||||
return BoundingBox3Base<Vec<3, Tout>>{b.min.template cast<Tout>(),
|
||||
b.max.template cast<Tout>()};
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
// Serialization through the Cereal library
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ add_library(libslic3r STATIC
|
|||
Fill/Fill.hpp
|
||||
Fill/Fill3DHoneycomb.cpp
|
||||
Fill/Fill3DHoneycomb.hpp
|
||||
Fill/FillAdaptive.cpp
|
||||
Fill/FillAdaptive.hpp
|
||||
Fill/FillBase.cpp
|
||||
Fill/FillBase.hpp
|
||||
Fill/FillConcentric.cpp
|
||||
|
|
@ -99,6 +101,8 @@ add_library(libslic3r STATIC
|
|||
GCode/ToolOrdering.hpp
|
||||
GCode/WipeTower.cpp
|
||||
GCode/WipeTower.hpp
|
||||
GCode/GCodeProcessor.cpp
|
||||
GCode/GCodeProcessor.hpp
|
||||
GCode.cpp
|
||||
GCode.hpp
|
||||
GCodeReader.cpp
|
||||
|
|
@ -147,6 +151,12 @@ add_library(libslic3r STATIC
|
|||
PolygonTrimmer.hpp
|
||||
Polyline.cpp
|
||||
Polyline.hpp
|
||||
Preset.cpp
|
||||
Preset.hpp
|
||||
PresetBundle.cpp
|
||||
PresetBundle.hpp
|
||||
AppConfig.cpp
|
||||
AppConfig.hpp
|
||||
Print.cpp
|
||||
Print.hpp
|
||||
PrintBase.cpp
|
||||
|
|
@ -155,6 +165,8 @@ add_library(libslic3r STATIC
|
|||
PrintConfig.hpp
|
||||
PrintObject.cpp
|
||||
PrintRegion.cpp
|
||||
PNGRead.hpp
|
||||
PNGRead.cpp
|
||||
Semver.cpp
|
||||
ShortestPath.cpp
|
||||
ShortestPath.hpp
|
||||
|
|
@ -187,6 +199,8 @@ add_library(libslic3r STATIC
|
|||
Utils.hpp
|
||||
Time.cpp
|
||||
Time.hpp
|
||||
TriangleSelector.cpp
|
||||
TriangleSelector.hpp
|
||||
MTUtils.hpp
|
||||
VoronoiOffset.cpp
|
||||
VoronoiOffset.hpp
|
||||
|
|
@ -201,12 +215,15 @@ add_library(libslic3r STATIC
|
|||
SimplifyMeshImpl.hpp
|
||||
SimplifyMesh.cpp
|
||||
MarchingSquares.hpp
|
||||
Optimize/Optimizer.hpp
|
||||
Optimize/NLoptOptimizer.hpp
|
||||
Optimize/BruteforceOptimizer.hpp
|
||||
${OpenVDBUtils_SOURCES}
|
||||
SLA/Common.hpp
|
||||
SLA/Common.cpp
|
||||
SLA/Pad.hpp
|
||||
SLA/Pad.cpp
|
||||
SLA/SupportTreeBuilder.hpp
|
||||
SLA/SupportTreeMesher.hpp
|
||||
SLA/SupportTreeMesher.cpp
|
||||
SLA/SupportTreeBuildsteps.hpp
|
||||
SLA/SupportTreeBuildsteps.cpp
|
||||
SLA/SupportTreeBuilder.cpp
|
||||
|
|
@ -218,6 +235,7 @@ add_library(libslic3r STATIC
|
|||
SLA/Rotfinder.cpp
|
||||
SLA/BoostAdapter.hpp
|
||||
SLA/SpatIndex.hpp
|
||||
SLA/SpatIndex.cpp
|
||||
SLA/RasterBase.hpp
|
||||
SLA/RasterBase.cpp
|
||||
SLA/AGGRaster.hpp
|
||||
|
|
@ -233,8 +251,10 @@ add_library(libslic3r STATIC
|
|||
SLA/SupportPointGenerator.cpp
|
||||
SLA/Contour3D.hpp
|
||||
SLA/Contour3D.cpp
|
||||
SLA/EigenMesh3D.hpp
|
||||
SLA/IndexedMesh.hpp
|
||||
SLA/IndexedMesh.cpp
|
||||
SLA/Clustering.hpp
|
||||
SLA/Clustering.cpp
|
||||
SLA/ReprojectPointsOnMesh.hpp
|
||||
)
|
||||
|
||||
|
|
@ -296,6 +316,8 @@ target_link_libraries(libslic3r
|
|||
TBB::tbb
|
||||
libslic3r_cgal
|
||||
${CMAKE_DL_LIBS}
|
||||
PNG::PNG
|
||||
ZLIB::ZLIB
|
||||
)
|
||||
|
||||
if (TARGET OpenVDB::openvdb)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,16 @@
|
|||
#include "SVG.hpp"
|
||||
#endif /* CLIPPER_UTILS_DEBUG */
|
||||
|
||||
#include <Shiny/Shiny.h>
|
||||
// Profiling support using the Shiny intrusive profiler
|
||||
//#define CLIPPER_UTILS_PROFILE
|
||||
#if defined(SLIC3R_PROFILE) && defined(CLIPPER_UTILS_PROFILE)
|
||||
#include <Shiny/Shiny.h>
|
||||
#define CLIPPERUTILS_PROFILE_FUNC() PROFILE_FUNC()
|
||||
#define CLIPPERUTILS_PROFILE_BLOCK(name) PROFILE_BLOCK(name)
|
||||
#else
|
||||
#define CLIPPERUTILS_PROFILE_FUNC()
|
||||
#define CLIPPERUTILS_PROFILE_BLOCK(name)
|
||||
#endif
|
||||
|
||||
#define CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR (0.005f)
|
||||
|
||||
|
|
@ -50,7 +59,7 @@ err:
|
|||
|
||||
void scaleClipperPolygon(ClipperLib::Path &polygon)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERUTILS_PROFILE_FUNC();
|
||||
for (ClipperLib::Path::iterator pit = polygon.begin(); pit != polygon.end(); ++pit) {
|
||||
pit->X <<= CLIPPER_OFFSET_POWER_OF_2;
|
||||
pit->Y <<= CLIPPER_OFFSET_POWER_OF_2;
|
||||
|
|
@ -59,7 +68,7 @@ void scaleClipperPolygon(ClipperLib::Path &polygon)
|
|||
|
||||
void scaleClipperPolygons(ClipperLib::Paths &polygons)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERUTILS_PROFILE_FUNC();
|
||||
for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it)
|
||||
for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) {
|
||||
pit->X <<= CLIPPER_OFFSET_POWER_OF_2;
|
||||
|
|
@ -69,7 +78,7 @@ void scaleClipperPolygons(ClipperLib::Paths &polygons)
|
|||
|
||||
void unscaleClipperPolygon(ClipperLib::Path &polygon)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERUTILS_PROFILE_FUNC();
|
||||
for (ClipperLib::Path::iterator pit = polygon.begin(); pit != polygon.end(); ++pit) {
|
||||
pit->X += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA;
|
||||
pit->Y += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA;
|
||||
|
|
@ -80,7 +89,7 @@ void unscaleClipperPolygon(ClipperLib::Path &polygon)
|
|||
|
||||
void unscaleClipperPolygons(ClipperLib::Paths &polygons)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERUTILS_PROFILE_FUNC();
|
||||
for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it)
|
||||
for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) {
|
||||
pit->X += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA;
|
||||
|
|
@ -790,7 +799,7 @@ ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear
|
|||
|
||||
void safety_offset(ClipperLib::Paths* paths)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
CLIPPERUTILS_PROFILE_FUNC();
|
||||
|
||||
// scale input
|
||||
scaleClipperPolygons(*paths);
|
||||
|
|
@ -812,11 +821,11 @@ void safety_offset(ClipperLib::Paths* paths)
|
|||
if (! ccw)
|
||||
std::reverse(path.begin(), path.end());
|
||||
{
|
||||
PROFILE_BLOCK(safety_offset_AddPaths);
|
||||
CLIPPERUTILS_PROFILE_BLOCK(safety_offset_AddPaths);
|
||||
co.AddPath((*paths)[i], ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
|
||||
}
|
||||
{
|
||||
PROFILE_BLOCK(safety_offset_Execute);
|
||||
CLIPPERUTILS_PROFILE_BLOCK(safety_offset_Execute);
|
||||
// offset outside by 10um
|
||||
ClipperLib::Paths out_this;
|
||||
co.Execute(out_this, ccw ? 10.f * float(CLIPPER_OFFSET_SCALE) : -10.f * float(CLIPPER_OFFSET_SCALE));
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <exception> // std::runtime_error
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/erase.hpp>
|
||||
|
|
@ -218,7 +217,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const
|
|||
case coInts: return new ConfigOptionIntsNullable();
|
||||
case coPercents: return new ConfigOptionPercentsNullable();
|
||||
case coBools: return new ConfigOptionBoolsNullable();
|
||||
default: throw std::runtime_error(std::string("Unknown option type for nullable option ") + this->label);
|
||||
default: throw Slic3r::RuntimeError(std::string("Unknown option type for nullable option ") + this->label);
|
||||
}
|
||||
} else {
|
||||
switch (this->type) {
|
||||
|
|
@ -238,7 +237,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const
|
|||
case coBool: return new ConfigOptionBool();
|
||||
case coBools: return new ConfigOptionBools();
|
||||
case coEnum: return new ConfigOptionEnumGeneric(this->enum_keys_map);
|
||||
default: throw std::runtime_error(std::string("Unknown option type for option ") + this->label);
|
||||
default: throw Slic3r::RuntimeError(std::string("Unknown option type for option ") + this->label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -535,7 +534,7 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key) const
|
|||
return opt_def->ratio_over.empty() ? 0. :
|
||||
static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->get_abs_value(this->get_abs_value(opt_def->ratio_over));
|
||||
}
|
||||
throw std::runtime_error("ConfigBase::get_abs_value(): Not a valid option type for get_abs_value()");
|
||||
throw Slic3r::RuntimeError("ConfigBase::get_abs_value(): Not a valid option type for get_abs_value()");
|
||||
}
|
||||
|
||||
// Return an absolute value of a possibly relative config variable.
|
||||
|
|
@ -546,7 +545,7 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key, double rati
|
|||
const ConfigOption *raw_opt = this->option(opt_key);
|
||||
assert(raw_opt != nullptr);
|
||||
if (raw_opt->type() != coFloatOrPercent)
|
||||
throw std::runtime_error("ConfigBase::get_abs_value(): opt_key is not of coFloatOrPercent");
|
||||
throw Slic3r::RuntimeError("ConfigBase::get_abs_value(): opt_key is not of coFloatOrPercent");
|
||||
// Compute absolute value.
|
||||
return static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->get_abs_value(ratio_over);
|
||||
}
|
||||
|
|
@ -609,7 +608,7 @@ void ConfigBase::load_from_gcode_file(const std::string &file)
|
|||
std::getline(ifs, firstline);
|
||||
if (strncmp(slic3r_gcode_header, firstline.c_str(), strlen(slic3r_gcode_header)) != 0 &&
|
||||
strncmp(prusaslicer_gcode_header, firstline.c_str(), strlen(prusaslicer_gcode_header)) != 0)
|
||||
throw std::runtime_error("Not a PrusaSlicer / Slic3r PE generated g-code.");
|
||||
throw Slic3r::RuntimeError("Not a PrusaSlicer / Slic3r PE generated g-code.");
|
||||
}
|
||||
ifs.seekg(0, ifs.end);
|
||||
auto file_length = ifs.tellg();
|
||||
|
|
@ -621,7 +620,7 @@ void ConfigBase::load_from_gcode_file(const std::string &file)
|
|||
|
||||
size_t key_value_pairs = load_from_gcode_string(data.data());
|
||||
if (key_value_pairs < 80)
|
||||
throw std::runtime_error(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs));
|
||||
throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs));
|
||||
}
|
||||
|
||||
// Load the config keys from the given string.
|
||||
|
|
@ -750,7 +749,7 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre
|
|||
throw NoDefinitionException(opt_key);
|
||||
const ConfigOptionDef *optdef = def->get(opt_key);
|
||||
if (optdef == nullptr)
|
||||
// throw std::runtime_error(std::string("Invalid option name: ") + opt_key);
|
||||
// throw Slic3r::RuntimeError(std::string("Invalid option name: ") + opt_key);
|
||||
// Let the parent decide what to do if the opt_key is not defined by this->def().
|
||||
return nullptr;
|
||||
ConfigOption *opt = optdef->create_default_option();
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <vector>
|
||||
#include "libslic3r.h"
|
||||
#include "clonable_ptr.hpp"
|
||||
#include "Exception.hpp"
|
||||
#include "Point.hpp"
|
||||
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
|
|
@ -34,31 +35,31 @@ extern bool unescape_string_cstyle(const std::string &str, std::string &
|
|||
extern bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &out);
|
||||
|
||||
/// Specialization of std::exception to indicate that an unknown config option has been encountered.
|
||||
class UnknownOptionException : public std::runtime_error {
|
||||
class UnknownOptionException : public Slic3r::RuntimeError {
|
||||
public:
|
||||
UnknownOptionException() :
|
||||
std::runtime_error("Unknown option exception") {}
|
||||
Slic3r::RuntimeError("Unknown option exception") {}
|
||||
UnknownOptionException(const std::string &opt_key) :
|
||||
std::runtime_error(std::string("Unknown option exception: ") + opt_key) {}
|
||||
Slic3r::RuntimeError(std::string("Unknown option exception: ") + opt_key) {}
|
||||
};
|
||||
|
||||
/// Indicate that the ConfigBase derived class does not provide config definition (the method def() returns null).
|
||||
class NoDefinitionException : public std::runtime_error
|
||||
class NoDefinitionException : public Slic3r::RuntimeError
|
||||
{
|
||||
public:
|
||||
NoDefinitionException() :
|
||||
std::runtime_error("No definition exception") {}
|
||||
Slic3r::RuntimeError("No definition exception") {}
|
||||
NoDefinitionException(const std::string &opt_key) :
|
||||
std::runtime_error(std::string("No definition exception: ") + opt_key) {}
|
||||
Slic3r::RuntimeError(std::string("No definition exception: ") + opt_key) {}
|
||||
};
|
||||
|
||||
/// Indicate that an unsupported accessor was called on a config option.
|
||||
class BadOptionTypeException : public std::runtime_error
|
||||
class BadOptionTypeException : public Slic3r::RuntimeError
|
||||
{
|
||||
public:
|
||||
BadOptionTypeException() : std::runtime_error("Bad option type exception") {}
|
||||
BadOptionTypeException(const std::string &message) : std::runtime_error(message) {}
|
||||
BadOptionTypeException(const char* message) : std::runtime_error(message) {}
|
||||
BadOptionTypeException() : Slic3r::RuntimeError("Bad option type exception") {}
|
||||
BadOptionTypeException(const std::string &message) : Slic3r::RuntimeError(message) {}
|
||||
BadOptionTypeException(const char* message) : Slic3r::RuntimeError(message) {}
|
||||
};
|
||||
|
||||
// Type of a configuration value.
|
||||
|
|
@ -167,7 +168,7 @@ public:
|
|||
void set(const ConfigOption *rhs) override
|
||||
{
|
||||
if (rhs->type() != this->type())
|
||||
throw std::runtime_error("ConfigOptionSingle: Assigning an incompatible type");
|
||||
throw Slic3r::RuntimeError("ConfigOptionSingle: Assigning an incompatible type");
|
||||
assert(dynamic_cast<const ConfigOptionSingle<T>*>(rhs));
|
||||
this->value = static_cast<const ConfigOptionSingle<T>*>(rhs)->value;
|
||||
}
|
||||
|
|
@ -175,7 +176,7 @@ public:
|
|||
bool operator==(const ConfigOption &rhs) const override
|
||||
{
|
||||
if (rhs.type() != this->type())
|
||||
throw std::runtime_error("ConfigOptionSingle: Comparing incompatible types");
|
||||
throw Slic3r::RuntimeError("ConfigOptionSingle: Comparing incompatible types");
|
||||
assert(dynamic_cast<const ConfigOptionSingle<T>*>(&rhs));
|
||||
return this->value == static_cast<const ConfigOptionSingle<T>*>(&rhs)->value;
|
||||
}
|
||||
|
|
@ -239,7 +240,7 @@ public:
|
|||
void set(const ConfigOption *rhs) override
|
||||
{
|
||||
if (rhs->type() != this->type())
|
||||
throw std::runtime_error("ConfigOptionVector: Assigning an incompatible type");
|
||||
throw Slic3r::RuntimeError("ConfigOptionVector: Assigning an incompatible type");
|
||||
assert(dynamic_cast<const ConfigOptionVector<T>*>(rhs));
|
||||
this->values = static_cast<const ConfigOptionVector<T>*>(rhs)->values;
|
||||
}
|
||||
|
|
@ -256,12 +257,12 @@ public:
|
|||
if (opt->type() == this->type()) {
|
||||
auto other = static_cast<const ConfigOptionVector<T>*>(opt);
|
||||
if (other->values.empty())
|
||||
throw std::runtime_error("ConfigOptionVector::set(): Assigning from an empty vector");
|
||||
throw Slic3r::RuntimeError("ConfigOptionVector::set(): Assigning from an empty vector");
|
||||
this->values.emplace_back(other->values.front());
|
||||
} else if (opt->type() == this->scalar_type())
|
||||
this->values.emplace_back(static_cast<const ConfigOptionSingle<T>*>(opt)->value);
|
||||
else
|
||||
throw std::runtime_error("ConfigOptionVector::set():: Assigning an incompatible type");
|
||||
throw Slic3r::RuntimeError("ConfigOptionVector::set():: Assigning an incompatible type");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -280,12 +281,12 @@ public:
|
|||
// Assign the first value of the rhs vector.
|
||||
auto other = static_cast<const ConfigOptionVector<T>*>(rhs);
|
||||
if (other->values.empty())
|
||||
throw std::runtime_error("ConfigOptionVector::set_at(): Assigning from an empty vector");
|
||||
throw Slic3r::RuntimeError("ConfigOptionVector::set_at(): Assigning from an empty vector");
|
||||
this->values[i] = other->get_at(j);
|
||||
} else if (rhs->type() == this->scalar_type())
|
||||
this->values[i] = static_cast<const ConfigOptionSingle<T>*>(rhs)->value;
|
||||
else
|
||||
throw std::runtime_error("ConfigOptionVector::set_at(): Assigning an incompatible type");
|
||||
throw Slic3r::RuntimeError("ConfigOptionVector::set_at(): Assigning an incompatible type");
|
||||
}
|
||||
|
||||
const T& get_at(size_t i) const
|
||||
|
|
@ -310,9 +311,9 @@ public:
|
|||
else if (n > this->values.size()) {
|
||||
if (this->values.empty()) {
|
||||
if (opt_default == nullptr)
|
||||
throw std::runtime_error("ConfigOptionVector::resize(): No default value provided.");
|
||||
throw Slic3r::RuntimeError("ConfigOptionVector::resize(): No default value provided.");
|
||||
if (opt_default->type() != this->type())
|
||||
throw std::runtime_error("ConfigOptionVector::resize(): Extending with an incompatible type.");
|
||||
throw Slic3r::RuntimeError("ConfigOptionVector::resize(): Extending with an incompatible type.");
|
||||
this->values.resize(n, static_cast<const ConfigOptionVector<T>*>(opt_default)->values.front());
|
||||
} else {
|
||||
// Resize by duplicating the last value.
|
||||
|
|
@ -329,7 +330,7 @@ public:
|
|||
bool operator==(const ConfigOption &rhs) const override
|
||||
{
|
||||
if (rhs.type() != this->type())
|
||||
throw std::runtime_error("ConfigOptionVector: Comparing incompatible types");
|
||||
throw Slic3r::RuntimeError("ConfigOptionVector: Comparing incompatible types");
|
||||
assert(dynamic_cast<const ConfigOptionVector<T>*>(&rhs));
|
||||
return this->values == static_cast<const ConfigOptionVector<T>*>(&rhs)->values;
|
||||
}
|
||||
|
|
@ -341,9 +342,9 @@ public:
|
|||
// An option overrides another option if it is not nil and not equal.
|
||||
bool overriden_by(const ConfigOption *rhs) const override {
|
||||
if (this->nullable())
|
||||
throw std::runtime_error("Cannot override a nullable ConfigOption.");
|
||||
throw Slic3r::RuntimeError("Cannot override a nullable ConfigOption.");
|
||||
if (rhs->type() != this->type())
|
||||
throw std::runtime_error("ConfigOptionVector.overriden_by() applied to different types.");
|
||||
throw Slic3r::RuntimeError("ConfigOptionVector.overriden_by() applied to different types.");
|
||||
auto rhs_vec = static_cast<const ConfigOptionVector<T>*>(rhs);
|
||||
if (! rhs->nullable())
|
||||
// Overridding a non-nullable object with another non-nullable object.
|
||||
|
|
@ -361,9 +362,9 @@ public:
|
|||
// Apply an override option, possibly a nullable one.
|
||||
bool apply_override(const ConfigOption *rhs) override {
|
||||
if (this->nullable())
|
||||
throw std::runtime_error("Cannot override a nullable ConfigOption.");
|
||||
throw Slic3r::RuntimeError("Cannot override a nullable ConfigOption.");
|
||||
if (rhs->type() != this->type())
|
||||
throw std::runtime_error("ConfigOptionVector.apply_override() applied to different types.");
|
||||
throw Slic3r::RuntimeError("ConfigOptionVector.apply_override() applied to different types.");
|
||||
auto rhs_vec = static_cast<const ConfigOptionVector<T>*>(rhs);
|
||||
if (! rhs->nullable()) {
|
||||
// Overridding a non-nullable object with another non-nullable object.
|
||||
|
|
@ -452,7 +453,7 @@ public:
|
|||
bool operator==(const ConfigOptionFloatsTempl &rhs) const { return vectors_equal(this->values, rhs.values); }
|
||||
bool operator==(const ConfigOption &rhs) const override {
|
||||
if (rhs.type() != this->type())
|
||||
throw std::runtime_error("ConfigOptionFloatsTempl: Comparing incompatible types");
|
||||
throw Slic3r::RuntimeError("ConfigOptionFloatsTempl: Comparing incompatible types");
|
||||
assert(dynamic_cast<const ConfigOptionVector<double>*>(&rhs));
|
||||
return vectors_equal(this->values, static_cast<const ConfigOptionVector<double>*>(&rhs)->values);
|
||||
}
|
||||
|
|
@ -499,7 +500,7 @@ public:
|
|||
if (NULLABLE)
|
||||
this->values.push_back(nil_value());
|
||||
else
|
||||
throw std::runtime_error("Deserializing nil into a non-nullable object");
|
||||
throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object");
|
||||
} else {
|
||||
std::istringstream iss(item_str);
|
||||
double value;
|
||||
|
|
@ -524,9 +525,9 @@ protected:
|
|||
if (NULLABLE)
|
||||
ss << "nil";
|
||||
else
|
||||
throw std::runtime_error("Serializing NaN");
|
||||
throw Slic3r::RuntimeError("Serializing NaN");
|
||||
} else
|
||||
throw std::runtime_error("Serializing invalid number");
|
||||
throw Slic3r::RuntimeError("Serializing invalid number");
|
||||
}
|
||||
static bool vectors_equal(const std::vector<double> &v1, const std::vector<double> &v2) {
|
||||
if (NULLABLE) {
|
||||
|
|
@ -645,7 +646,7 @@ public:
|
|||
if (NULLABLE)
|
||||
this->values.push_back(nil_value());
|
||||
else
|
||||
throw std::runtime_error("Deserializing nil into a non-nullable object");
|
||||
throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object");
|
||||
} else {
|
||||
std::istringstream iss(item_str);
|
||||
int value;
|
||||
|
|
@ -662,7 +663,7 @@ private:
|
|||
if (NULLABLE)
|
||||
ss << "nil";
|
||||
else
|
||||
throw std::runtime_error("Serializing NaN");
|
||||
throw Slic3r::RuntimeError("Serializing NaN");
|
||||
} else
|
||||
ss << v;
|
||||
}
|
||||
|
|
@ -847,7 +848,7 @@ public:
|
|||
bool operator==(const ConfigOption &rhs) const override
|
||||
{
|
||||
if (rhs.type() != this->type())
|
||||
throw std::runtime_error("ConfigOptionFloatOrPercent: Comparing incompatible types");
|
||||
throw Slic3r::RuntimeError("ConfigOptionFloatOrPercent: Comparing incompatible types");
|
||||
assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(&rhs));
|
||||
return *this == *static_cast<const ConfigOptionFloatOrPercent*>(&rhs);
|
||||
}
|
||||
|
|
@ -858,7 +859,7 @@ public:
|
|||
|
||||
void set(const ConfigOption *rhs) override {
|
||||
if (rhs->type() != this->type())
|
||||
throw std::runtime_error("ConfigOptionFloatOrPercent: Assigning an incompatible type");
|
||||
throw Slic3r::RuntimeError("ConfigOptionFloatOrPercent: Assigning an incompatible type");
|
||||
assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(rhs));
|
||||
*this = *static_cast<const ConfigOptionFloatOrPercent*>(rhs);
|
||||
}
|
||||
|
|
@ -1126,7 +1127,7 @@ public:
|
|||
if (NULLABLE)
|
||||
this->values.push_back(nil_value());
|
||||
else
|
||||
throw std::runtime_error("Deserializing nil into a non-nullable object");
|
||||
throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object");
|
||||
} else
|
||||
this->values.push_back(item_str.compare("1") == 0);
|
||||
}
|
||||
|
|
@ -1139,7 +1140,7 @@ protected:
|
|||
if (NULLABLE)
|
||||
ss << "nil";
|
||||
else
|
||||
throw std::runtime_error("Serializing NaN");
|
||||
throw Slic3r::RuntimeError("Serializing NaN");
|
||||
} else
|
||||
ss << (v ? "1" : "0");
|
||||
}
|
||||
|
|
@ -1175,14 +1176,14 @@ public:
|
|||
bool operator==(const ConfigOption &rhs) const override
|
||||
{
|
||||
if (rhs.type() != this->type())
|
||||
throw std::runtime_error("ConfigOptionEnum<T>: Comparing incompatible types");
|
||||
throw Slic3r::RuntimeError("ConfigOptionEnum<T>: Comparing incompatible types");
|
||||
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
|
||||
return this->value == (T)rhs.getInt();
|
||||
}
|
||||
|
||||
void set(const ConfigOption *rhs) override {
|
||||
if (rhs->type() != this->type())
|
||||
throw std::runtime_error("ConfigOptionEnum<T>: Assigning an incompatible type");
|
||||
throw Slic3r::RuntimeError("ConfigOptionEnum<T>: Assigning an incompatible type");
|
||||
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
|
||||
this->value = (T)rhs->getInt();
|
||||
}
|
||||
|
|
@ -1259,14 +1260,14 @@ public:
|
|||
bool operator==(const ConfigOption &rhs) const override
|
||||
{
|
||||
if (rhs.type() != this->type())
|
||||
throw std::runtime_error("ConfigOptionEnumGeneric: Comparing incompatible types");
|
||||
throw Slic3r::RuntimeError("ConfigOptionEnumGeneric: Comparing incompatible types");
|
||||
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
|
||||
return this->value == rhs.getInt();
|
||||
}
|
||||
|
||||
void set(const ConfigOption *rhs) override {
|
||||
if (rhs->type() != this->type())
|
||||
throw std::runtime_error("ConfigOptionEnumGeneric: Assigning an incompatible type");
|
||||
throw Slic3r::RuntimeError("ConfigOptionEnumGeneric: Assigning an incompatible type");
|
||||
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
|
||||
this->value = rhs->getInt();
|
||||
}
|
||||
|
|
@ -1321,7 +1322,7 @@ public:
|
|||
case coInts: { auto opt = new ConfigOptionIntsNullable(); archive(*opt); return opt; }
|
||||
case coPercents: { auto opt = new ConfigOptionPercentsNullable();archive(*opt); return opt; }
|
||||
case coBools: { auto opt = new ConfigOptionBoolsNullable(); archive(*opt); return opt; }
|
||||
default: throw std::runtime_error(std::string("ConfigOptionDef::load_option_from_archive(): Unknown nullable option type for option ") + this->opt_key);
|
||||
default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown nullable option type for option ") + this->opt_key);
|
||||
}
|
||||
} else {
|
||||
switch (this->type) {
|
||||
|
|
@ -1340,7 +1341,7 @@ public:
|
|||
case coBool: { auto opt = new ConfigOptionBool(); archive(*opt); return opt; }
|
||||
case coBools: { auto opt = new ConfigOptionBools(); archive(*opt); return opt; }
|
||||
case coEnum: { auto opt = new ConfigOptionEnumGeneric(this->enum_keys_map); archive(*opt); return opt; }
|
||||
default: throw std::runtime_error(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key);
|
||||
default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1352,7 +1353,7 @@ public:
|
|||
case coInts: archive(*static_cast<const ConfigOptionIntsNullable*>(opt)); break;
|
||||
case coPercents: archive(*static_cast<const ConfigOptionPercentsNullable*>(opt));break;
|
||||
case coBools: archive(*static_cast<const ConfigOptionBoolsNullable*>(opt)); break;
|
||||
default: throw std::runtime_error(std::string("ConfigOptionDef::save_option_to_archive(): Unknown nullable option type for option ") + this->opt_key);
|
||||
default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown nullable option type for option ") + this->opt_key);
|
||||
}
|
||||
} else {
|
||||
switch (this->type) {
|
||||
|
|
@ -1371,7 +1372,7 @@ public:
|
|||
case coBool: archive(*static_cast<const ConfigOptionBool*>(opt)); break;
|
||||
case coBools: archive(*static_cast<const ConfigOptionBools*>(opt)); break;
|
||||
case coEnum: archive(*static_cast<const ConfigOptionEnumGeneric*>(opt)); break;
|
||||
default: throw std::runtime_error(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") + this->opt_key);
|
||||
default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") + this->opt_key);
|
||||
}
|
||||
}
|
||||
// Make the compiler happy, shut up the warnings.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
#include "CustomGCode.hpp"
|
||||
#include "Config.hpp"
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include "GCode.hpp"
|
||||
#else
|
||||
#include "GCode/PreviewData.hpp"
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
#include "GCodeWriter.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
@ -17,8 +21,12 @@ extern void update_custom_gcode_per_print_z_from_config(Info& info, DynamicPrint
|
|||
return;
|
||||
if (info.gcodes.empty() && ! colorprint_heights->values.empty()) {
|
||||
// Convert the old colorprint_heighs only if there is no equivalent data in a new format.
|
||||
const std::vector<std::string>& colors = GCodePreviewData::ColorPrintColors();
|
||||
const auto& colorprint_values = colorprint_heights->values;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
const std::vector<std::string>& colors = ColorPrintColors::get();
|
||||
#else
|
||||
const std::vector<std::string>& colors = GCodePreviewData::ColorPrintColors();
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
const auto& colorprint_values = colorprint_heights->values;
|
||||
info.gcodes.clear();
|
||||
info.gcodes.reserve(colorprint_values.size());
|
||||
int i = 0;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "BoundingBox.hpp"
|
||||
#include "ExPolygon.hpp"
|
||||
#include "Exception.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "Polygon.hpp"
|
||||
#include "Line.hpp"
|
||||
|
|
@ -435,7 +436,7 @@ void ExPolygon::triangulate_pp(Polygons* polygons) const
|
|||
std::list<TPPLPoly> output;
|
||||
int res = TPPLPartition().Triangulate_MONO(&input, &output);
|
||||
if (res != 1)
|
||||
throw std::runtime_error("Triangulation failed");
|
||||
throw Slic3r::RuntimeError("Triangulation failed");
|
||||
|
||||
// convert output polygons
|
||||
for (std::list<TPPLPoly>::iterator poly = output.begin(); poly != output.end(); ++poly) {
|
||||
|
|
@ -548,7 +549,7 @@ void ExPolygon::triangulate_pp(Points *triangles) const
|
|||
int res = TPPLPartition().Triangulate_MONO(&input, &output);
|
||||
// int TPPLPartition::Triangulate_EC(TPPLPolyList *inpolys, TPPLPolyList *triangles) {
|
||||
if (res != 1)
|
||||
throw std::runtime_error("Triangulation failed");
|
||||
throw Slic3r::RuntimeError("Triangulation failed");
|
||||
*triangles = polypartition_output_to_triangles(output);
|
||||
}
|
||||
|
||||
|
|
@ -591,7 +592,7 @@ void ExPolygon::triangulate_p2t(Polygons* polygons) const
|
|||
}
|
||||
polygons->push_back(p);
|
||||
}
|
||||
} catch (const std::runtime_error & /* err */) {
|
||||
} catch (const Slic3r::RuntimeError & /* err */) {
|
||||
assert(false);
|
||||
// just ignore, don't triangulate
|
||||
}
|
||||
|
|
|
|||
|
|
@ -333,6 +333,14 @@ extern std::list<TPPLPoly> expoly_to_polypartition_input(const ExPolygons &expp)
|
|||
extern std::list<TPPLPoly> expoly_to_polypartition_input(const ExPolygon &ex);
|
||||
extern std::vector<Point> polypartition_output_to_triangles(const std::list<TPPLPoly> &output);
|
||||
|
||||
inline double area(const ExPolygons &polys)
|
||||
{
|
||||
double s = 0.;
|
||||
for (auto &p : polys) s += p.area();
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
// start Boost
|
||||
|
|
|
|||
28
src/libslic3r/Exception.hpp
Normal file
28
src/libslic3r/Exception.hpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef _libslic3r_Exception_h_
|
||||
#define _libslic3r_Exception_h_
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// PrusaSlicer's own exception hierarchy is derived from std::runtime_error.
|
||||
// Base for Slicer's own exceptions.
|
||||
class Exception : public std::runtime_error { using std::runtime_error::runtime_error; };
|
||||
#define SLIC3R_DERIVE_EXCEPTION(DERIVED_EXCEPTION, PARENT_EXCEPTION) \
|
||||
class DERIVED_EXCEPTION : public PARENT_EXCEPTION { using PARENT_EXCEPTION::PARENT_EXCEPTION; }
|
||||
// Critical exception produced by Slicer, such exception shall never propagate up to the UI thread.
|
||||
// If that happens, an ugly fat message box with an ugly fat exclamation mark is displayed.
|
||||
SLIC3R_DERIVE_EXCEPTION(CriticalException, Exception);
|
||||
SLIC3R_DERIVE_EXCEPTION(RuntimeError, CriticalException);
|
||||
SLIC3R_DERIVE_EXCEPTION(LogicError, CriticalException);
|
||||
SLIC3R_DERIVE_EXCEPTION(InvalidArgument, LogicError);
|
||||
SLIC3R_DERIVE_EXCEPTION(OutOfRange, LogicError);
|
||||
SLIC3R_DERIVE_EXCEPTION(IOError, CriticalException);
|
||||
SLIC3R_DERIVE_EXCEPTION(FileIOError, IOError);
|
||||
// Runtime exception produced by Slicer. Such exception cancels the slicing process and it shall be shown in notifications.
|
||||
SLIC3R_DERIVE_EXCEPTION(SlicingError, Exception);
|
||||
#undef SLIC3R_DERIVE_EXCEPTION
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // _libslic3r_Exception_h_
|
||||
|
|
@ -306,14 +306,18 @@ double ExtrusionLoop::min_mm3_per_mm() const
|
|||
std::string ExtrusionEntity::role_to_string(ExtrusionRole role)
|
||||
{
|
||||
switch (role) {
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
case erNone : return L("Unknown");
|
||||
#else
|
||||
case erNone : return L("None");
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
case erPerimeter : return L("Perimeter");
|
||||
case erExternalPerimeter : return L("External perimeter");
|
||||
case erOverhangPerimeter : return L("Overhang perimeter");
|
||||
case erInternalInfill : return L("Internal infill");
|
||||
case erSolidInfill : return L("Solid infill");
|
||||
case erIroning : return L("Ironing");
|
||||
case erTopSolidInfill : return L("Top solid infill");
|
||||
case erIroning : return L("Ironing");
|
||||
case erBridgeInfill : return L("Bridge infill");
|
||||
case erGapFill : return L("Gap fill");
|
||||
case erSkirt : return L("Skirt");
|
||||
|
|
@ -327,4 +331,40 @@ std::string ExtrusionEntity::role_to_string(ExtrusionRole role)
|
|||
return "";
|
||||
}
|
||||
|
||||
ExtrusionRole ExtrusionEntity::string_to_role(const std::string& role)
|
||||
{
|
||||
if (role == L("Perimeter"))
|
||||
return erPerimeter;
|
||||
else if (role == L("External perimeter"))
|
||||
return erExternalPerimeter;
|
||||
else if (role == L("Overhang perimeter"))
|
||||
return erOverhangPerimeter;
|
||||
else if (role == L("Internal infill"))
|
||||
return erInternalInfill;
|
||||
else if (role == L("Solid infill"))
|
||||
return erSolidInfill;
|
||||
else if (role == L("Top solid infill"))
|
||||
return erTopSolidInfill;
|
||||
else if (role == L("Ironing"))
|
||||
return erIroning;
|
||||
else if (role == L("Bridge infill"))
|
||||
return erBridgeInfill;
|
||||
else if (role == L("Gap fill"))
|
||||
return erGapFill;
|
||||
else if (role == L("Skirt"))
|
||||
return erSkirt;
|
||||
else if (role == L("Support material"))
|
||||
return erSupportMaterial;
|
||||
else if (role == L("Support material interface"))
|
||||
return erSupportMaterialInterface;
|
||||
else if (role == L("Wipe tower"))
|
||||
return erWipeTower;
|
||||
else if (role == L("Custom"))
|
||||
return erCustom;
|
||||
else if (role == L("Mixed"))
|
||||
return erMixed;
|
||||
else
|
||||
return erNone;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ public:
|
|||
virtual double total_volume() const = 0;
|
||||
|
||||
static std::string role_to_string(ExtrusionRole role);
|
||||
static ExtrusionRole string_to_role(const std::string& role);
|
||||
};
|
||||
|
||||
typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
|
||||
|
|
@ -121,8 +122,8 @@ public:
|
|||
// Height of the extrusion, used for visualization purposes.
|
||||
float height;
|
||||
|
||||
ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role) {};
|
||||
ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role) {};
|
||||
ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role) {}
|
||||
ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role) {}
|
||||
ExtrusionPath(const ExtrusionPath& rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {}
|
||||
ExtrusionPath(ExtrusionPath&& rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {}
|
||||
ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) : polyline(polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#define slic3r_ExtrusionEntityCollection_hpp_
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "Exception.hpp"
|
||||
#include "ExtrusionEntity.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
@ -107,7 +108,7 @@ public:
|
|||
|
||||
// Following methods shall never be called on an ExtrusionEntityCollection.
|
||||
Polyline as_polyline() const override {
|
||||
throw std::runtime_error("Calling as_polyline() on a ExtrusionEntityCollection");
|
||||
throw Slic3r::RuntimeError("Calling as_polyline() on a ExtrusionEntityCollection");
|
||||
return Polyline();
|
||||
};
|
||||
|
||||
|
|
@ -117,7 +118,7 @@ public:
|
|||
}
|
||||
|
||||
double length() const override {
|
||||
throw std::runtime_error("Calling length() on a ExtrusionEntityCollection");
|
||||
throw Slic3r::RuntimeError("Calling length() on a ExtrusionEntityCollection");
|
||||
return 0.;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@
|
|||
namespace Slic3r {
|
||||
|
||||
// Generic file parser error, mostly copied from boost::property_tree::file_parser_error
|
||||
class file_parser_error: public std::runtime_error
|
||||
class file_parser_error: public Slic3r::RuntimeError
|
||||
{
|
||||
public:
|
||||
file_parser_error(const std::string &msg, const std::string &file, unsigned long line = 0) :
|
||||
std::runtime_error(format_what(msg, file, line)),
|
||||
Slic3r::RuntimeError(format_what(msg, file, line)),
|
||||
m_message(msg), m_filename(file), m_line(line) {}
|
||||
file_parser_error(const std::string &msg, const boost::filesystem::path &file, unsigned long line = 0) :
|
||||
std::runtime_error(format_what(msg, file.string(), line)),
|
||||
Slic3r::RuntimeError(format_what(msg, file.string(), line)),
|
||||
m_message(msg), m_filename(file.string()), m_line(line) {}
|
||||
// gcc 3.4.2 complains about lack of throw specifier on compiler
|
||||
// generated dtor
|
||||
|
|
@ -35,7 +35,7 @@ private:
|
|||
std::string m_filename;
|
||||
unsigned long m_line;
|
||||
|
||||
// Format error message to be returned by std::runtime_error::what()
|
||||
// Format error message to be returned by Slic3r::RuntimeError::what()
|
||||
static std::string format_what(const std::string &msg, const std::string &file, unsigned long l)
|
||||
{
|
||||
std::stringstream stream;
|
||||
|
|
|
|||
|
|
@ -318,7 +318,7 @@ void export_group_fills_to_svg(const char *path, const std::vector<SurfaceFill>
|
|||
#endif
|
||||
|
||||
// friend to Layer
|
||||
void Layer::make_fills()
|
||||
void Layer::make_fills(FillAdaptive_Internal::Octree* adaptive_fill_octree, FillAdaptive_Internal::Octree* support_fill_octree)
|
||||
{
|
||||
for (LayerRegion *layerm : m_regions)
|
||||
layerm->fills.clear();
|
||||
|
|
@ -345,6 +345,8 @@ void Layer::make_fills()
|
|||
f->layer_id = this->id();
|
||||
f->z = this->print_z;
|
||||
f->angle = surface_fill.params.angle;
|
||||
f->adapt_fill_octree = adaptive_fill_octree;
|
||||
f->support_fill_octree = support_fill_octree;
|
||||
|
||||
// calculate flow spacing for infill pattern generation
|
||||
bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.flow.bridge;
|
||||
|
|
|
|||
520
src/libslic3r/Fill/FillAdaptive.cpp
Normal file
520
src/libslic3r/Fill/FillAdaptive.cpp
Normal file
|
|
@ -0,0 +1,520 @@
|
|||
#include "../ClipperUtils.hpp"
|
||||
#include "../ExPolygon.hpp"
|
||||
#include "../Surface.hpp"
|
||||
#include "../Geometry.hpp"
|
||||
#include "../AABBTreeIndirect.hpp"
|
||||
#include "../Layer.hpp"
|
||||
#include "../Print.hpp"
|
||||
#include "../ShortestPath.hpp"
|
||||
|
||||
#include "FillAdaptive.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
std::pair<double, double> adaptive_fill_line_spacing(const PrintObject &print_object)
|
||||
{
|
||||
// Output, spacing for icAdaptiveCubic and icSupportCubic
|
||||
double adaptive_line_spacing = 0.;
|
||||
double support_line_spacing = 0.;
|
||||
|
||||
enum class Tristate {
|
||||
Yes,
|
||||
No,
|
||||
Maybe
|
||||
};
|
||||
struct RegionFillData {
|
||||
Tristate has_adaptive_infill;
|
||||
Tristate has_support_infill;
|
||||
double density;
|
||||
double extrusion_width;
|
||||
};
|
||||
std::vector<RegionFillData> region_fill_data;
|
||||
region_fill_data.reserve(print_object.print()->regions().size());
|
||||
bool build_octree = false;
|
||||
for (const PrintRegion *region : print_object.print()->regions()) {
|
||||
const PrintRegionConfig &config = region->config();
|
||||
bool nonempty = config.fill_density > 0;
|
||||
bool has_adaptive_infill = nonempty && config.fill_pattern == ipAdaptiveCubic;
|
||||
bool has_support_infill = nonempty && config.fill_pattern == ipSupportCubic;
|
||||
region_fill_data.push_back(RegionFillData({
|
||||
has_adaptive_infill ? Tristate::Maybe : Tristate::No,
|
||||
has_support_infill ? Tristate::Maybe : Tristate::No,
|
||||
config.fill_density,
|
||||
config.infill_extrusion_width
|
||||
}));
|
||||
build_octree |= has_adaptive_infill || has_support_infill;
|
||||
}
|
||||
|
||||
if (build_octree) {
|
||||
// Compute the average of above parameters over all layers
|
||||
for (const Layer *layer : print_object.layers())
|
||||
for (size_t region_id = 0; region_id < layer->regions().size(); ++ region_id) {
|
||||
RegionFillData &rd = region_fill_data[region_id];
|
||||
if (rd.has_adaptive_infill == Tristate::Maybe && ! layer->regions()[region_id]->fill_surfaces.empty())
|
||||
rd.has_adaptive_infill = Tristate::Yes;
|
||||
if (rd.has_support_infill == Tristate::Maybe && ! layer->regions()[region_id]->fill_surfaces.empty())
|
||||
rd.has_support_infill = Tristate::Yes;
|
||||
}
|
||||
|
||||
double adaptive_fill_density = 0.;
|
||||
double adaptive_infill_extrusion_width = 0.;
|
||||
int adaptive_cnt = 0;
|
||||
double support_fill_density = 0.;
|
||||
double support_infill_extrusion_width = 0.;
|
||||
int support_cnt = 0;
|
||||
|
||||
for (const RegionFillData &rd : region_fill_data) {
|
||||
if (rd.has_adaptive_infill == Tristate::Yes) {
|
||||
adaptive_fill_density += rd.density;
|
||||
adaptive_infill_extrusion_width += rd.extrusion_width;
|
||||
++ adaptive_cnt;
|
||||
} else if (rd.has_support_infill == Tristate::Yes) {
|
||||
support_fill_density += rd.density;
|
||||
support_infill_extrusion_width += rd.extrusion_width;
|
||||
++ support_cnt;
|
||||
}
|
||||
}
|
||||
|
||||
auto to_line_spacing = [](int cnt, double density, double extrusion_width) {
|
||||
if (cnt) {
|
||||
density /= double(cnt);
|
||||
extrusion_width /= double(cnt);
|
||||
return extrusion_width / ((density / 100.0f) * 0.333333333f);
|
||||
} else
|
||||
return 0.;
|
||||
};
|
||||
adaptive_line_spacing = to_line_spacing(adaptive_cnt, adaptive_fill_density, adaptive_infill_extrusion_width);
|
||||
support_line_spacing = to_line_spacing(support_cnt, support_fill_density, support_infill_extrusion_width);
|
||||
}
|
||||
|
||||
return std::make_pair(adaptive_line_spacing, support_line_spacing);
|
||||
}
|
||||
|
||||
void FillAdaptive::_fill_surface_single(const FillParams & params,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon & expolygon,
|
||||
Polylines & polylines_out)
|
||||
{
|
||||
if(this->adapt_fill_octree != nullptr)
|
||||
this->generate_infill(params, thickness_layers, direction, expolygon, polylines_out, this->adapt_fill_octree);
|
||||
}
|
||||
|
||||
void FillAdaptive::generate_infill(const FillParams & params,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon & expolygon,
|
||||
Polylines & polylines_out,
|
||||
FillAdaptive_Internal::Octree *octree)
|
||||
{
|
||||
Vec3d rotation = Vec3d((5.0 * M_PI) / 4.0, Geometry::deg2rad(215.264), M_PI / 6.0);
|
||||
Transform3d rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation, Vec3d::Ones(), Vec3d::Ones());
|
||||
|
||||
// Store grouped lines by its direction (multiple of 120°)
|
||||
std::vector<Lines> infill_lines_dir(3);
|
||||
this->generate_infill_lines(octree->root_cube.get(),
|
||||
this->z, octree->origin, rotation_matrix,
|
||||
infill_lines_dir, octree->cubes_properties,
|
||||
int(octree->cubes_properties.size()) - 1);
|
||||
|
||||
Polylines all_polylines;
|
||||
all_polylines.reserve(infill_lines_dir[0].size() * 3);
|
||||
for (Lines &infill_lines : infill_lines_dir)
|
||||
{
|
||||
for (const Line &line : infill_lines)
|
||||
{
|
||||
all_polylines.emplace_back(line.a, line.b);
|
||||
}
|
||||
}
|
||||
|
||||
if (params.dont_connect)
|
||||
{
|
||||
// Crop all polylines
|
||||
polylines_out = intersection_pl(all_polylines, to_polygons(expolygon));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Crop all polylines
|
||||
all_polylines = intersection_pl(all_polylines, to_polygons(expolygon));
|
||||
|
||||
Polylines boundary_polylines;
|
||||
Polylines non_boundary_polylines;
|
||||
for (const Polyline &polyline : all_polylines)
|
||||
{
|
||||
// connect_infill required all polylines to touch the boundary.
|
||||
if(polyline.lines().size() == 1 && expolygon.has_boundary_point(polyline.lines().front().a) && expolygon.has_boundary_point(polyline.lines().front().b))
|
||||
{
|
||||
boundary_polylines.push_back(polyline);
|
||||
}
|
||||
else
|
||||
{
|
||||
non_boundary_polylines.push_back(polyline);
|
||||
}
|
||||
}
|
||||
|
||||
if(!boundary_polylines.empty())
|
||||
{
|
||||
boundary_polylines = chain_polylines(boundary_polylines);
|
||||
FillAdaptive::connect_infill(std::move(boundary_polylines), expolygon, polylines_out, this->spacing, params);
|
||||
}
|
||||
|
||||
polylines_out.insert(polylines_out.end(), non_boundary_polylines.begin(), non_boundary_polylines.end());
|
||||
}
|
||||
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
{
|
||||
static int iRuna = 0;
|
||||
BoundingBox bbox_svg = this->bounding_box;
|
||||
{
|
||||
::Slic3r::SVG svg(debug_out_path("FillAdaptive-%d.svg", iRuna), bbox_svg);
|
||||
for (const Polyline &polyline : polylines_out)
|
||||
{
|
||||
for (const Line &line : polyline.lines())
|
||||
{
|
||||
Point from = line.a;
|
||||
Point to = line.b;
|
||||
Point diff = to - from;
|
||||
|
||||
float shrink_length = scale_(0.4);
|
||||
float line_slope = (float)diff.y() / diff.x();
|
||||
float shrink_x = shrink_length / (float)std::sqrt(1.0 + (line_slope * line_slope));
|
||||
float shrink_y = line_slope * shrink_x;
|
||||
|
||||
to.x() -= shrink_x;
|
||||
to.y() -= shrink_y;
|
||||
from.x() += shrink_x;
|
||||
from.y() += shrink_y;
|
||||
|
||||
svg.draw(Line(from, to));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iRuna++;
|
||||
}
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
}
|
||||
|
||||
void FillAdaptive::generate_infill_lines(
|
||||
FillAdaptive_Internal::Cube *cube,
|
||||
double z_position,
|
||||
const Vec3d &origin,
|
||||
const Transform3d &rotation_matrix,
|
||||
std::vector<Lines> &dir_lines_out,
|
||||
const std::vector<FillAdaptive_Internal::CubeProperties> &cubes_properties,
|
||||
int depth)
|
||||
{
|
||||
using namespace FillAdaptive_Internal;
|
||||
|
||||
if(cube == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vec3d cube_center_tranformed = rotation_matrix * cube->center;
|
||||
double z_diff = std::abs(z_position - cube_center_tranformed.z());
|
||||
|
||||
if (z_diff > cubes_properties[depth].height / 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (z_diff < cubes_properties[depth].line_z_distance)
|
||||
{
|
||||
Point from(
|
||||
scale_((cubes_properties[depth].diagonal_length / 2) * (cubes_properties[depth].line_z_distance - z_diff) / cubes_properties[depth].line_z_distance),
|
||||
scale_(cubes_properties[depth].line_xy_distance - ((z_position - (cube_center_tranformed.z() - cubes_properties[depth].line_z_distance)) / sqrt(2))));
|
||||
Point to(-from.x(), from.y());
|
||||
// Relative to cube center
|
||||
|
||||
double rotation_angle = (2.0 * M_PI) / 3.0;
|
||||
for (Lines &lines : dir_lines_out)
|
||||
{
|
||||
Vec3d offset = cube_center_tranformed - (rotation_matrix * origin);
|
||||
Point from_abs(from), to_abs(to);
|
||||
|
||||
from_abs.x() += int(scale_(offset.x()));
|
||||
from_abs.y() += int(scale_(offset.y()));
|
||||
to_abs.x() += int(scale_(offset.x()));
|
||||
to_abs.y() += int(scale_(offset.y()));
|
||||
|
||||
// lines.emplace_back(from_abs, to_abs);
|
||||
this->connect_lines(lines, Line(from_abs, to_abs));
|
||||
|
||||
from.rotate(rotation_angle);
|
||||
to.rotate(rotation_angle);
|
||||
}
|
||||
}
|
||||
|
||||
for(const std::unique_ptr<Cube> &child : cube->children)
|
||||
{
|
||||
if(child != nullptr)
|
||||
{
|
||||
generate_infill_lines(child.get(), z_position, origin, rotation_matrix, dir_lines_out, cubes_properties, depth - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FillAdaptive::connect_lines(Lines &lines, Line new_line)
|
||||
{
|
||||
auto eps = int(scale_(0.10));
|
||||
for (size_t i = 0; i < lines.size(); ++i)
|
||||
{
|
||||
if (std::abs(new_line.a.x() - lines[i].b.x()) < eps && std::abs(new_line.a.y() - lines[i].b.y()) < eps)
|
||||
{
|
||||
new_line.a = lines[i].a;
|
||||
lines.erase(lines.begin() + i);
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::abs(new_line.b.x() - lines[i].a.x()) < eps && std::abs(new_line.b.y() - lines[i].a.y()) < eps)
|
||||
{
|
||||
new_line.b = lines[i].b;
|
||||
lines.erase(lines.begin() + i);
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
lines.emplace_back(new_line.a, new_line.b);
|
||||
}
|
||||
|
||||
std::unique_ptr<FillAdaptive_Internal::Octree> FillAdaptive::build_octree(
|
||||
TriangleMesh &triangle_mesh,
|
||||
coordf_t line_spacing,
|
||||
const Vec3d &cube_center)
|
||||
{
|
||||
using namespace FillAdaptive_Internal;
|
||||
|
||||
if(line_spacing <= 0 || std::isnan(line_spacing))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Vec3d bb_size = triangle_mesh.bounding_box().size();
|
||||
// The furthest point from the center of the bottom of the mesh bounding box.
|
||||
double furthest_point = std::sqrt(((bb_size.x() * bb_size.x()) / 4.0) +
|
||||
((bb_size.y() * bb_size.y()) / 4.0) +
|
||||
(bb_size.z() * bb_size.z()));
|
||||
double max_cube_edge_length = furthest_point * 2;
|
||||
|
||||
std::vector<CubeProperties> cubes_properties;
|
||||
for (double edge_length = (line_spacing * 2); edge_length < (max_cube_edge_length * 2); edge_length *= 2)
|
||||
{
|
||||
CubeProperties props{};
|
||||
props.edge_length = edge_length;
|
||||
props.height = edge_length * sqrt(3);
|
||||
props.diagonal_length = edge_length * sqrt(2);
|
||||
props.line_z_distance = edge_length / sqrt(3);
|
||||
props.line_xy_distance = edge_length / sqrt(6);
|
||||
cubes_properties.push_back(props);
|
||||
}
|
||||
|
||||
if (triangle_mesh.its.vertices.empty())
|
||||
{
|
||||
triangle_mesh.require_shared_vertices();
|
||||
}
|
||||
|
||||
AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(
|
||||
triangle_mesh.its.vertices, triangle_mesh.its.indices);
|
||||
auto octree = std::make_unique<Octree>(std::make_unique<Cube>(cube_center), cube_center, cubes_properties);
|
||||
|
||||
FillAdaptive::expand_cube(octree->root_cube.get(), cubes_properties, aabbTree, triangle_mesh, int(cubes_properties.size()) - 1);
|
||||
|
||||
return octree;
|
||||
}
|
||||
|
||||
void FillAdaptive::expand_cube(
|
||||
FillAdaptive_Internal::Cube *cube,
|
||||
const std::vector<FillAdaptive_Internal::CubeProperties> &cubes_properties,
|
||||
const AABBTreeIndirect::Tree3f &distance_tree,
|
||||
const TriangleMesh &triangle_mesh, int depth)
|
||||
{
|
||||
using namespace FillAdaptive_Internal;
|
||||
|
||||
if (cube == nullptr || depth == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<Vec3d> child_centers = {
|
||||
Vec3d(-1, -1, -1), Vec3d( 1, -1, -1), Vec3d(-1, 1, -1), Vec3d( 1, 1, -1),
|
||||
Vec3d(-1, -1, 1), Vec3d( 1, -1, 1), Vec3d(-1, 1, 1), Vec3d( 1, 1, 1)
|
||||
};
|
||||
|
||||
double cube_radius_squared = (cubes_properties[depth].height * cubes_properties[depth].height) / 16;
|
||||
|
||||
for (size_t i = 0; i < 8; ++i)
|
||||
{
|
||||
const Vec3d &child_center = child_centers[i];
|
||||
Vec3d child_center_transformed = cube->center + (child_center * (cubes_properties[depth].edge_length / 4));
|
||||
|
||||
if(AABBTreeIndirect::is_any_triangle_in_radius(triangle_mesh.its.vertices, triangle_mesh.its.indices,
|
||||
distance_tree, child_center_transformed, cube_radius_squared))
|
||||
{
|
||||
cube->children[i] = std::make_unique<Cube>(child_center_transformed);
|
||||
FillAdaptive::expand_cube(cube->children[i].get(), cubes_properties, distance_tree, triangle_mesh, depth - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FillAdaptive_Internal::Octree::propagate_point(
|
||||
Vec3d point,
|
||||
FillAdaptive_Internal::Cube * current,
|
||||
int depth,
|
||||
const std::vector<FillAdaptive_Internal::CubeProperties> &cubes_properties)
|
||||
{
|
||||
using namespace FillAdaptive_Internal;
|
||||
|
||||
if(depth <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
size_t octant_idx = Octree::find_octant(point, current->center);
|
||||
Cube * child = current->children[octant_idx].get();
|
||||
|
||||
// Octant not exists, then create it
|
||||
if(child == nullptr) {
|
||||
std::vector<Vec3d> child_centers = {
|
||||
Vec3d(-1, -1, -1), Vec3d( 1, -1, -1), Vec3d(-1, 1, -1), Vec3d( 1, 1, -1),
|
||||
Vec3d(-1, -1, 1), Vec3d( 1, -1, 1), Vec3d(-1, 1, 1), Vec3d( 1, 1, 1)
|
||||
};
|
||||
|
||||
const Vec3d &child_center = child_centers[octant_idx];
|
||||
Vec3d child_center_transformed = current->center + (child_center * (cubes_properties[depth].edge_length / 4));
|
||||
|
||||
current->children[octant_idx] = std::make_unique<Cube>(child_center_transformed);
|
||||
child = current->children[octant_idx].get();
|
||||
}
|
||||
|
||||
Octree::propagate_point(point, child, (depth - 1), cubes_properties);
|
||||
}
|
||||
|
||||
std::unique_ptr<FillAdaptive_Internal::Octree> FillSupportCubic::build_octree(
|
||||
TriangleMesh & triangle_mesh,
|
||||
coordf_t line_spacing,
|
||||
const Vec3d & cube_center,
|
||||
const Transform3d &rotation_matrix)
|
||||
{
|
||||
using namespace FillAdaptive_Internal;
|
||||
|
||||
if(line_spacing <= 0 || std::isnan(line_spacing))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Vec3d bb_size = triangle_mesh.bounding_box().size();
|
||||
// The furthest point from the center of the bottom of the mesh bounding box.
|
||||
double furthest_point = std::sqrt(((bb_size.x() * bb_size.x()) / 4.0) +
|
||||
((bb_size.y() * bb_size.y()) / 4.0) +
|
||||
(bb_size.z() * bb_size.z()));
|
||||
double max_cube_edge_length = furthest_point * 2;
|
||||
|
||||
std::vector<CubeProperties> cubes_properties;
|
||||
for (double edge_length = (line_spacing * 2); edge_length < (max_cube_edge_length * 2); edge_length *= 2)
|
||||
{
|
||||
CubeProperties props{};
|
||||
props.edge_length = edge_length;
|
||||
props.height = edge_length * sqrt(3);
|
||||
props.diagonal_length = edge_length * sqrt(2);
|
||||
props.line_z_distance = edge_length / sqrt(3);
|
||||
props.line_xy_distance = edge_length / sqrt(6);
|
||||
cubes_properties.push_back(props);
|
||||
}
|
||||
|
||||
if (triangle_mesh.its.vertices.empty())
|
||||
{
|
||||
triangle_mesh.require_shared_vertices();
|
||||
}
|
||||
|
||||
AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(
|
||||
triangle_mesh.its.vertices, triangle_mesh.its.indices);
|
||||
|
||||
auto octree = std::make_unique<Octree>(std::make_unique<Cube>(cube_center), cube_center, cubes_properties);
|
||||
|
||||
double cube_edge_length = line_spacing / 2.0;
|
||||
int max_depth = int(octree->cubes_properties.size()) - 1;
|
||||
BoundingBoxf3 mesh_bb = triangle_mesh.bounding_box();
|
||||
Vec3f vertical(0, 0, 1);
|
||||
|
||||
for (size_t facet_idx = 0; facet_idx < triangle_mesh.stl.facet_start.size(); ++facet_idx)
|
||||
{
|
||||
if(triangle_mesh.stl.facet_start[facet_idx].normal.dot(vertical) <= 0.707)
|
||||
{
|
||||
// The angle is smaller than PI/4, than infill don't to be there
|
||||
continue;
|
||||
}
|
||||
|
||||
stl_vertex v_1 = triangle_mesh.stl.facet_start[facet_idx].vertex[0];
|
||||
stl_vertex v_2 = triangle_mesh.stl.facet_start[facet_idx].vertex[1];
|
||||
stl_vertex v_3 = triangle_mesh.stl.facet_start[facet_idx].vertex[2];
|
||||
|
||||
std::vector<Vec3d> triangle_vertices =
|
||||
{Vec3d(v_1.x(), v_1.y(), v_1.z()),
|
||||
Vec3d(v_2.x(), v_2.y(), v_2.z()),
|
||||
Vec3d(v_3.x(), v_3.y(), v_3.z())};
|
||||
|
||||
BoundingBoxf3 triangle_bb(triangle_vertices);
|
||||
|
||||
Vec3d triangle_start_relative = triangle_bb.min - mesh_bb.min;
|
||||
Vec3d triangle_end_relative = triangle_bb.max - mesh_bb.min;
|
||||
|
||||
Vec3crd triangle_start_idx = Vec3crd(
|
||||
int(std::floor(triangle_start_relative.x() / cube_edge_length)),
|
||||
int(std::floor(triangle_start_relative.y() / cube_edge_length)),
|
||||
int(std::floor(triangle_start_relative.z() / cube_edge_length)));
|
||||
Vec3crd triangle_end_idx = Vec3crd(
|
||||
int(std::floor(triangle_end_relative.x() / cube_edge_length)),
|
||||
int(std::floor(triangle_end_relative.y() / cube_edge_length)),
|
||||
int(std::floor(triangle_end_relative.z() / cube_edge_length)));
|
||||
|
||||
for (int z = triangle_start_idx.z(); z <= triangle_end_idx.z(); ++z)
|
||||
{
|
||||
for (int y = triangle_start_idx.y(); y <= triangle_end_idx.y(); ++y)
|
||||
{
|
||||
for (int x = triangle_start_idx.x(); x <= triangle_end_idx.x(); ++x)
|
||||
{
|
||||
Vec3d cube_center_relative(x * cube_edge_length + (cube_edge_length / 2.0), y * cube_edge_length + (cube_edge_length / 2.0), z * cube_edge_length);
|
||||
Vec3d cube_center_absolute = cube_center_relative + mesh_bb.min;
|
||||
|
||||
double cube_center_absolute_arr[3] = {cube_center_absolute.x(), cube_center_absolute.y(), cube_center_absolute.z()};
|
||||
double distance = 0, cord_u = 0, cord_v = 0;
|
||||
|
||||
double dir[3] = {0.0, 0.0, 1.0};
|
||||
|
||||
double vert_0[3] = {triangle_vertices[0].x(),
|
||||
triangle_vertices[0].y(),
|
||||
triangle_vertices[0].z()};
|
||||
double vert_1[3] = {triangle_vertices[1].x(),
|
||||
triangle_vertices[1].y(),
|
||||
triangle_vertices[1].z()};
|
||||
double vert_2[3] = {triangle_vertices[2].x(),
|
||||
triangle_vertices[2].y(),
|
||||
triangle_vertices[2].z()};
|
||||
|
||||
if(intersect_triangle(cube_center_absolute_arr, dir, vert_0, vert_1, vert_2, &distance, &cord_u, &cord_v) && distance > 0 && distance <= cube_edge_length)
|
||||
{
|
||||
Vec3d cube_center_transformed(cube_center_absolute.x(), cube_center_absolute.y(), cube_center_absolute.z() + (cube_edge_length / 2.0));
|
||||
Octree::propagate_point(rotation_matrix * cube_center_transformed, octree->root_cube.get(), max_depth, octree->cubes_properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return octree;
|
||||
}
|
||||
|
||||
void FillSupportCubic::_fill_surface_single(const FillParams & params,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon & expolygon,
|
||||
Polylines & polylines_out)
|
||||
{
|
||||
if (this->support_fill_octree != nullptr)
|
||||
this->generate_infill(params, thickness_layers, direction, expolygon, polylines_out, this->support_fill_octree);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
139
src/libslic3r/Fill/FillAdaptive.hpp
Normal file
139
src/libslic3r/Fill/FillAdaptive.hpp
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
#ifndef slic3r_FillAdaptive_hpp_
|
||||
#define slic3r_FillAdaptive_hpp_
|
||||
|
||||
#include "../AABBTreeIndirect.hpp"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class PrintObject;
|
||||
class TriangleMesh;
|
||||
|
||||
namespace FillAdaptive_Internal
|
||||
{
|
||||
struct CubeProperties
|
||||
{
|
||||
double edge_length; // Lenght of edge of a cube
|
||||
double height; // Height of rotated cube (standing on the corner)
|
||||
double diagonal_length; // Length of diagonal of a cube a face
|
||||
double line_z_distance; // Defines maximal distance from a center of a cube on Z axis on which lines will be created
|
||||
double line_xy_distance;// Defines maximal distance from a center of a cube on X and Y axis on which lines will be created
|
||||
};
|
||||
|
||||
struct Cube
|
||||
{
|
||||
Vec3d center;
|
||||
std::unique_ptr<Cube> children[8] = {};
|
||||
Cube(const Vec3d ¢er) : center(center) {}
|
||||
};
|
||||
|
||||
struct Octree
|
||||
{
|
||||
std::unique_ptr<Cube> root_cube;
|
||||
Vec3d origin;
|
||||
std::vector<CubeProperties> cubes_properties;
|
||||
|
||||
Octree(std::unique_ptr<Cube> rootCube, const Vec3d &origin, const std::vector<CubeProperties> &cubes_properties)
|
||||
: root_cube(std::move(rootCube)), origin(origin), cubes_properties(cubes_properties) {}
|
||||
|
||||
inline static int find_octant(const Vec3d &i_cube, const Vec3d ¤t)
|
||||
{
|
||||
return (i_cube.z() > current.z()) * 4 + (i_cube.y() > current.y()) * 2 + (i_cube.x() > current.x());
|
||||
}
|
||||
|
||||
static void propagate_point(
|
||||
Vec3d point,
|
||||
FillAdaptive_Internal::Cube *current_cube,
|
||||
int depth,
|
||||
const std::vector<FillAdaptive_Internal::CubeProperties> &cubes_properties);
|
||||
};
|
||||
}; // namespace FillAdaptive_Internal
|
||||
|
||||
//
|
||||
// Some of the algorithms used by class FillAdaptive were inspired by
|
||||
// Cura Engine's class SubDivCube
|
||||
// https://github.com/Ultimaker/CuraEngine/blob/master/src/infill/SubDivCube.h
|
||||
//
|
||||
class FillAdaptive : public Fill
|
||||
{
|
||||
public:
|
||||
virtual ~FillAdaptive() {}
|
||||
|
||||
protected:
|
||||
virtual Fill* clone() const { return new FillAdaptive(*this); };
|
||||
virtual void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
|
||||
virtual bool no_sort() const { return true; }
|
||||
|
||||
void generate_infill_lines(
|
||||
FillAdaptive_Internal::Cube *cube,
|
||||
double z_position,
|
||||
const Vec3d & origin,
|
||||
const Transform3d & rotation_matrix,
|
||||
std::vector<Lines> & dir_lines_out,
|
||||
const std::vector<FillAdaptive_Internal::CubeProperties> &cubes_properties,
|
||||
int depth);
|
||||
|
||||
static void connect_lines(Lines &lines, Line new_line);
|
||||
|
||||
void generate_infill(const FillParams & params,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon & expolygon,
|
||||
Polylines & polylines_out,
|
||||
FillAdaptive_Internal::Octree *octree);
|
||||
|
||||
public:
|
||||
static std::unique_ptr<FillAdaptive_Internal::Octree> build_octree(
|
||||
TriangleMesh &triangle_mesh,
|
||||
coordf_t line_spacing,
|
||||
const Vec3d & cube_center);
|
||||
|
||||
static void expand_cube(
|
||||
FillAdaptive_Internal::Cube *cube,
|
||||
const std::vector<FillAdaptive_Internal::CubeProperties> &cubes_properties,
|
||||
const AABBTreeIndirect::Tree3f &distance_tree,
|
||||
const TriangleMesh & triangle_mesh,
|
||||
int depth);
|
||||
};
|
||||
|
||||
class FillSupportCubic : public FillAdaptive
|
||||
{
|
||||
public:
|
||||
virtual ~FillSupportCubic() = default;
|
||||
|
||||
protected:
|
||||
virtual Fill* clone() const { return new FillSupportCubic(*this); };
|
||||
|
||||
virtual bool no_sort() const { return true; }
|
||||
|
||||
virtual void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
|
||||
public:
|
||||
static std::unique_ptr<FillAdaptive_Internal::Octree> build_octree(
|
||||
TriangleMesh & triangle_mesh,
|
||||
coordf_t line_spacing,
|
||||
const Vec3d & cube_center,
|
||||
const Transform3d &rotation_matrix);
|
||||
};
|
||||
|
||||
// Calculate line spacing for
|
||||
// 1) adaptive cubic infill
|
||||
// 2) adaptive internal support cubic infill
|
||||
// Returns zero for a particular infill type if no such infill is to be generated.
|
||||
std::pair<double, double> adaptive_fill_line_spacing(const PrintObject &print_object);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillAdaptive_hpp_
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
#include "FillRectilinear.hpp"
|
||||
#include "FillRectilinear2.hpp"
|
||||
#include "FillRectilinear3.hpp"
|
||||
#include "FillAdaptive.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
|
@ -37,7 +38,9 @@ Fill* Fill::new_from_type(const InfillPattern type)
|
|||
case ipArchimedeanChords: return new FillArchimedeanChords();
|
||||
case ipHilbertCurve: return new FillHilbertCurve();
|
||||
case ipOctagramSpiral: return new FillOctagramSpiral();
|
||||
default: throw std::invalid_argument("unknown type");
|
||||
case ipAdaptiveCubic: return new FillAdaptive();
|
||||
case ipSupportCubic: return new FillSupportCubic();
|
||||
default: throw Slic3r::InvalidArgument("unknown type");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "../libslic3r.h"
|
||||
#include "../BoundingBox.hpp"
|
||||
#include "../Exception.hpp"
|
||||
#include "../Utils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
@ -19,9 +20,14 @@ class ExPolygon;
|
|||
class Surface;
|
||||
enum InfillPattern : int;
|
||||
|
||||
class InfillFailedException : public std::runtime_error {
|
||||
namespace FillAdaptive_Internal {
|
||||
struct Octree;
|
||||
};
|
||||
|
||||
// Infill shall never fail, therefore the error is classified as RuntimeError, not SlicingError.
|
||||
class InfillFailedException : public Slic3r::RuntimeError {
|
||||
public:
|
||||
InfillFailedException() : std::runtime_error("Infill failed") {}
|
||||
InfillFailedException() : Slic3r::RuntimeError("Infill failed") {}
|
||||
};
|
||||
|
||||
struct FillParams
|
||||
|
|
@ -69,6 +75,11 @@ public:
|
|||
// In scaled coordinates. Bounding box of the 2D projection of the object.
|
||||
BoundingBox bounding_box;
|
||||
|
||||
// Octree builds on mesh for usage in the adaptive cubic infill
|
||||
FillAdaptive_Internal::Octree* adapt_fill_octree = nullptr;
|
||||
// Octree builds on mesh for usage in the support cubic infill
|
||||
FillAdaptive_Internal::Octree* support_fill_octree = nullptr;
|
||||
|
||||
public:
|
||||
virtual ~Fill() {}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ static inline FlowRole opt_key_to_flow_role(const std::string &opt_key)
|
|||
else if (opt_key == "support_material_extrusion_width")
|
||||
return frSupportMaterial;
|
||||
else
|
||||
throw std::runtime_error("opt_key_to_flow_role: invalid argument");
|
||||
throw Slic3r::RuntimeError("opt_key_to_flow_role: invalid argument");
|
||||
};
|
||||
|
||||
static inline void throw_on_missing_variable(const std::string &opt_key, const char *dependent_opt_key)
|
||||
|
|
@ -126,7 +126,7 @@ Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent
|
|||
{
|
||||
// we need layer height unless it's a bridge
|
||||
if (height <= 0 && bridge_flow_ratio == 0)
|
||||
throw std::invalid_argument("Invalid flow height supplied to new_from_config_width()");
|
||||
throw Slic3r::InvalidArgument("Invalid flow height supplied to new_from_config_width()");
|
||||
|
||||
float w;
|
||||
if (bridge_flow_ratio > 0) {
|
||||
|
|
@ -151,7 +151,7 @@ Flow Flow::new_from_spacing(float spacing, float nozzle_diameter, float height,
|
|||
{
|
||||
// we need layer height unless it's a bridge
|
||||
if (height <= 0 && !bridge)
|
||||
throw std::invalid_argument("Invalid flow height supplied to new_from_spacing()");
|
||||
throw Slic3r::InvalidArgument("Invalid flow height supplied to new_from_spacing()");
|
||||
// Calculate width from spacing.
|
||||
// For normal extrusons, extrusion width is wider than the spacing due to the rounding and squishing of the extrusions.
|
||||
// For bridge extrusions, the extrusions are placed with a tiny BRIDGE_EXTRA_SPACING gaps between the threads.
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "libslic3r.h"
|
||||
#include "Config.hpp"
|
||||
#include "Exception.hpp"
|
||||
#include "ExtrusionEntity.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
@ -27,11 +28,11 @@ enum FlowRole {
|
|||
frSupportMaterialInterface,
|
||||
};
|
||||
|
||||
class FlowError : public std::invalid_argument
|
||||
class FlowError : public Slic3r::InvalidArgument
|
||||
{
|
||||
public:
|
||||
FlowError(const std::string& what_arg) : invalid_argument(what_arg) {}
|
||||
FlowError(const char* what_arg) : invalid_argument(what_arg) {}
|
||||
FlowError(const std::string& what_arg) : Slic3r::InvalidArgument(what_arg) {}
|
||||
FlowError(const char* what_arg) : Slic3r::InvalidArgument(what_arg) {}
|
||||
};
|
||||
|
||||
class FlowErrorNegativeSpacing : public FlowError
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
#include "../libslic3r.h"
|
||||
#include "../Exception.hpp"
|
||||
#include "../Model.hpp"
|
||||
#include "../Utils.hpp"
|
||||
#include "../GCode.hpp"
|
||||
#include "../Geometry.hpp"
|
||||
#include "../GCode/ThumbnailData.hpp"
|
||||
#include "../Time.hpp"
|
||||
|
||||
#include "../I18N.hpp"
|
||||
|
||||
|
|
@ -85,6 +87,8 @@ const char* OBJECTID_ATTR = "objectid";
|
|||
const char* TRANSFORM_ATTR = "transform";
|
||||
const char* PRINTABLE_ATTR = "printable";
|
||||
const char* INSTANCESCOUNT_ATTR = "instances_count";
|
||||
const char* CUSTOM_SUPPORTS_ATTR = "slic3rpe:custom_supports";
|
||||
const char* CUSTOM_SEAM_ATTR = "slic3rpe:custom_seam";
|
||||
|
||||
const char* KEY_ATTR = "key";
|
||||
const char* VALUE_ATTR = "value";
|
||||
|
|
@ -120,11 +124,11 @@ const char* INVALID_OBJECT_TYPES[] =
|
|||
"other"
|
||||
};
|
||||
|
||||
class version_error : public std::runtime_error
|
||||
class version_error : public Slic3r::FileIOError
|
||||
{
|
||||
public:
|
||||
version_error(const std::string& what_arg) : std::runtime_error(what_arg) {}
|
||||
version_error(const char* what_arg) : std::runtime_error(what_arg) {}
|
||||
version_error(const std::string& what_arg) : Slic3r::FileIOError(what_arg) {}
|
||||
version_error(const char* what_arg) : Slic3r::FileIOError(what_arg) {}
|
||||
};
|
||||
|
||||
const char* get_attribute_value_charptr(const char** attributes, unsigned int attributes_size, const char* attribute_key)
|
||||
|
|
@ -282,6 +286,8 @@ namespace Slic3r {
|
|||
{
|
||||
std::vector<float> vertices;
|
||||
std::vector<unsigned int> triangles;
|
||||
std::vector<std::string> custom_supports;
|
||||
std::vector<std::string> custom_seam;
|
||||
|
||||
bool empty()
|
||||
{
|
||||
|
|
@ -292,6 +298,8 @@ namespace Slic3r {
|
|||
{
|
||||
vertices.clear();
|
||||
triangles.clear();
|
||||
custom_supports.clear();
|
||||
custom_seam.clear();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -600,7 +608,7 @@ namespace Slic3r {
|
|||
{
|
||||
// ensure the zip archive is closed and rethrow the exception
|
||||
close_zip_reader(&archive);
|
||||
throw std::runtime_error(e.what());
|
||||
throw Slic3r::FileIOError(e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -773,7 +781,7 @@ namespace Slic3r {
|
|||
{
|
||||
char error_buf[1024];
|
||||
::sprintf(error_buf, "Error (%s) while parsing '%s' at line %d", XML_ErrorString(XML_GetErrorCode(data->parser)), data->stat.m_filename, (int)XML_GetCurrentLineNumber(data->parser));
|
||||
throw std::runtime_error(error_buf);
|
||||
throw Slic3r::FileIOError(error_buf);
|
||||
}
|
||||
|
||||
return n;
|
||||
|
|
@ -782,7 +790,7 @@ namespace Slic3r {
|
|||
catch (const version_error& e)
|
||||
{
|
||||
// rethrow the exception
|
||||
throw std::runtime_error(e.what());
|
||||
throw Slic3r::FileIOError(e.what());
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
|
|
@ -1110,6 +1118,15 @@ namespace Slic3r {
|
|||
float(std::atof(object_data_points[i+6].c_str())),
|
||||
float(std::atof(object_data_points[i+7].c_str())));
|
||||
}
|
||||
|
||||
// The holes are saved elevated above the mesh and deeper (bad idea indeed).
|
||||
// This is retained for compatibility.
|
||||
// Place the hole to the mesh and make it shallower to compensate.
|
||||
// The offset is 1 mm above the mesh.
|
||||
for (sla::DrainHole& hole : sla_drain_holes) {
|
||||
hole.pos += hole.normal.normalized();
|
||||
hole.height -= 1.f;
|
||||
}
|
||||
|
||||
if (!sla_drain_holes.empty())
|
||||
m_sla_drain_holes.insert(IdToSlaDrainHolesMap::value_type(object_id, sla_drain_holes));
|
||||
|
|
@ -1538,6 +1555,9 @@ namespace Slic3r {
|
|||
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V1_ATTR));
|
||||
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V2_ATTR));
|
||||
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V3_ATTR));
|
||||
|
||||
m_curr_object.geometry.custom_supports.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SUPPORTS_ATTR));
|
||||
m_curr_object.geometry.custom_seam.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SEAM_ATTR));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1871,6 +1891,18 @@ namespace Slic3r {
|
|||
volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object);
|
||||
volume->calculate_convex_hull();
|
||||
|
||||
// recreate custom supports and seam from previously loaded attribute
|
||||
for (unsigned i=0; i<triangles_count; ++i) {
|
||||
size_t index = src_start_id/3 + i;
|
||||
assert(index < geometry.custom_supports.size());
|
||||
assert(index < geometry.custom_seam.size());
|
||||
if (! geometry.custom_supports[index].empty())
|
||||
volume->m_supported_facets.set_triangle_from_string(i, geometry.custom_supports[index]);
|
||||
if (! geometry.custom_seam[index].empty())
|
||||
volume->m_seam_facets.set_triangle_from_string(i, geometry.custom_seam[index]);
|
||||
}
|
||||
|
||||
|
||||
// apply the remaining volume's metadata
|
||||
for (const Metadata& metadata : volume_data.metadata)
|
||||
{
|
||||
|
|
@ -1991,7 +2023,7 @@ namespace Slic3r {
|
|||
bool _add_content_types_file_to_archive(mz_zip_archive& archive);
|
||||
bool _add_thumbnail_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data);
|
||||
bool _add_relationships_file_to_archive(mz_zip_archive& archive);
|
||||
bool _add_model_file_to_archive(mz_zip_archive& archive, const Model& model, IdToObjectDataMap &objects_data);
|
||||
bool _add_model_file_to_archive(const std::string& filename, mz_zip_archive& archive, const Model& model, IdToObjectDataMap& objects_data);
|
||||
bool _add_object_to_model_stream(std::stringstream& stream, unsigned int& object_id, ModelObject& object, BuildItemsList& build_items, VolumeToOffsetsMap& volumes_offsets);
|
||||
bool _add_mesh_to_object_stream(std::stringstream& stream, ModelObject& object, VolumeToOffsetsMap& volumes_offsets);
|
||||
bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items);
|
||||
|
|
@ -2054,7 +2086,7 @@ namespace Slic3r {
|
|||
// Adds model file ("3D/3dmodel.model").
|
||||
// This is the one and only file that contains all the geometry (vertices and triangles) of all ModelVolumes.
|
||||
IdToObjectDataMap objects_data;
|
||||
if (!_add_model_file_to_archive(archive, model, objects_data))
|
||||
if (!_add_model_file_to_archive(filename, archive, model, objects_data))
|
||||
{
|
||||
close_zip_writer(&archive);
|
||||
boost::filesystem::remove(filename);
|
||||
|
|
@ -2203,7 +2235,7 @@ namespace Slic3r {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool _3MF_Exporter::_add_model_file_to_archive(mz_zip_archive& archive, const Model& model, IdToObjectDataMap &objects_data)
|
||||
bool _3MF_Exporter::_add_model_file_to_archive(const std::string& filename, mz_zip_archive& archive, const Model& model, IdToObjectDataMap& objects_data)
|
||||
{
|
||||
std::stringstream stream;
|
||||
// https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10
|
||||
|
|
@ -2214,6 +2246,19 @@ namespace Slic3r {
|
|||
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:slic3rpe=\"http://schemas.slic3r.org/3mf/2017/06\">\n";
|
||||
stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_3MF_VERSION << "\">" << VERSION_3MF << "</" << METADATA_TAG << ">\n";
|
||||
std::string name = boost::filesystem::path(filename).stem().string();
|
||||
stream << " <" << METADATA_TAG << " name=\"Title\">" << name << "</" << METADATA_TAG << ">\n";
|
||||
stream << " <" << METADATA_TAG << " name=\"Designer\">" << "</" << METADATA_TAG << ">\n";
|
||||
stream << " <" << METADATA_TAG << " name=\"Description\">" << name << "</" << METADATA_TAG << ">\n";
|
||||
stream << " <" << METADATA_TAG << " name=\"Copyright\">" << "</" << METADATA_TAG << ">\n";
|
||||
stream << " <" << METADATA_TAG << " name=\"LicenseTerms\">" << "</" << METADATA_TAG << ">\n";
|
||||
stream << " <" << METADATA_TAG << " name=\"Rating\">" << "</" << METADATA_TAG << ">\n";
|
||||
std::string date = Slic3r::Utils::utc_timestamp(Slic3r::Utils::get_current_time_utc());
|
||||
// keep only the date part of the string
|
||||
date = date.substr(0, 10);
|
||||
stream << " <" << METADATA_TAG << " name=\"CreationDate\">" << date << "</" << METADATA_TAG << ">\n";
|
||||
stream << " <" << METADATA_TAG << " name=\"ModificationDate\">" << date << "</" << METADATA_TAG << ">\n";
|
||||
stream << " <" << METADATA_TAG << " name=\"Application\">" << SLIC3R_APP_KEY << "-" << SLIC3R_VERSION << "</" << METADATA_TAG << ">\n";
|
||||
stream << " <" << RESOURCES_TAG << ">\n";
|
||||
|
||||
// Instance transformations, indexed by the 3MF object ID (which is a linear serialization of all instances of all ModelObjects).
|
||||
|
|
@ -2316,9 +2361,9 @@ namespace Slic3r {
|
|||
continue;
|
||||
|
||||
if (!volume->mesh().repaired)
|
||||
throw std::runtime_error("store_3mf() requires repair()");
|
||||
throw Slic3r::FileIOError("store_3mf() requires repair()");
|
||||
if (!volume->mesh().has_shared_vertices())
|
||||
throw std::runtime_error("store_3mf() requires shared vertices");
|
||||
throw Slic3r::FileIOError("store_3mf() requires shared vertices");
|
||||
|
||||
volumes_offsets.insert(VolumeToOffsetsMap::value_type(volume, Offsets(vertices_count))).first;
|
||||
|
||||
|
|
@ -2369,6 +2414,15 @@ namespace Slic3r {
|
|||
{
|
||||
stream << "v" << j + 1 << "=\"" << its.indices[i][j] + volume_it->second.first_vertex_id << "\" ";
|
||||
}
|
||||
|
||||
std::string custom_supports_data_string = volume->m_supported_facets.get_triangle_as_string(i);
|
||||
if (! custom_supports_data_string.empty())
|
||||
stream << CUSTOM_SUPPORTS_ATTR << "=\"" << custom_supports_data_string << "\" ";
|
||||
|
||||
std::string custom_seam_data_string = volume->m_seam_facets.get_triangle_as_string(i);
|
||||
if (! custom_seam_data_string.empty())
|
||||
stream << CUSTOM_SEAM_ATTR << "=\"" << custom_seam_data_string << "\" ";
|
||||
|
||||
stream << "/>\n";
|
||||
}
|
||||
}
|
||||
|
|
@ -2559,7 +2613,18 @@ namespace Slic3r {
|
|||
for (const ModelObject* object : model.objects)
|
||||
{
|
||||
++count;
|
||||
auto& drain_holes = object->sla_drain_holes;
|
||||
sla::DrainHoles drain_holes = object->sla_drain_holes;
|
||||
|
||||
// The holes were placed 1mm above the mesh in the first implementation.
|
||||
// This was a bad idea and the reference point was changed in 2.3 so
|
||||
// to be on the mesh exactly. The elevated position is still saved
|
||||
// in 3MFs for compatibility reasons.
|
||||
for (sla::DrainHole& hole : drain_holes) {
|
||||
hole.pos -= hole.normal.normalized();
|
||||
hole.height += 1.f;
|
||||
}
|
||||
|
||||
|
||||
if (!drain_holes.empty())
|
||||
{
|
||||
out += string_printf(fmt, count);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,6 @@ namespace Slic3r {
|
|||
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
|
||||
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr);
|
||||
|
||||
}; // namespace Slic3r
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Format_3mf_hpp_ */
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <boost/nowide/cstdio.hpp>
|
||||
|
||||
#include "../libslic3r.h"
|
||||
#include "../Exception.hpp"
|
||||
#include "../Model.hpp"
|
||||
#include "../GCode.hpp"
|
||||
#include "../PrintConfig.hpp"
|
||||
|
|
@ -923,7 +924,7 @@ bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_fi
|
|||
{
|
||||
char error_buf[1024];
|
||||
::sprintf(error_buf, "Error (%s) while parsing '%s' at line %d", XML_ErrorString(XML_GetErrorCode(data->parser)), data->stat.m_filename, (int)XML_GetCurrentLineNumber(data->parser));
|
||||
throw std::runtime_error(error_buf);
|
||||
throw Slic3r::FileIOError(error_buf);
|
||||
}
|
||||
|
||||
return n;
|
||||
|
|
@ -948,9 +949,9 @@ bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_fi
|
|||
if (check_version && (ctx.m_version > VERSION_AMF_COMPATIBLE))
|
||||
{
|
||||
// std::string msg = _(L("The selected amf file has been saved with a newer version of " + std::string(SLIC3R_APP_NAME) + " and is not compatible."));
|
||||
// throw std::runtime_error(msg.c_str());
|
||||
// throw Slic3r::FileIOError(msg.c_str());
|
||||
const std::string msg = (boost::format(_(L("The selected amf file has been saved with a newer version of %1% and is not compatible."))) % std::string(SLIC3R_APP_NAME)).str();
|
||||
throw std::runtime_error(msg);
|
||||
throw Slic3r::FileIOError(msg);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -994,7 +995,7 @@ bool load_amf_archive(const char* path, DynamicPrintConfig* config, Model* model
|
|||
{
|
||||
// ensure the zip archive is closed and rethrow the exception
|
||||
close_zip_reader(&archive);
|
||||
throw std::runtime_error(e.what());
|
||||
throw Slic3r::FileIOError(e.what());
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
@ -1147,9 +1148,9 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config,
|
|||
for (ModelVolume *volume : object->volumes) {
|
||||
vertices_offsets.push_back(num_vertices);
|
||||
if (! volume->mesh().repaired)
|
||||
throw std::runtime_error("store_amf() requires repair()");
|
||||
throw Slic3r::FileIOError("store_amf() requires repair()");
|
||||
if (! volume->mesh().has_shared_vertices())
|
||||
throw std::runtime_error("store_amf() requires shared vertices");
|
||||
throw Slic3r::FileIOError("store_amf() requires shared vertices");
|
||||
const indexed_triangle_set &its = volume->mesh().its;
|
||||
const Transform3d& matrix = volume->get_matrix();
|
||||
for (size_t i = 0; i < its.vertices.size(); ++i) {
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ static void extract_model_from_archive(
|
|||
}
|
||||
}
|
||||
if (! trafo_set)
|
||||
throw std::runtime_error(std::string("Archive ") + path + " does not contain a valid entry in scene.xml for " + name);
|
||||
throw Slic3r::FileIOError(std::string("Archive ") + path + " does not contain a valid entry in scene.xml for " + name);
|
||||
|
||||
// Extract the STL.
|
||||
StlHeader header;
|
||||
|
|
@ -266,7 +266,7 @@ static void extract_model_from_archive(
|
|||
}
|
||||
|
||||
if (! mesh_valid)
|
||||
throw std::runtime_error(std::string("Archive ") + path + " does not contain a valid mesh for " + name);
|
||||
throw Slic3r::FileIOError(std::string("Archive ") + path + " does not contain a valid mesh for " + name);
|
||||
|
||||
// Add this mesh to the model.
|
||||
ModelVolume *volume = nullptr;
|
||||
|
|
@ -303,7 +303,7 @@ bool load_prus(const char *path, Model *model)
|
|||
mz_bool res = MZ_FALSE;
|
||||
try {
|
||||
if (!open_zip_reader(&archive, path))
|
||||
throw std::runtime_error(std::string("Unable to init zip reader for ") + path);
|
||||
throw Slic3r::FileIOError(std::string("Unable to init zip reader for ") + path);
|
||||
std::vector<char> scene_xml_data;
|
||||
// For grouping multiple STLs into a single ModelObject for multi-material prints.
|
||||
std::map<int, ModelObject*> group_to_model_object;
|
||||
|
|
@ -316,10 +316,10 @@ bool load_prus(const char *path, Model *model)
|
|||
buffer.assign((size_t)stat.m_uncomp_size, 0);
|
||||
res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (char*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
|
||||
if (res == MZ_FALSE)
|
||||
std::runtime_error(std::string("Error while extracting a file from ") + path);
|
||||
throw Slic3r::FileIOError(std::string("Error while extracting a file from ") + path);
|
||||
if (strcmp(stat.m_filename, "scene.xml") == 0) {
|
||||
if (! scene_xml_data.empty())
|
||||
throw std::runtime_error(std::string("Multiple scene.xml were found in the archive.") + path);
|
||||
throw Slic3r::FileIOError(std::string("Multiple scene.xml were found in the archive.") + path);
|
||||
scene_xml_data = std::move(buffer);
|
||||
} else if (boost::iends_with(stat.m_filename, ".stl")) {
|
||||
// May throw std::exception
|
||||
|
|
|
|||
|
|
@ -8,8 +8,317 @@
|
|||
#include "libslic3r/Zipper.hpp"
|
||||
#include "libslic3r/SLAPrint.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "libslic3r/Exception.hpp"
|
||||
#include "libslic3r/SlicesToTriangleMesh.hpp"
|
||||
#include "libslic3r/MarchingSquares.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/MTUtils.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/SLA/RasterBase.hpp"
|
||||
#include "libslic3r/miniz_extension.hpp"
|
||||
#include "libslic3r/PNGRead.hpp"
|
||||
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
namespace marchsq {
|
||||
|
||||
template<> struct _RasterTraits<Slic3r::png::ImageGreyscale> {
|
||||
using Rst = Slic3r::png::ImageGreyscale;
|
||||
|
||||
// The type of pixel cell in the raster
|
||||
using ValueType = uint8_t;
|
||||
|
||||
// Value at a given position
|
||||
static uint8_t get(const Rst &rst, size_t row, size_t col)
|
||||
{
|
||||
return rst.get(row, col);
|
||||
}
|
||||
|
||||
// Number of rows and cols of the raster
|
||||
static size_t rows(const Rst &rst) { return rst.rows; }
|
||||
static size_t cols(const Rst &rst) { return rst.cols; }
|
||||
};
|
||||
|
||||
} // namespace marchsq
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace {
|
||||
|
||||
struct PNGBuffer { std::vector<uint8_t> buf; std::string fname; };
|
||||
struct ArchiveData {
|
||||
boost::property_tree::ptree profile, config;
|
||||
std::vector<PNGBuffer> images;
|
||||
};
|
||||
|
||||
static const constexpr char *CONFIG_FNAME = "config.ini";
|
||||
static const constexpr char *PROFILE_FNAME = "prusaslicer.ini";
|
||||
|
||||
boost::property_tree::ptree read_ini(const mz_zip_archive_file_stat &entry,
|
||||
MZ_Archive & zip)
|
||||
{
|
||||
std::string buf(size_t(entry.m_uncomp_size), '\0');
|
||||
|
||||
if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename,
|
||||
buf.data(), buf.size(), 0))
|
||||
throw Slic3r::FileIOError(zip.get_errorstr());
|
||||
|
||||
boost::property_tree::ptree tree;
|
||||
std::stringstream ss(buf);
|
||||
boost::property_tree::read_ini(ss, tree);
|
||||
return tree;
|
||||
}
|
||||
|
||||
PNGBuffer read_png(const mz_zip_archive_file_stat &entry,
|
||||
MZ_Archive & zip,
|
||||
const std::string & name)
|
||||
{
|
||||
std::vector<uint8_t> buf(entry.m_uncomp_size);
|
||||
|
||||
if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename,
|
||||
buf.data(), buf.size(), 0))
|
||||
throw Slic3r::FileIOError(zip.get_errorstr());
|
||||
|
||||
return {std::move(buf), (name.empty() ? entry.m_filename : name)};
|
||||
}
|
||||
|
||||
ArchiveData extract_sla_archive(const std::string &zipfname,
|
||||
const std::string &exclude)
|
||||
{
|
||||
ArchiveData arch;
|
||||
|
||||
// Little RAII
|
||||
struct Arch: public MZ_Archive {
|
||||
Arch(const std::string &fname) {
|
||||
if (!open_zip_reader(&arch, fname))
|
||||
throw Slic3r::FileIOError(get_errorstr());
|
||||
}
|
||||
|
||||
~Arch() { close_zip_reader(&arch); }
|
||||
} zip (zipfname);
|
||||
|
||||
mz_uint num_entries = mz_zip_reader_get_num_files(&zip.arch);
|
||||
|
||||
for (mz_uint i = 0; i < num_entries; ++i)
|
||||
{
|
||||
mz_zip_archive_file_stat entry;
|
||||
|
||||
if (mz_zip_reader_file_stat(&zip.arch, i, &entry))
|
||||
{
|
||||
std::string name = entry.m_filename;
|
||||
boost::algorithm::to_lower(name);
|
||||
|
||||
if (boost::algorithm::contains(name, exclude)) continue;
|
||||
|
||||
if (name == CONFIG_FNAME) arch.config = read_ini(entry, zip);
|
||||
if (name == PROFILE_FNAME) arch.profile = read_ini(entry, zip);
|
||||
|
||||
if (boost::filesystem::path(name).extension().string() == ".png") {
|
||||
auto it = std::lower_bound(
|
||||
arch.images.begin(), arch.images.end(), PNGBuffer{{}, name},
|
||||
[](const PNGBuffer &r1, const PNGBuffer &r2) {
|
||||
return std::less<std::string>()(r1.fname, r2.fname);
|
||||
});
|
||||
|
||||
arch.images.insert(it, read_png(entry, zip, name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return arch;
|
||||
}
|
||||
|
||||
ExPolygons rings_to_expolygons(const std::vector<marchsq::Ring> &rings,
|
||||
double px_w, double px_h)
|
||||
{
|
||||
ExPolygons polys; polys.reserve(rings.size());
|
||||
|
||||
for (const marchsq::Ring &ring : rings) {
|
||||
Polygon poly; Points &pts = poly.points;
|
||||
pts.reserve(ring.size());
|
||||
|
||||
for (const marchsq::Coord &crd : ring)
|
||||
pts.emplace_back(scaled(crd.c * px_w), scaled(crd.r * px_h));
|
||||
|
||||
polys.emplace_back(poly);
|
||||
}
|
||||
|
||||
// reverse the raster transformations
|
||||
return union_ex(polys);
|
||||
}
|
||||
|
||||
template<class Fn> void foreach_vertex(ExPolygon &poly, Fn &&fn)
|
||||
{
|
||||
for (auto &p : poly.contour.points) fn(p);
|
||||
for (auto &h : poly.holes)
|
||||
for (auto &p : h.points) fn(p);
|
||||
}
|
||||
|
||||
void invert_raster_trafo(ExPolygons & expolys,
|
||||
const sla::RasterBase::Trafo &trafo,
|
||||
coord_t width,
|
||||
coord_t height)
|
||||
{
|
||||
if (trafo.flipXY) std::swap(height, width);
|
||||
|
||||
for (auto &expoly : expolys) {
|
||||
if (trafo.mirror_y)
|
||||
foreach_vertex(expoly, [height](Point &p) {p.y() = height - p.y(); });
|
||||
|
||||
if (trafo.mirror_x)
|
||||
foreach_vertex(expoly, [width](Point &p) {p.x() = width - p.x(); });
|
||||
|
||||
expoly.translate(-trafo.center_x, -trafo.center_y);
|
||||
|
||||
if (trafo.flipXY)
|
||||
foreach_vertex(expoly, [](Point &p) { std::swap(p.x(), p.y()); });
|
||||
|
||||
if ((trafo.mirror_x + trafo.mirror_y + trafo.flipXY) % 2) {
|
||||
expoly.contour.reverse();
|
||||
for (auto &h : expoly.holes) h.reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct RasterParams {
|
||||
sla::RasterBase::Trafo trafo; // Raster transformations
|
||||
coord_t width, height; // scaled raster dimensions (not resolution)
|
||||
double px_h, px_w; // pixel dimesions
|
||||
marchsq::Coord win; // marching squares window size
|
||||
};
|
||||
|
||||
RasterParams get_raster_params(const DynamicPrintConfig &cfg)
|
||||
{
|
||||
auto *opt_disp_cols = cfg.option<ConfigOptionInt>("display_pixels_x");
|
||||
auto *opt_disp_rows = cfg.option<ConfigOptionInt>("display_pixels_y");
|
||||
auto *opt_disp_w = cfg.option<ConfigOptionFloat>("display_width");
|
||||
auto *opt_disp_h = cfg.option<ConfigOptionFloat>("display_height");
|
||||
auto *opt_mirror_x = cfg.option<ConfigOptionBool>("display_mirror_x");
|
||||
auto *opt_mirror_y = cfg.option<ConfigOptionBool>("display_mirror_y");
|
||||
auto *opt_orient = cfg.option<ConfigOptionEnum<SLADisplayOrientation>>("display_orientation");
|
||||
|
||||
if (!opt_disp_cols || !opt_disp_rows || !opt_disp_w || !opt_disp_h ||
|
||||
!opt_mirror_x || !opt_mirror_y || !opt_orient)
|
||||
throw Slic3r::FileIOError("Invalid SL1 file");
|
||||
|
||||
RasterParams rstp;
|
||||
|
||||
rstp.px_w = opt_disp_w->value / (opt_disp_cols->value - 1);
|
||||
rstp.px_h = opt_disp_h->value / (opt_disp_rows->value - 1);
|
||||
|
||||
rstp.trafo = sla::RasterBase::Trafo{opt_orient->value == sladoLandscape ?
|
||||
sla::RasterBase::roLandscape :
|
||||
sla::RasterBase::roPortrait,
|
||||
{opt_mirror_x->value, opt_mirror_y->value}};
|
||||
|
||||
rstp.height = scaled(opt_disp_h->value);
|
||||
rstp.width = scaled(opt_disp_w->value);
|
||||
|
||||
return rstp;
|
||||
}
|
||||
|
||||
struct SliceParams { double layerh = 0., initial_layerh = 0.; };
|
||||
|
||||
SliceParams get_slice_params(const DynamicPrintConfig &cfg)
|
||||
{
|
||||
auto *opt_layerh = cfg.option<ConfigOptionFloat>("layer_height");
|
||||
auto *opt_init_layerh = cfg.option<ConfigOptionFloat>("initial_layer_height");
|
||||
|
||||
if (!opt_layerh || !opt_init_layerh)
|
||||
throw Slic3r::FileIOError("Invalid SL1 file");
|
||||
|
||||
return SliceParams{opt_layerh->getFloat(), opt_init_layerh->getFloat()};
|
||||
}
|
||||
|
||||
std::vector<ExPolygons> extract_slices_from_sla_archive(
|
||||
ArchiveData & arch,
|
||||
const RasterParams & rstp,
|
||||
std::function<bool(int)> progr)
|
||||
{
|
||||
auto jobdir = arch.config.get<std::string>("jobDir");
|
||||
for (auto &c : jobdir) c = std::tolower(c);
|
||||
|
||||
std::vector<ExPolygons> slices(arch.images.size());
|
||||
|
||||
struct Status
|
||||
{
|
||||
double incr, val, prev;
|
||||
bool stop = false;
|
||||
tbb::spin_mutex mutex;
|
||||
} st {100. / slices.size(), 0., 0.};
|
||||
|
||||
tbb::parallel_for(size_t(0), arch.images.size(),
|
||||
[&arch, &slices, &st, &rstp, progr](size_t i) {
|
||||
// Status indication guarded with the spinlock
|
||||
{
|
||||
std::lock_guard<tbb::spin_mutex> lck(st.mutex);
|
||||
if (st.stop) return;
|
||||
|
||||
st.val += st.incr;
|
||||
double curr = std::round(st.val);
|
||||
if (curr > st.prev) {
|
||||
st.prev = curr;
|
||||
st.stop = !progr(int(curr));
|
||||
}
|
||||
}
|
||||
|
||||
png::ImageGreyscale img;
|
||||
png::ReadBuf rb{arch.images[i].buf.data(), arch.images[i].buf.size()};
|
||||
if (!png::decode_png(rb, img)) return;
|
||||
|
||||
auto rings = marchsq::execute(img, 128, rstp.win);
|
||||
ExPolygons expolys = rings_to_expolygons(rings, rstp.px_w, rstp.px_h);
|
||||
|
||||
// Invert the raster transformations indicated in
|
||||
// the profile metadata
|
||||
invert_raster_trafo(expolys, rstp.trafo, rstp.width, rstp.height);
|
||||
|
||||
slices[i] = std::move(expolys);
|
||||
});
|
||||
|
||||
if (st.stop) slices = {};
|
||||
|
||||
return slices;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out)
|
||||
{
|
||||
ArchiveData arch = extract_sla_archive(zipfname, "png");
|
||||
out.load(arch.profile);
|
||||
}
|
||||
|
||||
void import_sla_archive(
|
||||
const std::string & zipfname,
|
||||
Vec2i windowsize,
|
||||
TriangleMesh & out,
|
||||
DynamicPrintConfig & profile,
|
||||
std::function<bool(int)> progr)
|
||||
{
|
||||
// Ensure minimum window size for marching squares
|
||||
windowsize.x() = std::max(2, windowsize.x());
|
||||
windowsize.y() = std::max(2, windowsize.y());
|
||||
|
||||
ArchiveData arch = extract_sla_archive(zipfname, "thumbnail");
|
||||
profile.load(arch.profile);
|
||||
|
||||
RasterParams rstp = get_raster_params(profile);
|
||||
rstp.win = {windowsize.y(), windowsize.x()};
|
||||
|
||||
SliceParams slicp = get_slice_params(profile);
|
||||
|
||||
std::vector<ExPolygons> slices =
|
||||
extract_slices_from_sla_archive(arch, rstp, progr);
|
||||
|
||||
if (!slices.empty())
|
||||
out = slices_to_triangle_mesh(slices, 0, slicp.layerh, slicp.initial_layerh);
|
||||
}
|
||||
|
||||
using ConfMap = std::map<std::string, std::string>;
|
||||
|
||||
namespace {
|
||||
|
|
@ -126,9 +435,9 @@ uqptr<sla::RasterBase> SL1Archive::create_raster() const
|
|||
return sla::create_raster_grayscale_aa(res, pxdim, gamma, tr);
|
||||
}
|
||||
|
||||
sla::EncodedRaster SL1Archive::encode_raster(const sla::RasterBase &rst) const
|
||||
sla::RasterEncoder SL1Archive::get_encoder() const
|
||||
{
|
||||
return rst.encode(sla::PNGRasterEncoder());
|
||||
return sla::PNGRasterEncoder{};
|
||||
}
|
||||
|
||||
void SL1Archive::export_print(Zipper& zipper,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class SL1Archive: public SLAPrinter {
|
|||
|
||||
protected:
|
||||
uqptr<sla::RasterBase> create_raster() const override;
|
||||
sla::EncodedRaster encode_raster(const sla::RasterBase &rst) const override;
|
||||
sla::RasterEncoder get_encoder() const override;
|
||||
|
||||
public:
|
||||
|
||||
|
|
@ -38,6 +38,24 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out);
|
||||
|
||||
void import_sla_archive(
|
||||
const std::string & zipfname,
|
||||
Vec2i windowsize,
|
||||
TriangleMesh & out,
|
||||
DynamicPrintConfig & profile,
|
||||
std::function<bool(int)> progr = [](int) { return true; });
|
||||
|
||||
inline void import_sla_archive(
|
||||
const std::string & zipfname,
|
||||
Vec2i windowsize,
|
||||
TriangleMesh & out,
|
||||
std::function<bool(int)> progr = [](int) { return true; })
|
||||
{
|
||||
DynamicPrintConfig profile;
|
||||
import_sla_archive(zipfname, windowsize, out, profile, progr);
|
||||
}
|
||||
|
||||
} // namespace Slic3r::sla
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -13,9 +13,13 @@
|
|||
#include "GCode/SpiralVase.hpp"
|
||||
#include "GCode/ToolOrdering.hpp"
|
||||
#include "GCode/WipeTower.hpp"
|
||||
#include "GCodeTimeEstimator.hpp"
|
||||
#include "EdgeGrid.hpp"
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include "GCode/GCodeProcessor.hpp"
|
||||
#else
|
||||
#include "GCode/Analyzer.hpp"
|
||||
#include "GCodeTimeEstimator.hpp"
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
#include "EdgeGrid.hpp"
|
||||
#include "GCode/ThumbnailData.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
|
@ -29,7 +33,9 @@ namespace Slic3r {
|
|||
|
||||
// Forward declarations.
|
||||
class GCode;
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
class GCodePreviewData;
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
namespace { struct Item; }
|
||||
struct PrintInstance;
|
||||
|
|
@ -138,6 +144,15 @@ private:
|
|||
double m_last_wipe_tower_print_z = 0.f;
|
||||
};
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
class ColorPrintColors
|
||||
{
|
||||
static const std::vector<std::string> Colors;
|
||||
public:
|
||||
static const std::vector<std::string>& get() { return Colors; }
|
||||
};
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
class GCode {
|
||||
public:
|
||||
GCode() :
|
||||
|
|
@ -145,21 +160,33 @@ public:
|
|||
m_enable_loop_clipping(true),
|
||||
m_enable_cooling_markers(false),
|
||||
m_enable_extrusion_role_markers(false),
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
m_last_processor_extrusion_role(erNone),
|
||||
#else
|
||||
m_enable_analyzer(false),
|
||||
m_last_analyzer_extrusion_role(erNone),
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
m_layer_count(0),
|
||||
m_layer_index(-1),
|
||||
m_layer(nullptr),
|
||||
m_volumetric_speed(0),
|
||||
m_last_pos_defined(false),
|
||||
m_last_extrusion_role(erNone),
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_last_mm3_per_mm(0.0),
|
||||
m_last_width(0.0f),
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
m_last_mm3_per_mm(GCodeAnalyzer::Default_mm3_per_mm),
|
||||
m_last_width(GCodeAnalyzer::Default_Width),
|
||||
m_last_height(GCodeAnalyzer::Default_Height),
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
m_brim_done(false),
|
||||
m_second_layer_things_done(false),
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
m_normal_time_estimator(GCodeTimeEstimator::Normal),
|
||||
m_silent_time_estimator(GCodeTimeEstimator::Silent),
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
m_silent_time_estimator_enabled(false),
|
||||
m_last_obj_copy(nullptr, Point(std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max()))
|
||||
{}
|
||||
|
|
@ -167,7 +194,11 @@ public:
|
|||
|
||||
// throws std::runtime_exception on error,
|
||||
// throws CanceledException through print->throw_if_canceled().
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
void do_export(Print* print, const char* path, GCodeProcessor::Result* result = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
|
||||
#else
|
||||
void do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
// Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
|
||||
const Vec2d& origin() const { return m_origin; }
|
||||
|
|
@ -327,11 +358,16 @@ private:
|
|||
// Markers for the Pressure Equalizer to recognize the extrusion type.
|
||||
// The Pressure Equalizer removes the markers from the final G-code.
|
||||
bool m_enable_extrusion_role_markers;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
// Keeps track of the last extrusion role passed to the processor
|
||||
ExtrusionRole m_last_processor_extrusion_role;
|
||||
#else
|
||||
// Enableds the G-code Analyzer.
|
||||
// Extended markers will be added during G-code generation.
|
||||
// The G-code Analyzer will remove these comments from the final G-code.
|
||||
bool m_enable_analyzer;
|
||||
ExtrusionRole m_last_analyzer_extrusion_role;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
// How many times will change_layer() be called?
|
||||
// change_layer() will update the progress bar.
|
||||
unsigned int m_layer_count;
|
||||
|
|
@ -344,10 +380,20 @@ private:
|
|||
double m_volumetric_speed;
|
||||
// Support for the extrusion role markers. Which marker is active?
|
||||
ExtrusionRole m_last_extrusion_role;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
// Support for G-Code Processor
|
||||
float m_last_height{ 0.0f };
|
||||
float m_last_layer_z{ 0.0f };
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
double m_last_mm3_per_mm;
|
||||
float m_last_width{ 0.0f };
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
#else
|
||||
// Support for G-Code Analyzer
|
||||
double m_last_mm3_per_mm;
|
||||
float m_last_width;
|
||||
float m_last_height;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
Point m_last_pos;
|
||||
bool m_last_pos_defined;
|
||||
|
|
@ -368,13 +414,20 @@ private:
|
|||
// Index of a last object copy extruded.
|
||||
std::pair<const PrintObject*, Point> m_last_obj_copy;
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
// Time estimators
|
||||
GCodeTimeEstimator m_normal_time_estimator;
|
||||
GCodeTimeEstimator m_silent_time_estimator;
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
bool m_silent_time_estimator_enabled;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
// Processor
|
||||
GCodeProcessor m_processor;
|
||||
#else
|
||||
// Analyzer
|
||||
GCodeAnalyzer m_analyzer;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
// Write a string into a file.
|
||||
void _write(FILE* file, const std::string& what) { this->_write(file, what.c_str()); }
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
#include "Analyzer.hpp"
|
||||
#include "PreviewData.hpp"
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
|
||||
static const std::string AXIS_STR = "XYZE";
|
||||
static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
|
||||
static const float INCHES_TO_MM = 25.4f;
|
||||
|
|
@ -350,7 +352,7 @@ void GCodeAnalyzer::_processG1(const GCodeReader::GCodeLine& line)
|
|||
if (delta_pos[E] < 0.0f)
|
||||
{
|
||||
if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f) || (delta_pos[Z] != 0.0f))
|
||||
type = GCodeMove::Move;
|
||||
type = GCodeMove::Move;
|
||||
else
|
||||
type = GCodeMove::Retract;
|
||||
}
|
||||
|
|
@ -651,7 +653,7 @@ bool GCodeAnalyzer::_process_tags(const GCodeReader::GCodeLine& line)
|
|||
return true;
|
||||
}
|
||||
|
||||
// color change tag
|
||||
// pause print tag
|
||||
pos = comment.find(Pause_Print_Tag);
|
||||
if (pos != comment.npos)
|
||||
{
|
||||
|
|
@ -659,7 +661,7 @@ bool GCodeAnalyzer::_process_tags(const GCodeReader::GCodeLine& line)
|
|||
return true;
|
||||
}
|
||||
|
||||
// color change tag
|
||||
// custom code tag
|
||||
pos = comment.find(Custom_Code_Tag);
|
||||
if (pos != comment.npos)
|
||||
{
|
||||
|
|
@ -667,7 +669,7 @@ bool GCodeAnalyzer::_process_tags(const GCodeReader::GCodeLine& line)
|
|||
return true;
|
||||
}
|
||||
|
||||
// color change tag
|
||||
// end pause print or custom code tag
|
||||
pos = comment.find(End_Pause_Print_Or_Custom_Code_Tag);
|
||||
if (pos != comment.npos)
|
||||
{
|
||||
|
|
@ -1191,3 +1193,5 @@ size_t GCodeAnalyzer::memory_used() const
|
|||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef slic3r_GCode_Analyzer_hpp_
|
||||
#define slic3r_GCode_Analyzer_hpp_
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
|
||||
#include "../libslic3r.h"
|
||||
#include "../PrintConfig.hpp"
|
||||
#include "../ExtrusionEntity.hpp"
|
||||
|
|
@ -302,4 +304,6 @@ private:
|
|||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
#endif /* slic3r_GCode_Analyzer_hpp_ */
|
||||
|
|
|
|||
2181
src/libslic3r/GCode/GCodeProcessor.cpp
Normal file
2181
src/libslic3r/GCode/GCodeProcessor.cpp
Normal file
File diff suppressed because it is too large
Load diff
560
src/libslic3r/GCode/GCodeProcessor.hpp
Normal file
560
src/libslic3r/GCode/GCodeProcessor.hpp
Normal file
|
|
@ -0,0 +1,560 @@
|
|||
#ifndef slic3r_GCodeProcessor_hpp_
|
||||
#define slic3r_GCodeProcessor_hpp_
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include "libslic3r/GCodeReader.hpp"
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/ExtrusionEntity.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/CustomGCode.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
enum class EMoveType : unsigned char
|
||||
{
|
||||
Noop,
|
||||
Retract,
|
||||
Unretract,
|
||||
Tool_change,
|
||||
Color_change,
|
||||
Pause_Print,
|
||||
Custom_GCode,
|
||||
Travel,
|
||||
Extrude,
|
||||
Count
|
||||
};
|
||||
|
||||
struct PrintEstimatedTimeStatistics
|
||||
{
|
||||
enum class ETimeMode : unsigned char
|
||||
{
|
||||
Normal,
|
||||
Stealth,
|
||||
Count
|
||||
};
|
||||
|
||||
struct Mode
|
||||
{
|
||||
float time;
|
||||
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> custom_gcode_times;
|
||||
std::vector<std::pair<EMoveType, float>> moves_times;
|
||||
std::vector<std::pair<ExtrusionRole, float>> roles_times;
|
||||
std::vector<float> layers_times;
|
||||
|
||||
void reset() {
|
||||
time = 0.0f;
|
||||
custom_gcode_times.clear();
|
||||
moves_times.clear();
|
||||
roles_times.clear();
|
||||
layers_times.clear();
|
||||
}
|
||||
};
|
||||
|
||||
std::array<Mode, static_cast<size_t>(ETimeMode::Count)> modes;
|
||||
|
||||
PrintEstimatedTimeStatistics() { reset(); }
|
||||
|
||||
void reset() {
|
||||
for (auto m : modes) {
|
||||
m.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class GCodeProcessor
|
||||
{
|
||||
public:
|
||||
static const std::string Extrusion_Role_Tag;
|
||||
static const std::string Height_Tag;
|
||||
static const std::string Layer_Change_Tag;
|
||||
static const std::string Color_Change_Tag;
|
||||
static const std::string Pause_Print_Tag;
|
||||
static const std::string Custom_Code_Tag;
|
||||
static const std::string First_Line_M73_Placeholder_Tag;
|
||||
static const std::string Last_Line_M73_Placeholder_Tag;
|
||||
static const std::string Estimated_Printing_Time_Placeholder_Tag;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
static const std::string Width_Tag;
|
||||
static const std::string Mm3_Per_Mm_Tag;
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
private:
|
||||
using AxisCoords = std::array<float, 4>;
|
||||
using ExtruderColors = std::vector<unsigned char>;
|
||||
|
||||
enum class EUnits : unsigned char
|
||||
{
|
||||
Millimeters,
|
||||
Inches
|
||||
};
|
||||
|
||||
enum class EPositioningType : unsigned char
|
||||
{
|
||||
Absolute,
|
||||
Relative
|
||||
};
|
||||
|
||||
struct CachedPosition
|
||||
{
|
||||
AxisCoords position; // mm
|
||||
float feedrate; // mm/s
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
struct CpColor
|
||||
{
|
||||
unsigned char counter;
|
||||
unsigned char current;
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
public:
|
||||
struct FeedrateProfile
|
||||
{
|
||||
float entry{ 0.0f }; // mm/s
|
||||
float cruise{ 0.0f }; // mm/s
|
||||
float exit{ 0.0f }; // mm/s
|
||||
};
|
||||
|
||||
struct Trapezoid
|
||||
{
|
||||
float accelerate_until{ 0.0f }; // mm
|
||||
float decelerate_after{ 0.0f }; // mm
|
||||
float cruise_feedrate{ 0.0f }; // mm/sec
|
||||
|
||||
float acceleration_time(float entry_feedrate, float acceleration) const;
|
||||
float cruise_time() const;
|
||||
float deceleration_time(float distance, float acceleration) const;
|
||||
float cruise_distance() const;
|
||||
};
|
||||
|
||||
struct TimeBlock
|
||||
{
|
||||
struct Flags
|
||||
{
|
||||
bool recalculate{ false };
|
||||
bool nominal_length{ false };
|
||||
};
|
||||
|
||||
EMoveType move_type{ EMoveType::Noop };
|
||||
ExtrusionRole role{ erNone };
|
||||
unsigned int layer_id{ 0 };
|
||||
float distance{ 0.0f }; // mm
|
||||
float acceleration{ 0.0f }; // mm/s^2
|
||||
float max_entry_speed{ 0.0f }; // mm/s
|
||||
float safe_feedrate{ 0.0f }; // mm/s
|
||||
Flags flags;
|
||||
FeedrateProfile feedrate_profile;
|
||||
Trapezoid trapezoid;
|
||||
|
||||
// Calculates this block's trapezoid
|
||||
void calculate_trapezoid();
|
||||
|
||||
float time() const;
|
||||
};
|
||||
|
||||
private:
|
||||
struct TimeMachine
|
||||
{
|
||||
struct State
|
||||
{
|
||||
float feedrate; // mm/s
|
||||
float safe_feedrate; // mm/s
|
||||
AxisCoords axis_feedrate; // mm/s
|
||||
AxisCoords abs_axis_feedrate; // mm/s
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
struct CustomGCodeTime
|
||||
{
|
||||
bool needed;
|
||||
float cache;
|
||||
std::vector<std::pair<CustomGCode::Type, float>> times;
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
bool enabled;
|
||||
float acceleration; // mm/s^2
|
||||
// hard limit for the acceleration, to which the firmware will clamp.
|
||||
float max_acceleration; // mm/s^2
|
||||
float extrude_factor_override_percentage;
|
||||
float time; // s
|
||||
std::string line_m73_mask;
|
||||
State curr;
|
||||
State prev;
|
||||
CustomGCodeTime gcode_time;
|
||||
std::vector<TimeBlock> blocks;
|
||||
std::vector<float> g1_times_cache;
|
||||
std::array<float, static_cast<size_t>(EMoveType::Count)> moves_time;
|
||||
std::array<float, static_cast<size_t>(ExtrusionRole::erCount)> roles_time;
|
||||
std::vector<float> layers_time;
|
||||
|
||||
void reset();
|
||||
|
||||
// Simulates firmware st_synchronize() call
|
||||
void simulate_st_synchronize(float additional_time = 0.0f);
|
||||
void calculate_time(size_t keep_last_n_blocks = 0);
|
||||
};
|
||||
|
||||
struct TimeProcessor
|
||||
{
|
||||
struct Planner
|
||||
{
|
||||
// Size of the firmware planner queue. The old 8-bit Marlins usually just managed 16 trapezoidal blocks.
|
||||
// Let's be conservative and plan for newer boards with more memory.
|
||||
static constexpr size_t queue_size = 64;
|
||||
// The firmware recalculates last planner_queue_size trapezoidal blocks each time a new block is added.
|
||||
// We are not simulating the firmware exactly, we calculate a sequence of blocks once a reasonable number of blocks accumulate.
|
||||
static constexpr size_t refresh_threshold = queue_size * 4;
|
||||
};
|
||||
|
||||
// extruder_id is currently used to correctly calculate filament load / unload times into the total print time.
|
||||
// This is currently only really used by the MK3 MMU2:
|
||||
// extruder_unloaded = true means no filament is loaded yet, all the filaments are parked in the MK3 MMU2 unit.
|
||||
bool extruder_unloaded;
|
||||
// whether or not to export post-process the gcode to export lines M73 in it
|
||||
bool export_remaining_time_enabled;
|
||||
// allow to skip the lines M201/M203/M204/M205 generated by GCode::print_machine_envelope()
|
||||
bool machine_envelope_processing_enabled;
|
||||
MachineEnvelopeConfig machine_limits;
|
||||
// Additional load / unload times for a filament exchange sequence.
|
||||
std::vector<float> filament_load_times;
|
||||
std::vector<float> filament_unload_times;
|
||||
std::array<TimeMachine, static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count)> machines;
|
||||
|
||||
void reset();
|
||||
|
||||
// post process the file with the given filename to add remaining time lines M73
|
||||
void post_process(const std::string& filename);
|
||||
};
|
||||
|
||||
public:
|
||||
struct MoveVertex
|
||||
{
|
||||
EMoveType type{ EMoveType::Noop };
|
||||
ExtrusionRole extrusion_role{ erNone };
|
||||
unsigned char extruder_id{ 0 };
|
||||
unsigned char cp_color_id{ 0 };
|
||||
Vec3f position{ Vec3f::Zero() }; // mm
|
||||
float delta_extruder{ 0.0f }; // mm
|
||||
float feedrate{ 0.0f }; // mm/s
|
||||
float width{ 0.0f }; // mm
|
||||
float height{ 0.0f }; // mm
|
||||
float mm3_per_mm{ 0.0f };
|
||||
float fan_speed{ 0.0f }; // percentage
|
||||
float time{ 0.0f }; // s
|
||||
|
||||
float volumetric_rate() const { return feedrate * mm3_per_mm; }
|
||||
};
|
||||
|
||||
struct Result
|
||||
{
|
||||
unsigned int id;
|
||||
std::vector<MoveVertex> moves;
|
||||
Pointfs bed_shape;
|
||||
std::string printer_settings_id;
|
||||
std::vector<std::string> extruder_colors;
|
||||
PrintEstimatedTimeStatistics time_statistics;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
long long time{ 0 };
|
||||
void reset()
|
||||
{
|
||||
time = 0;
|
||||
moves = std::vector<MoveVertex>();
|
||||
bed_shape = Pointfs();
|
||||
extruder_colors = std::vector<std::string>();
|
||||
}
|
||||
#else
|
||||
void reset()
|
||||
{
|
||||
moves = std::vector<MoveVertex>();
|
||||
bed_shape = Pointfs();
|
||||
extruder_colors = std::vector<std::string>();
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
};
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
struct DataChecker
|
||||
{
|
||||
struct Error
|
||||
{
|
||||
float value;
|
||||
float tag_value;
|
||||
ExtrusionRole role;
|
||||
};
|
||||
|
||||
std::string type;
|
||||
float threshold{ 0.01f };
|
||||
float last_tag_value{ 0.0f };
|
||||
unsigned int count{ 0 };
|
||||
std::vector<Error> errors;
|
||||
|
||||
DataChecker(const std::string& type, float threshold)
|
||||
: type(type), threshold(threshold)
|
||||
{}
|
||||
|
||||
void update(float value, ExtrusionRole role) {
|
||||
++count;
|
||||
if (last_tag_value != 0.0f) {
|
||||
if (std::abs(value - last_tag_value) / last_tag_value > threshold)
|
||||
errors.push_back({ value, last_tag_value, role });
|
||||
}
|
||||
}
|
||||
|
||||
void reset() { last_tag_value = 0.0f; errors.clear(); count = 0; }
|
||||
|
||||
std::pair<float, float> get_min() const {
|
||||
float delta_min = FLT_MAX;
|
||||
float perc_min = 0.0f;
|
||||
for (const Error& e : errors) {
|
||||
if (delta_min > e.value - e.tag_value) {
|
||||
delta_min = e.value - e.tag_value;
|
||||
perc_min = 100.0f * delta_min / e.tag_value;
|
||||
}
|
||||
}
|
||||
return { delta_min, perc_min };
|
||||
}
|
||||
|
||||
std::pair<float, float> get_max() const {
|
||||
float delta_max = -FLT_MAX;
|
||||
float perc_max = 0.0f;
|
||||
for (const Error& e : errors) {
|
||||
if (delta_max < e.value - e.tag_value) {
|
||||
delta_max = e.value - e.tag_value;
|
||||
perc_max = 100.0f * delta_max / e.tag_value;
|
||||
}
|
||||
}
|
||||
return { delta_max, perc_max };
|
||||
}
|
||||
|
||||
void output() const {
|
||||
if (!errors.empty()) {
|
||||
std::cout << type << ":\n";
|
||||
std::cout << "Errors: " << errors.size() << " (" << 100.0f * float(errors.size()) / float(count) << "%)\n";
|
||||
auto [min, perc_min] = get_min();
|
||||
auto [max, perc_max] = get_max();
|
||||
std::cout << "min: " << min << "(" << perc_min << "%) - max: " << max << "(" << perc_max << "%)\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
private:
|
||||
GCodeReader m_parser;
|
||||
|
||||
EUnits m_units;
|
||||
EPositioningType m_global_positioning_type;
|
||||
EPositioningType m_e_local_positioning_type;
|
||||
std::vector<Vec3f> m_extruder_offsets;
|
||||
GCodeFlavor m_flavor;
|
||||
|
||||
AxisCoords m_start_position; // mm
|
||||
AxisCoords m_end_position; // mm
|
||||
AxisCoords m_origin; // mm
|
||||
CachedPosition m_cached_position;
|
||||
|
||||
float m_feedrate; // mm/s
|
||||
float m_width; // mm
|
||||
float m_height; // mm
|
||||
float m_mm3_per_mm;
|
||||
float m_fan_speed; // percentage
|
||||
ExtrusionRole m_extrusion_role;
|
||||
unsigned char m_extruder_id;
|
||||
ExtruderColors m_extruder_colors;
|
||||
std::vector<float> m_filament_diameters;
|
||||
float m_extruded_last_z;
|
||||
unsigned int m_layer_id;
|
||||
CpColor m_cp_color;
|
||||
|
||||
enum class EProducer
|
||||
{
|
||||
Unknown,
|
||||
PrusaSlicer,
|
||||
Cura,
|
||||
Simplify3D,
|
||||
CraftWare,
|
||||
ideaMaker
|
||||
};
|
||||
|
||||
static const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> Producers;
|
||||
EProducer m_producer;
|
||||
bool m_producers_enabled;
|
||||
|
||||
TimeProcessor m_time_processor;
|
||||
|
||||
Result m_result;
|
||||
static unsigned int s_result_id;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
DataChecker m_mm3_per_mm_compare{ "mm3_per_mm", 0.01f };
|
||||
DataChecker m_height_compare{ "height", 0.01f };
|
||||
DataChecker m_width_compare{ "width", 0.01f };
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
public:
|
||||
GCodeProcessor();
|
||||
|
||||
void apply_config(const PrintConfig& config);
|
||||
void apply_config(const DynamicPrintConfig& config);
|
||||
void enable_stealth_time_estimator(bool enabled);
|
||||
bool is_stealth_time_estimator_enabled() const {
|
||||
return m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].enabled;
|
||||
}
|
||||
void enable_machine_envelope_processing(bool enabled) { m_time_processor.machine_envelope_processing_enabled = enabled; }
|
||||
void enable_producers(bool enabled) { m_producers_enabled = enabled; }
|
||||
void reset();
|
||||
|
||||
const Result& get_result() const { return m_result; }
|
||||
Result&& extract_result() { return std::move(m_result); }
|
||||
|
||||
// Process the gcode contained in the file with the given filename
|
||||
// throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
|
||||
void process_file(const std::string& filename, std::function<void()> cancel_callback = nullptr);
|
||||
|
||||
float get_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
||||
std::string get_time_dhm(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
||||
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> get_custom_gcode_times(PrintEstimatedTimeStatistics::ETimeMode mode, bool include_remaining) const;
|
||||
|
||||
std::vector<std::pair<EMoveType, float>> get_moves_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
||||
std::vector<std::pair<ExtrusionRole, float>> get_roles_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
||||
std::vector<float> get_layers_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
||||
|
||||
private:
|
||||
void process_gcode_line(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Process tags embedded into comments
|
||||
void process_tags(const std::string& comment);
|
||||
bool process_producers_tags(const std::string& comment);
|
||||
bool process_prusaslicer_tags(const std::string& comment);
|
||||
bool process_cura_tags(const std::string& comment);
|
||||
bool process_simplify3d_tags(const std::string& comment);
|
||||
bool process_craftware_tags(const std::string& comment);
|
||||
bool process_ideamaker_tags(const std::string& comment);
|
||||
|
||||
bool detect_producer(const std::string& comment);
|
||||
|
||||
// Move
|
||||
void process_G0(const GCodeReader::GCodeLine& line);
|
||||
void process_G1(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Retract
|
||||
void process_G10(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Unretract
|
||||
void process_G11(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set Units to Inches
|
||||
void process_G20(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set Units to Millimeters
|
||||
void process_G21(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Firmware controlled Retract
|
||||
void process_G22(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Firmware controlled Unretract
|
||||
void process_G23(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set to Absolute Positioning
|
||||
void process_G90(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set to Relative Positioning
|
||||
void process_G91(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set Position
|
||||
void process_G92(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Sleep or Conditional stop
|
||||
void process_M1(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set extruder to absolute mode
|
||||
void process_M82(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set extruder to relative mode
|
||||
void process_M83(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set fan speed
|
||||
void process_M106(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Disable fan
|
||||
void process_M107(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set tool (Sailfish)
|
||||
void process_M108(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Recall stored home offsets
|
||||
void process_M132(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set tool (MakerWare)
|
||||
void process_M135(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set max printing acceleration
|
||||
void process_M201(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set maximum feedrate
|
||||
void process_M203(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set default acceleration
|
||||
void process_M204(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Advanced settings
|
||||
void process_M205(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set extrude factor override percentage
|
||||
void process_M221(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Repetier: Store x, y and z position
|
||||
void process_M401(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Repetier: Go to stored position
|
||||
void process_M402(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set allowable instantaneous speed change
|
||||
void process_M566(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Unload the current filament into the MK3 MMU2 unit at the end of print.
|
||||
void process_M702(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Processes T line (Select Tool)
|
||||
void process_T(const GCodeReader::GCodeLine& line);
|
||||
void process_T(const std::string& command);
|
||||
|
||||
void store_move_vertex(EMoveType type);
|
||||
|
||||
float minimum_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const;
|
||||
float minimum_travel_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const;
|
||||
float get_axis_max_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const;
|
||||
float get_axis_max_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const;
|
||||
float get_axis_max_jerk(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const;
|
||||
float get_retract_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
||||
float get_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
||||
void set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value);
|
||||
float get_filament_load_time(size_t extruder_id);
|
||||
float get_filament_unload_time(size_t extruder_id);
|
||||
|
||||
void process_custom_gcode_time(CustomGCode::Type code);
|
||||
|
||||
// Simulates firmware st_synchronize() call
|
||||
void simulate_st_synchronize(float additional_time = 0.0f);
|
||||
|
||||
void update_estimated_times_stats();
|
||||
};
|
||||
|
||||
} /* namespace Slic3r */
|
||||
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
#endif /* slic3r_GCodeProcessor_hpp_ */
|
||||
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ static DWORD execute_process_winapi(const std::wstring &command_line)
|
|||
if (! ::CreateProcessW(
|
||||
nullptr /* lpApplicationName */, (LPWSTR)command_line.c_str(), nullptr /* lpProcessAttributes */, nullptr /* lpThreadAttributes */, false /* bInheritHandles */,
|
||||
CREATE_UNICODE_ENVIRONMENT /* | CREATE_NEW_CONSOLE */ /* dwCreationFlags */, (LPVOID)envstr.c_str(), nullptr /* lpCurrentDirectory */, &startup_info, &process_info))
|
||||
throw std::runtime_error(std::string("Failed starting the script ") + boost::nowide::narrow(command_line) + ", Win32 error: " + std::to_string(int(::GetLastError())));
|
||||
throw Slic3r::RuntimeError(std::string("Failed starting the script ") + boost::nowide::narrow(command_line) + ", Win32 error: " + std::to_string(int(::GetLastError())));
|
||||
::WaitForSingleObject(process_info.hProcess, INFINITE);
|
||||
ULONG rc = 0;
|
||||
::GetExitCodeProcess(process_info.hProcess, &rc);
|
||||
|
|
@ -98,13 +98,13 @@ static int run_script(const std::string &script, const std::string &gcode, std::
|
|||
LPWSTR *szArglist = CommandLineToArgvW(boost::nowide::widen(script).c_str(), &nArgs);
|
||||
if (szArglist == nullptr || nArgs <= 0) {
|
||||
// CommandLineToArgvW failed. Maybe the command line escapment is invalid?
|
||||
throw std::runtime_error(std::string("Post processing script ") + script + " on file " + gcode + " failed. CommandLineToArgvW() refused to parse the command line path.");
|
||||
throw Slic3r::RuntimeError(std::string("Post processing script ") + script + " on file " + gcode + " failed. CommandLineToArgvW() refused to parse the command line path.");
|
||||
}
|
||||
|
||||
std::wstring command_line;
|
||||
std::wstring command = szArglist[0];
|
||||
if (! boost::filesystem::exists(boost::filesystem::path(command)))
|
||||
throw std::runtime_error(std::string("The configured post-processing script does not exist: ") + boost::nowide::narrow(command));
|
||||
throw Slic3r::RuntimeError(std::string("The configured post-processing script does not exist: ") + boost::nowide::narrow(command));
|
||||
if (boost::iends_with(command, L".pl")) {
|
||||
// This is a perl script. Run it through the perl interpreter.
|
||||
// The current process may be slic3r.exe or slic3r-console.exe.
|
||||
|
|
@ -115,7 +115,7 @@ static int run_script(const std::string &script, const std::string &gcode, std::
|
|||
boost::filesystem::path path_perl = path_exe.parent_path() / "perl" / "perl.exe";
|
||||
if (! boost::filesystem::exists(path_perl)) {
|
||||
LocalFree(szArglist);
|
||||
throw std::runtime_error(std::string("Perl interpreter ") + path_perl.string() + " does not exist.");
|
||||
throw Slic3r::RuntimeError(std::string("Perl interpreter ") + path_perl.string() + " does not exist.");
|
||||
}
|
||||
// Replace it with the current perl interpreter.
|
||||
quote_argv_winapi(boost::nowide::widen(path_perl.string()), command_line);
|
||||
|
|
@ -187,7 +187,7 @@ void run_post_process_scripts(const std::string &path, const PrintConfig &config
|
|||
config.setenv_();
|
||||
auto gcode_file = boost::filesystem::path(path);
|
||||
if (! boost::filesystem::exists(gcode_file))
|
||||
throw std::runtime_error(std::string("Post-processor can't find exported gcode file"));
|
||||
throw Slic3r::RuntimeError(std::string("Post-processor can't find exported gcode file"));
|
||||
|
||||
for (const std::string &scripts : config.post_process.values) {
|
||||
std::vector<std::string> lines;
|
||||
|
|
@ -205,7 +205,7 @@ void run_post_process_scripts(const std::string &path, const PrintConfig &config
|
|||
const std::string msg = std_err.empty() ? (boost::format("Post-processing script %1% on file %2% failed.\nError code: %3%") % script % path % result).str()
|
||||
: (boost::format("Post-processing script %1% on file %2% failed.\nError code: %3%\nOutput:\n%4%") % script % path % result % std_err).str();
|
||||
BOOST_LOG_TRIVIAL(error) << msg;
|
||||
throw std::runtime_error(msg);
|
||||
throw Slic3r::RuntimeError(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ static inline int parse_int(const char *&line)
|
|||
char *endptr = NULL;
|
||||
long result = strtol(line, &endptr, 10);
|
||||
if (endptr == NULL || !is_ws_or_eol(*endptr))
|
||||
throw std::runtime_error("PressureEqualizer: Error parsing an int");
|
||||
throw Slic3r::RuntimeError("PressureEqualizer: Error parsing an int");
|
||||
line = endptr;
|
||||
return int(result);
|
||||
};
|
||||
|
|
@ -160,7 +160,7 @@ static inline float parse_float(const char *&line)
|
|||
char *endptr = NULL;
|
||||
float result = strtof(line, &endptr);
|
||||
if (endptr == NULL || !is_ws_or_eol(*endptr))
|
||||
throw std::runtime_error("PressureEqualizer: Error parsing a float");
|
||||
throw Slic3r::RuntimeError("PressureEqualizer: Error parsing a float");
|
||||
line = endptr;
|
||||
return result;
|
||||
};
|
||||
|
|
@ -229,7 +229,7 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
|
|||
assert(false);
|
||||
}
|
||||
if (i == -1)
|
||||
throw std::runtime_error(std::string("GCode::PressureEqualizer: Invalid axis for G0/G1: ") + axis);
|
||||
throw Slic3r::RuntimeError(std::string("GCode::PressureEqualizer: Invalid axis for G0/G1: ") + axis);
|
||||
buf.pos_provided[i] = true;
|
||||
new_pos[i] = parse_float(line);
|
||||
if (i == 3 && m_config->use_relative_e_distances.value)
|
||||
|
|
@ -298,7 +298,7 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
|
|||
set = true;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error(std::string("GCode::PressureEqualizer: Incorrect axis in a G92 G-code: ") + axis);
|
||||
throw Slic3r::RuntimeError(std::string("GCode::PressureEqualizer: Incorrect axis in a G92 G-code: ") + axis);
|
||||
}
|
||||
eatws(line);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
|
||||
//! macro used to mark string used at localization,
|
||||
#define L(s) (s)
|
||||
|
||||
|
|
@ -516,3 +518,5 @@ Color operator * (float f, const Color& color)
|
|||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef slic3r_GCode_PreviewData_hpp_
|
||||
#define slic3r_GCode_PreviewData_hpp_
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
|
||||
#include "../libslic3r.h"
|
||||
#include "../ExtrusionEntity.hpp"
|
||||
#include "../Point.hpp"
|
||||
|
|
@ -56,8 +58,7 @@ public:
|
|||
// Color mapping to convert a float into a smooth rainbow of 10 colors.
|
||||
class RangeBase
|
||||
{
|
||||
public:
|
||||
|
||||
public:
|
||||
virtual void reset() = 0;
|
||||
virtual bool empty() const = 0;
|
||||
virtual float min() const = 0;
|
||||
|
|
@ -73,7 +74,7 @@ public:
|
|||
// Color mapping converting a float in a range between a min and a max into a smooth rainbow of 10 colors.
|
||||
class Range : public RangeBase
|
||||
{
|
||||
public:
|
||||
public:
|
||||
Range();
|
||||
|
||||
// RangeBase Overrides
|
||||
|
|
@ -97,8 +98,7 @@ public:
|
|||
template <typename EnumRangeType>
|
||||
class MultiRange : public RangeBase
|
||||
{
|
||||
public:
|
||||
|
||||
public:
|
||||
void reset() override
|
||||
{
|
||||
bounds = decltype(bounds){};
|
||||
|
|
@ -160,8 +160,7 @@ public:
|
|||
mode.set(static_cast<std::size_t>(range_type_value), enable);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
private:
|
||||
// Interval bounds
|
||||
struct Bounds
|
||||
{
|
||||
|
|
@ -394,4 +393,6 @@ public:
|
|||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
#endif /* slic3r_GCode_PreviewData_hpp_ */
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ static BoundingBoxf extrusionentity_extents(const ExtrusionEntity *extrusion_ent
|
|||
auto *extrusion_entity_collection = dynamic_cast<const ExtrusionEntityCollection*>(extrusion_entity);
|
||||
if (extrusion_entity_collection != nullptr)
|
||||
return extrusionentity_extents(*extrusion_entity_collection);
|
||||
throw std::runtime_error("Unexpected extrusion_entity type in extrusionentity_extents()");
|
||||
throw Slic3r::RuntimeError("Unexpected extrusion_entity type in extrusionentity_extents()");
|
||||
return BoundingBoxf();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,11 @@ TODO LIST
|
|||
#include <vector>
|
||||
#include <numeric>
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include "GCodeProcessor.hpp"
|
||||
#else
|
||||
#include "Analyzer.hpp"
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
#include "BoundingBox.hpp"
|
||||
|
||||
#if defined(__linux) || defined(__GNUC__ )
|
||||
|
|
@ -47,36 +51,69 @@ public:
|
|||
m_extrusion_flow(0.f),
|
||||
m_preview_suppressed(false),
|
||||
m_elapsed_time(0.f),
|
||||
#if !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_default_analyzer_line_width(line_width),
|
||||
#endif // !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_gcode_flavor(flavor),
|
||||
m_filpar(filament_parameters)
|
||||
{
|
||||
// adds tag for analyzer:
|
||||
char buf[64];
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
sprintf(buf, ";%s%f\n", GCodeProcessor::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming
|
||||
m_gcode += buf;
|
||||
sprintf(buf, ";%s%s\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), ExtrusionEntity::role_to_string(erWipeTower).c_str());
|
||||
m_gcode += buf;
|
||||
#else
|
||||
sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming
|
||||
m_gcode += buf;
|
||||
sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower);
|
||||
m_gcode += buf;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
change_analyzer_line_width(line_width);
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
}
|
||||
|
||||
WipeTowerWriter& change_analyzer_line_width(float line_width) {
|
||||
// adds tag for analyzer:
|
||||
char buf[64];
|
||||
sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width);
|
||||
m_gcode += buf;
|
||||
return *this;
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
WipeTowerWriter& change_analyzer_line_width(float line_width) {
|
||||
// adds tag for analyzer:
|
||||
char buf[64];
|
||||
sprintf(buf, ";%s%f\n", GCodeProcessor::Width_Tag.c_str(), line_width);
|
||||
m_gcode += buf;
|
||||
return *this;
|
||||
}
|
||||
|
||||
WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) {
|
||||
static const float area = float(M_PI) * 1.75f * 1.75f / 4.f;
|
||||
float mm3_per_mm = (len == 0.f ? 0.f : area * e / len);
|
||||
// adds tag for analyzer:
|
||||
char buf[64];
|
||||
sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm);
|
||||
m_gcode += buf;
|
||||
return *this;
|
||||
WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) {
|
||||
static const float area = float(M_PI) * 1.75f * 1.75f / 4.f;
|
||||
float mm3_per_mm = (len == 0.f ? 0.f : area * e / len);
|
||||
// adds tag for processor:
|
||||
char buf[64];
|
||||
sprintf(buf, ";%s%f\n", GCodeProcessor::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm);
|
||||
m_gcode += buf;
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
WipeTowerWriter& change_analyzer_line_width(float line_width) {
|
||||
// adds tag for analyzer:
|
||||
char buf[64];
|
||||
sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width);
|
||||
m_gcode += buf;
|
||||
return *this;
|
||||
}
|
||||
|
||||
WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) {
|
||||
static const float area = float(M_PI) * 1.75f * 1.75f / 4.f;
|
||||
float mm3_per_mm = (len == 0.f ? 0.f : area * e / len);
|
||||
// adds tag for analyzer:
|
||||
char buf[64];
|
||||
sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm);
|
||||
m_gcode += buf;
|
||||
return *this;
|
||||
}
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
WipeTowerWriter& set_initial_position(const Vec2f &pos, float width = 0.f, float depth = 0.f, float internal_angle = 0.f) {
|
||||
m_wipe_tower_width = width;
|
||||
|
|
@ -111,8 +148,13 @@ public:
|
|||
// Suppress / resume G-code preview in Slic3r. Slic3r will have difficulty to differentiate the various
|
||||
// filament loading and cooling moves from normal extrusion moves. Therefore the writer
|
||||
// is asked to suppres output of some lines, which look like extrusions.
|
||||
WipeTowerWriter& suppress_preview() { change_analyzer_line_width(0.f); m_preview_suppressed = true; return *this; }
|
||||
WipeTowerWriter& resume_preview() { change_analyzer_line_width(m_default_analyzer_line_width); m_preview_suppressed = false; return *this; }
|
||||
#if !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
WipeTowerWriter& suppress_preview() { change_analyzer_line_width(0.f); m_preview_suppressed = true; return *this; }
|
||||
WipeTowerWriter& resume_preview() { change_analyzer_line_width(m_default_analyzer_line_width); m_preview_suppressed = false; return *this; }
|
||||
#else
|
||||
WipeTowerWriter& suppress_preview() { m_preview_suppressed = true; return *this; }
|
||||
WipeTowerWriter& resume_preview() { m_preview_suppressed = false; return *this; }
|
||||
#endif // !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
WipeTowerWriter& feedrate(float f)
|
||||
{
|
||||
|
|
@ -149,8 +191,14 @@ public:
|
|||
Vec2f rot(this->rotate(Vec2f(x,y))); // this is where we want to go
|
||||
|
||||
if (! m_preview_suppressed && e > 0.f && len > 0.f) {
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
change_analyzer_mm3_per_mm(len, e);
|
||||
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
|
||||
#else
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
change_analyzer_mm3_per_mm(len, e);
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
|
||||
// This is left zero if it is a travel move.
|
||||
float width = e * m_filpar[0].filament_area / (len * m_layer_height);
|
||||
// Correct for the roundings of a squished extrusion.
|
||||
|
|
@ -411,7 +459,9 @@ private:
|
|||
float m_wipe_tower_depth = 0.f;
|
||||
unsigned m_last_fan_speed = 0;
|
||||
int current_temp = -1;
|
||||
#if !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
const float m_default_analyzer_line_width;
|
||||
#endif // !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
float m_used_filament_length = 0.f;
|
||||
GCodeFlavor m_gcode_flavor;
|
||||
const std::vector<WipeTower::FilamentParameters>& m_filpar;
|
||||
|
|
@ -852,8 +902,12 @@ void WipeTower::toolchange_Unload(
|
|||
const float line_width = m_perimeter_width * m_filpar[m_current_tool].ramming_line_width_multiplicator; // desired ramming line thickness
|
||||
const float y_step = line_width * m_filpar[m_current_tool].ramming_step_multiplicator * m_extra_spacing; // spacing between lines in mm
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
writer.append("; CP TOOLCHANGE UNLOAD\n")
|
||||
.change_analyzer_line_width(line_width);
|
||||
.change_analyzer_line_width(line_width);
|
||||
#else
|
||||
writer.append("; CP TOOLCHANGE UNLOAD\n");
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
unsigned i = 0; // iterates through ramming_speed
|
||||
m_left_to_right = true; // current direction of ramming
|
||||
|
|
@ -930,7 +984,9 @@ void WipeTower::toolchange_Unload(
|
|||
}
|
||||
}
|
||||
Vec2f end_of_ramming(writer.x(),writer.y());
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
writer.change_analyzer_line_width(m_perimeter_width); // so the next lines are not affected by ramming_line_width_multiplier
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
// Retraction:
|
||||
float old_x = writer.x();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
#include "GCodeReader.hpp"
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
|
@ -113,9 +116,18 @@ void GCodeReader::update_coordinates(GCodeLine &gline, std::pair<const char*, co
|
|||
|
||||
void GCodeReader::parse_file(const std::string &file, callback_t callback)
|
||||
{
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
boost::nowide::ifstream f(file);
|
||||
#else
|
||||
std::ifstream f(file);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
std::string line;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
m_parsing_file = true;
|
||||
while (m_parsing_file && std::getline(f, line))
|
||||
#else
|
||||
while (std::getline(f, line))
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
this->parse_line(line, callback);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -107,6 +107,9 @@ public:
|
|||
{ GCodeLine gline; this->parse_line(line.c_str(), gline, callback); }
|
||||
|
||||
void parse_file(const std::string &file, callback_t callback);
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
void quit_parsing_file() { m_parsing_file = false; }
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
float& x() { return m_position[X]; }
|
||||
float x() const { return m_position[X]; }
|
||||
|
|
@ -145,6 +148,9 @@ private:
|
|||
char m_extrusion_axis;
|
||||
float m_position[NUM_AXES];
|
||||
bool m_verbose;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
bool m_parsing_file{ false };
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
};
|
||||
|
||||
} /* namespace Slic3r */
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ GCodeSender::set_baud_rate(unsigned int baud_rate)
|
|||
if (::tcsetattr(handle, TCSAFLUSH, &ios) != 0)
|
||||
printf("Failed to set baud rate: %s\n", strerror(errno));
|
||||
#else
|
||||
//throw invalid_argument ("OS does not currently support custom bauds");
|
||||
//throw Slic3r::InvalidArgument("OS does not currently support custom bauds");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include "Exception.hpp"
|
||||
#include "GCodeTimeEstimator.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include <boost/bind.hpp>
|
||||
|
|
@ -9,6 +10,8 @@
|
|||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
|
||||
static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
|
||||
static const float MILLISEC_TO_SEC = 0.001f;
|
||||
static const float INCHES_TO_MM = 25.4f;
|
||||
|
|
@ -252,13 +255,13 @@ namespace Slic3r {
|
|||
{
|
||||
boost::nowide::ifstream in(filename);
|
||||
if (!in.good())
|
||||
throw std::runtime_error(std::string("Time estimator post process export failed.\nCannot open file for reading.\n"));
|
||||
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for reading.\n"));
|
||||
|
||||
std::string path_tmp = filename + ".postprocess";
|
||||
|
||||
FILE* out = boost::nowide::fopen(path_tmp.c_str(), "wb");
|
||||
if (out == nullptr)
|
||||
throw std::runtime_error(std::string("Time estimator post process export failed.\nCannot open file for writing.\n"));
|
||||
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for writing.\n"));
|
||||
|
||||
std::string normal_time_mask = "M73 P%s R%s\n";
|
||||
std::string silent_time_mask = "M73 Q%s S%s\n";
|
||||
|
|
@ -276,7 +279,7 @@ namespace Slic3r {
|
|||
in.close();
|
||||
fclose(out);
|
||||
boost::nowide::remove(path_tmp.c_str());
|
||||
throw std::runtime_error(std::string("Time estimator post process export failed.\nIs the disk full?\n"));
|
||||
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nIs the disk full?\n"));
|
||||
}
|
||||
export_line.clear();
|
||||
};
|
||||
|
|
@ -324,7 +327,7 @@ namespace Slic3r {
|
|||
if (!in.good())
|
||||
{
|
||||
fclose(out);
|
||||
throw std::runtime_error(std::string("Time estimator post process export failed.\nError while reading from file.\n"));
|
||||
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nError while reading from file.\n"));
|
||||
}
|
||||
|
||||
// check tags
|
||||
|
|
@ -381,7 +384,7 @@ namespace Slic3r {
|
|||
in.close();
|
||||
|
||||
if (rename_file(path_tmp, filename))
|
||||
throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' +
|
||||
throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' +
|
||||
"Is " + path_tmp + " locked?" + '\n');
|
||||
|
||||
return true;
|
||||
|
|
@ -1672,3 +1675,5 @@ namespace Slic3r {
|
|||
}
|
||||
#endif // ENABLE_MOVE_STATS
|
||||
}
|
||||
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
#include "GCodeReader.hpp"
|
||||
#include "CustomGCode.hpp"
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
|
||||
#define ENABLE_MOVE_STATS 0
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
@ -481,4 +483,6 @@ namespace Slic3r {
|
|||
|
||||
} /* namespace Slic3r */
|
||||
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
#endif /* slic3r_GCodeTimeEstimator_hpp_ */
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "libslic3r.h"
|
||||
#include "Exception.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "ExPolygon.hpp"
|
||||
|
|
@ -471,7 +472,7 @@ Pointfs arrange(size_t num_parts, const Vec2d &part_size, coordf_t gap, const Bo
|
|||
size_t cellw = size_t(floor((bed_bbox.size()(0) + gap) / cell_size(0)));
|
||||
size_t cellh = size_t(floor((bed_bbox.size()(1) + gap) / cell_size(1)));
|
||||
if (num_parts > cellw * cellh)
|
||||
throw std::invalid_argument("%zu parts won't fit in your print area!\n", num_parts);
|
||||
throw Slic3r::InvalidArgument("%zu parts won't fit in your print area!\n", num_parts);
|
||||
|
||||
// Get a bounding box of cellw x cellh cells, centered at the center of the bed.
|
||||
Vec2d cells_size(cellw * cell_size(0) - gap, cellh * cell_size(1) - gap);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@ class Layer;
|
|||
class PrintRegion;
|
||||
class PrintObject;
|
||||
|
||||
namespace FillAdaptive_Internal {
|
||||
struct Octree;
|
||||
};
|
||||
|
||||
class LayerRegion
|
||||
{
|
||||
public:
|
||||
|
|
@ -134,7 +138,8 @@ public:
|
|||
return false;
|
||||
}
|
||||
void make_perimeters();
|
||||
void make_fills();
|
||||
void make_fills() { this->make_fills(nullptr, nullptr); };
|
||||
void make_fills(FillAdaptive_Internal::Octree* adaptive_fill_octree, FillAdaptive_Internal::Octree* support_fill_octree);
|
||||
void make_ironing();
|
||||
|
||||
void export_region_slices_to_svg(const char *path) const;
|
||||
|
|
|
|||
|
|
@ -33,24 +33,6 @@ bool Line::intersection_infinite(const Line &other, Point* point) const
|
|||
return true;
|
||||
}
|
||||
|
||||
// Distance to the closest point of line.
|
||||
double Line::distance_to_squared(const Point &point, const Point &a, const Point &b)
|
||||
{
|
||||
const Vec2d v = (b - a).cast<double>();
|
||||
const Vec2d va = (point - a).cast<double>();
|
||||
const double l2 = v.squaredNorm(); // avoid a sqrt
|
||||
if (l2 == 0.0)
|
||||
// a == b case
|
||||
return va.squaredNorm();
|
||||
// Consider the line extending the segment, parameterized as a + t (b - a).
|
||||
// We find projection of this point onto the line.
|
||||
// It falls where t = [(this-a) . (b-a)] / |b-a|^2
|
||||
const double t = va.dot(v) / l2;
|
||||
if (t < 0.0) return va.squaredNorm(); // beyond the 'a' end of the segment
|
||||
else if (t > 1.0) return (point - b).cast<double>().squaredNorm(); // beyond the 'b' end of the segment
|
||||
return (t * v - va).squaredNorm();
|
||||
}
|
||||
|
||||
double Line::perp_distance_to(const Point &point) const
|
||||
{
|
||||
const Line &line = *this;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,35 @@ typedef std::vector<ThickLine> ThickLines;
|
|||
|
||||
Linef3 transform(const Linef3& line, const Transform3d& t);
|
||||
|
||||
namespace line_alg {
|
||||
|
||||
// Distance to the closest point of line.
|
||||
template<class L, class T, int N>
|
||||
double distance_to_squared(const L &line, const Vec<N, T> &point)
|
||||
{
|
||||
const Vec<N, double> v = line.vector().template cast<double>();
|
||||
const Vec<N, double> va = (point - line.a).template cast<double>();
|
||||
const double l2 = v.squaredNorm(); // avoid a sqrt
|
||||
if (l2 == 0.0)
|
||||
// a == b case
|
||||
return va.squaredNorm();
|
||||
// Consider the line extending the segment, parameterized as a + t (b - a).
|
||||
// We find projection of this point onto the line.
|
||||
// It falls where t = [(this-a) . (b-a)] / |b-a|^2
|
||||
const double t = va.dot(v) / l2;
|
||||
if (t < 0.0) return va.squaredNorm(); // beyond the 'a' end of the segment
|
||||
else if (t > 1.0) return (point - line.b).template cast<double>().squaredNorm(); // beyond the 'b' end of the segment
|
||||
return (t * v - va).squaredNorm();
|
||||
}
|
||||
|
||||
template<class L, class T, int N>
|
||||
double distance_to(const L &line, const Vec<N, T> &point)
|
||||
{
|
||||
return std::sqrt(distance_to_squared(line, point));
|
||||
}
|
||||
|
||||
} // namespace line_alg
|
||||
|
||||
class Line
|
||||
{
|
||||
public:
|
||||
|
|
@ -47,7 +76,7 @@ public:
|
|||
// Clip a line with a bounding box. Returns false if the line is completely outside of the bounding box.
|
||||
bool clip_with_bbox(const BoundingBox &bbox);
|
||||
|
||||
static double distance_to_squared(const Point &point, const Point &a, const Point &b);
|
||||
static inline double distance_to_squared(const Point &point, const Point &a, const Point &b) { return line_alg::distance_to_squared(Line{a, b}, Vec<2, coord_t>{point}); }
|
||||
static double distance_to(const Point &point, const Point &a, const Point &b) { return sqrt(distance_to_squared(point, a, b)); }
|
||||
|
||||
Point a;
|
||||
|
|
|
|||
|
|
@ -114,15 +114,6 @@ template<class T> struct remove_cvref
|
|||
|
||||
template<class T> using remove_cvref_t = typename remove_cvref<T>::type;
|
||||
|
||||
template<class T, class I, class... Args> // Arbitrary allocator can be used
|
||||
inline IntegerOnly<I, std::vector<T, Args...>> reserve_vector(I capacity)
|
||||
{
|
||||
std::vector<T, Args...> ret;
|
||||
if (capacity > I(0)) ret.reserve(size_t(capacity));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html
|
||||
template<class T, class I, class = IntegerOnly<I>>
|
||||
inline std::vector<T> linspace_vector(const ArithmeticOnly<T> &start,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include "Exception.hpp"
|
||||
#include "MeshBoolean.hpp"
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
#undef PI
|
||||
|
|
@ -136,7 +137,7 @@ template<class _Mesh> void triangle_mesh_to_cgal(const TriangleMesh &M, _Mesh &o
|
|||
if(CGAL::is_closed(out))
|
||||
CGALProc::orient_to_bound_a_volume(out);
|
||||
else
|
||||
std::runtime_error("Mesh not watertight");
|
||||
throw Slic3r::RuntimeError("Mesh not watertight");
|
||||
}
|
||||
|
||||
inline Vec3d to_vec3d(const _EpicMesh::Point &v)
|
||||
|
|
@ -222,7 +223,7 @@ template<class Op> void _cgal_do(Op &&op, CGALMesh &A, CGALMesh &B)
|
|||
}
|
||||
|
||||
if (! success)
|
||||
throw std::runtime_error("CGAL mesh boolean operation failed.");
|
||||
throw Slic3r::RuntimeError("CGAL mesh boolean operation failed.");
|
||||
}
|
||||
|
||||
void minus(CGALMesh &A, CGALMesh &B) { _cgal_do(_cgal_diff, A, B); }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
#include "Exception.hpp"
|
||||
#include "Model.hpp"
|
||||
#include "ModelArrange.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "MTUtils.hpp"
|
||||
#include "TriangleSelector.hpp"
|
||||
|
||||
#include "Format/AMF.hpp"
|
||||
#include "Format/OBJ.hpp"
|
||||
|
|
@ -20,7 +22,9 @@
|
|||
#include "SVG.hpp"
|
||||
#include <Eigen/Dense>
|
||||
#include "GCodeWriter.hpp"
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
#include "GCode/PreviewData.hpp"
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
|
@ -113,13 +117,13 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
|
|||
else if (boost::algorithm::iends_with(input_file, ".prusa"))
|
||||
result = load_prus(input_file.c_str(), &model);
|
||||
else
|
||||
throw std::runtime_error("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension.");
|
||||
throw Slic3r::RuntimeError("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension.");
|
||||
|
||||
if (! result)
|
||||
throw std::runtime_error("Loading of a model file failed.");
|
||||
throw Slic3r::RuntimeError("Loading of a model file failed.");
|
||||
|
||||
if (model.objects.empty())
|
||||
throw std::runtime_error("The supplied file couldn't be read because it's empty");
|
||||
throw Slic3r::RuntimeError("The supplied file couldn't be read because it's empty");
|
||||
|
||||
for (ModelObject *o : model.objects)
|
||||
o->input_file = input_file;
|
||||
|
|
@ -143,13 +147,13 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
|
|||
else if (boost::algorithm::iends_with(input_file, ".zip.amf"))
|
||||
result = load_amf(input_file.c_str(), config, &model, check_version);
|
||||
else
|
||||
throw std::runtime_error("Unknown file format. Input file must have .3mf or .zip.amf extension.");
|
||||
throw Slic3r::RuntimeError("Unknown file format. Input file must have .3mf or .zip.amf extension.");
|
||||
|
||||
if (!result)
|
||||
throw std::runtime_error("Loading of a model file failed.");
|
||||
throw Slic3r::RuntimeError("Loading of a model file failed.");
|
||||
|
||||
if (model.objects.empty())
|
||||
throw std::runtime_error("The supplied file couldn't be read because it's empty");
|
||||
throw Slic3r::RuntimeError("The supplied file couldn't be read because it's empty");
|
||||
|
||||
for (ModelObject *o : model.objects)
|
||||
{
|
||||
|
|
@ -814,7 +818,7 @@ const BoundingBoxf3& ModelObject::raw_bounding_box() const
|
|||
m_raw_bounding_box_valid = true;
|
||||
m_raw_bounding_box.reset();
|
||||
if (this->instances.empty())
|
||||
throw std::invalid_argument("Can't call raw_bounding_box() with no instances");
|
||||
throw Slic3r::InvalidArgument("Can't call raw_bounding_box() with no instances");
|
||||
|
||||
const Transform3d& inst_matrix = this->instances.front()->get_transformation().get_matrix(true);
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
|
|
@ -1006,6 +1010,7 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, bool from_imperial
|
|||
for (ModelVolume* volume : volumes)
|
||||
{
|
||||
volume->m_supported_facets.clear();
|
||||
volume->m_seam_facets.clear();
|
||||
if (!volume->mesh().empty()) {
|
||||
TriangleMesh mesh(volume->mesh());
|
||||
mesh.require_shared_vertices();
|
||||
|
|
@ -1111,6 +1116,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
|
|||
const auto volume_matrix = volume->get_matrix();
|
||||
|
||||
volume->m_supported_facets.clear();
|
||||
volume->m_seam_facets.clear();
|
||||
|
||||
if (! volume->is_model_part()) {
|
||||
// Modifiers are not cut, but we still need to add the instance transformation
|
||||
|
|
@ -1830,28 +1836,25 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
|
|||
}
|
||||
|
||||
|
||||
std::vector<int> FacetsAnnotation::get_facets(FacetSupportType type) const
|
||||
indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const
|
||||
{
|
||||
std::vector<int> out;
|
||||
for (auto& [facet_idx, this_type] : m_data)
|
||||
if (this_type == type)
|
||||
out.push_back(facet_idx);
|
||||
TriangleSelector selector(mv.mesh());
|
||||
selector.deserialize(m_data);
|
||||
indexed_triangle_set out = selector.get_facets(type);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FacetsAnnotation::set_facet(int idx, FacetSupportType type)
|
||||
bool FacetsAnnotation::set(const TriangleSelector& selector)
|
||||
{
|
||||
bool changed = true;
|
||||
|
||||
if (type == FacetSupportType::NONE)
|
||||
changed = m_data.erase(idx) != 0;
|
||||
else
|
||||
m_data[idx] = type;
|
||||
|
||||
if (changed)
|
||||
std::map<int, std::vector<bool>> sel_map = selector.serialize();
|
||||
if (sel_map != m_data) {
|
||||
m_data = sel_map;
|
||||
update_timestamp();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1864,6 +1867,64 @@ void FacetsAnnotation::clear()
|
|||
|
||||
|
||||
|
||||
// Following function takes data from a triangle and encodes it as string
|
||||
// of hexadecimal numbers (one digit per triangle). Used for 3MF export,
|
||||
// changing it may break backwards compatibility !!!!!
|
||||
std::string FacetsAnnotation::get_triangle_as_string(int triangle_idx) const
|
||||
{
|
||||
std::string out;
|
||||
|
||||
auto triangle_it = m_data.find(triangle_idx);
|
||||
if (triangle_it != m_data.end()) {
|
||||
const std::vector<bool>& code = triangle_it->second;
|
||||
int offset = 0;
|
||||
while (offset < int(code.size())) {
|
||||
int next_code = 0;
|
||||
for (int i=3; i>=0; --i) {
|
||||
next_code = next_code << 1;
|
||||
next_code |= int(code[offset + i]);
|
||||
}
|
||||
offset += 4;
|
||||
|
||||
assert(next_code >=0 && next_code <= 15);
|
||||
char digit = next_code < 10 ? next_code + '0' : (next_code-10)+'A';
|
||||
out.insert(out.begin(), digit);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Recover triangle splitting & state from string of hexadecimal values previously
|
||||
// generated by get_triangle_as_string. Used to load from 3MF.
|
||||
void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::string& str)
|
||||
{
|
||||
assert(! str.empty());
|
||||
m_data[triangle_id] = std::vector<bool>(); // zero current state or create new
|
||||
std::vector<bool>& code = m_data[triangle_id];
|
||||
|
||||
for (auto it = str.crbegin(); it != str.crend(); ++it) {
|
||||
const char ch = *it;
|
||||
int dec = 0;
|
||||
if (ch >= '0' && ch<='9')
|
||||
dec = int(ch - '0');
|
||||
else if (ch >='A' && ch <= 'F')
|
||||
dec = 10 + int(ch - 'A');
|
||||
else
|
||||
assert(false);
|
||||
|
||||
// Convert to binary and append into code.
|
||||
for (int i=0; i<4; ++i) {
|
||||
code.insert(code.end(), bool(dec & (1 << i)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
|
||||
// ordered in the same order. In that case it is not necessary to kill the background processing.
|
||||
bool model_object_list_equal(const Model &model_old, const Model &model_new)
|
||||
|
|
@ -1935,7 +1996,17 @@ bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo_new) {
|
||||
assert(! model_volume_list_changed(mo, mo_new, ModelVolumeType::MODEL_PART));
|
||||
assert(mo.volumes.size() == mo_new.volumes.size());
|
||||
for (size_t i=0; i<mo.volumes.size(); ++i) {
|
||||
if (! mo_new.volumes[i]->m_seam_facets.is_same_as(mo.volumes[i]->m_seam_facets))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
extern bool model_has_multi_part_objects(const Model &model)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ class ModelVolume;
|
|||
class ModelWipeTower;
|
||||
class Print;
|
||||
class SLAPrint;
|
||||
class TriangleSelector;
|
||||
|
||||
namespace UndoRedo {
|
||||
class StackImpl;
|
||||
|
|
@ -393,7 +394,8 @@ enum class ModelVolumeType : int {
|
|||
SUPPORT_BLOCKER,
|
||||
};
|
||||
|
||||
enum class FacetSupportType : int8_t {
|
||||
enum class EnforcerBlockerType : int8_t {
|
||||
// Maximum is 3. The value is serialized in TriangleSelector into 2 bits!
|
||||
NONE = 0,
|
||||
ENFORCER = 1,
|
||||
BLOCKER = 2
|
||||
|
|
@ -403,9 +405,12 @@ class FacetsAnnotation {
|
|||
public:
|
||||
using ClockType = std::chrono::steady_clock;
|
||||
|
||||
std::vector<int> get_facets(FacetSupportType type) const;
|
||||
void set_facet(int idx, FacetSupportType type);
|
||||
const std::map<int, std::vector<bool>>& get_data() const { return m_data; }
|
||||
bool set(const TriangleSelector& selector);
|
||||
indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
|
||||
void clear();
|
||||
std::string get_triangle_as_string(int i) const;
|
||||
void set_triangle_from_string(int triangle_id, const std::string& str);
|
||||
|
||||
ClockType::time_point get_timestamp() const { return timestamp; }
|
||||
bool is_same_as(const FacetsAnnotation& other) const {
|
||||
|
|
@ -418,7 +423,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
std::map<int, FacetSupportType> m_data;
|
||||
std::map<int, std::vector<bool>> m_data;
|
||||
|
||||
ClockType::time_point timestamp;
|
||||
void update_timestamp() {
|
||||
|
|
@ -459,6 +464,9 @@ public:
|
|||
// List of mesh facets to be supported/unsupported.
|
||||
FacetsAnnotation m_supported_facets;
|
||||
|
||||
// List of seam enforcers/blockers.
|
||||
FacetsAnnotation m_seam_facets;
|
||||
|
||||
// A parent object owning this modifier volume.
|
||||
ModelObject* get_object() const { return this->object; }
|
||||
ModelVolumeType type() const { return m_type; }
|
||||
|
|
@ -588,7 +596,7 @@ private:
|
|||
ObjectBase(other),
|
||||
name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull),
|
||||
config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation),
|
||||
m_supported_facets(other.m_supported_facets)
|
||||
m_supported_facets(other.m_supported_facets), m_seam_facets(other.m_seam_facets)
|
||||
{
|
||||
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
|
||||
assert(this->id() == other.id() && this->config.id() == other.config.id());
|
||||
|
|
@ -607,6 +615,7 @@ private:
|
|||
assert(this->config.id().valid()); assert(this->config.id() != other.config.id()); assert(this->id() != this->config.id());
|
||||
|
||||
m_supported_facets.clear();
|
||||
m_seam_facets.clear();
|
||||
}
|
||||
|
||||
ModelVolume& operator=(ModelVolume &rhs) = delete;
|
||||
|
|
@ -620,7 +629,7 @@ private:
|
|||
template<class Archive> void load(Archive &ar) {
|
||||
bool has_convex_hull;
|
||||
ar(name, source, m_mesh, m_type, m_material_id, m_transformation,
|
||||
m_is_splittable, has_convex_hull, m_supported_facets);
|
||||
m_is_splittable, has_convex_hull, m_supported_facets, m_seam_facets);
|
||||
cereal::load_by_value(ar, config);
|
||||
assert(m_mesh);
|
||||
if (has_convex_hull) {
|
||||
|
|
@ -634,7 +643,7 @@ private:
|
|||
template<class Archive> void save(Archive &ar) const {
|
||||
bool has_convex_hull = m_convex_hull.get() != nullptr;
|
||||
ar(name, source, m_mesh, m_type, m_material_id, m_transformation,
|
||||
m_is_splittable, has_convex_hull, m_supported_facets);
|
||||
m_is_splittable, has_convex_hull, m_supported_facets, m_seam_facets);
|
||||
cereal::save_by_value(ar, config);
|
||||
if (has_convex_hull)
|
||||
cereal::save_optional(ar, m_convex_hull);
|
||||
|
|
@ -899,6 +908,10 @@ extern bool model_volume_list_changed(const ModelObject &model_object_old, const
|
|||
// The function assumes that volumes list is synchronized.
|
||||
extern bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject& mo_new);
|
||||
|
||||
// Test whether the now ModelObject has newer custom seam data than the old one.
|
||||
// The function assumes that volumes list is synchronized.
|
||||
extern bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo_new);
|
||||
|
||||
// If the model has multi-part objects, then it is currently not supported by the SLA mode.
|
||||
// Either the model cannot be loaded, or a SLA printer has to be activated.
|
||||
extern bool model_has_multi_part_objects(const Model &model);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ using VirtualBedFn = std::function<void(arrangement::ArrangePolygon&)>;
|
|||
|
||||
[[noreturn]] inline void throw_if_out_of_bed(arrangement::ArrangePolygon&)
|
||||
{
|
||||
throw std::runtime_error("Objects could not fit on the bed");
|
||||
throw Slic3r::RuntimeError("Objects could not fit on the bed");
|
||||
}
|
||||
|
||||
ArrangePolygons get_arrange_polys(const Model &model, ModelInstancePtrs &instances);
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ private:
|
|||
|
||||
// Base for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial to provide a unique ID
|
||||
// to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject).
|
||||
// Also base for Print, PrintObject, SLAPrint, SLAPrintObject to provide a unique ID for matching Model / ModelObject
|
||||
// with their corresponding Print / PrintObject objects by the notification center at the UI when processing back-end warnings.
|
||||
// Achtung! The s_last_id counter is not thread safe, so it is expected, that the ObjectBase derived instances
|
||||
// are only instantiated from the main thread.
|
||||
class ObjectBase
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "OpenVDBUtils.hpp"
|
||||
#include <openvdb/tools/MeshToVolume.h>
|
||||
#include <openvdb/tools/VolumeToMesh.h>
|
||||
#include <openvdb/tools/Composite.h>
|
||||
#include <openvdb/tools/LevelSetRebuild.h>
|
||||
|
||||
//#include "MTUtils.hpp"
|
||||
|
|
@ -57,17 +58,42 @@ void Contour3DDataAdapter::getIndexSpacePoint(size_t n,
|
|||
// TODO: Do I need to call initialize? Seems to work without it as well but the
|
||||
// docs say it should be called ones. It does a mutex lock-unlock sequence all
|
||||
// even if was called previously.
|
||||
|
||||
openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh &mesh,
|
||||
const openvdb::math::Transform &tr,
|
||||
float exteriorBandWidth,
|
||||
float interiorBandWidth,
|
||||
int flags)
|
||||
{
|
||||
// openvdb::initialize();
|
||||
// return openvdb::tools::meshToVolume<openvdb::FloatGrid>(
|
||||
// TriangleMeshDataAdapter{mesh}, tr, exteriorBandWidth,
|
||||
// interiorBandWidth, flags);
|
||||
|
||||
openvdb::initialize();
|
||||
return openvdb::tools::meshToVolume<openvdb::FloatGrid>(
|
||||
TriangleMeshDataAdapter{mesh}, tr, exteriorBandWidth,
|
||||
interiorBandWidth, flags);
|
||||
|
||||
TriangleMeshPtrs meshparts = mesh.split();
|
||||
|
||||
auto it = std::remove_if(meshparts.begin(), meshparts.end(),
|
||||
[](TriangleMesh *m){
|
||||
m->require_shared_vertices();
|
||||
return !m->is_manifold() || m->volume() < EPSILON;
|
||||
});
|
||||
|
||||
meshparts.erase(it, meshparts.end());
|
||||
|
||||
openvdb::FloatGrid::Ptr grid;
|
||||
for (TriangleMesh *m : meshparts) {
|
||||
auto gridptr = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
|
||||
TriangleMeshDataAdapter{*m}, tr, exteriorBandWidth,
|
||||
interiorBandWidth, flags);
|
||||
|
||||
if (grid && gridptr) openvdb::tools::csgUnion(*grid, *gridptr);
|
||||
else if (gridptr) grid = std::move(gridptr);
|
||||
}
|
||||
|
||||
grid = openvdb::tools::levelSetRebuild(*grid, 0., exteriorBandWidth, interiorBandWidth);
|
||||
|
||||
return grid;
|
||||
}
|
||||
|
||||
openvdb::FloatGrid::Ptr mesh_to_grid(const sla::Contour3D &mesh,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
#define OPENVDBUTILS_HPP
|
||||
|
||||
#include <libslic3r/TriangleMesh.hpp>
|
||||
#include <libslic3r/SLA/Common.hpp>
|
||||
#include <libslic3r/SLA/Contour3D.hpp>
|
||||
#include <openvdb/openvdb.h>
|
||||
|
||||
|
|
|
|||
140
src/libslic3r/Optimize/BruteforceOptimizer.hpp
Normal file
140
src/libslic3r/Optimize/BruteforceOptimizer.hpp
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
#ifndef BRUTEFORCEOPTIMIZER_HPP
|
||||
#define BRUTEFORCEOPTIMIZER_HPP
|
||||
|
||||
#include <libslic3r/Optimize/Optimizer.hpp>
|
||||
|
||||
namespace Slic3r { namespace opt {
|
||||
|
||||
namespace detail {
|
||||
// Implementing a bruteforce optimizer
|
||||
|
||||
// Return the number of iterations needed to reach a specific grid position (idx)
|
||||
template<size_t N>
|
||||
long num_iter(const std::array<size_t, N> &idx, size_t gridsz)
|
||||
{
|
||||
long ret = 0;
|
||||
for (size_t i = 0; i < N; ++i) ret += idx[i] * std::pow(gridsz, i);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Implementation of a grid search where the search interval is sampled in
|
||||
// equidistant points for each dimension. Grid size determines the number of
|
||||
// samples for one dimension so the number of function calls is gridsize ^ dimension.
|
||||
struct AlgBurteForce {
|
||||
bool to_min;
|
||||
StopCriteria stc;
|
||||
size_t gridsz;
|
||||
|
||||
AlgBurteForce(const StopCriteria &cr, size_t gs): stc{cr}, gridsz{gs} {}
|
||||
|
||||
// This function is called recursively for each dimension and generates
|
||||
// the grid values for the particular dimension. If D is less than zero,
|
||||
// the object function input values are generated for each dimension and it
|
||||
// can be evaluated. The current best score is compared with the newly
|
||||
// returned score and changed appropriately.
|
||||
template<int D, size_t N, class Fn, class Cmp>
|
||||
bool run(std::array<size_t, N> &idx,
|
||||
Result<N> &result,
|
||||
const Bounds<N> &bounds,
|
||||
Fn &&fn,
|
||||
Cmp &&cmp)
|
||||
{
|
||||
if (stc.stop_condition()) return false;
|
||||
|
||||
if constexpr (D < 0) { // Let's evaluate fn
|
||||
Input<N> inp;
|
||||
|
||||
auto max_iter = stc.max_iterations();
|
||||
if (max_iter && num_iter(idx, gridsz) >= max_iter)
|
||||
return false;
|
||||
|
||||
for (size_t d = 0; d < N; ++d) {
|
||||
const Bound &b = bounds[d];
|
||||
double step = (b.max() - b.min()) / (gridsz - 1);
|
||||
inp[d] = b.min() + idx[d] * step;
|
||||
}
|
||||
|
||||
auto score = fn(inp);
|
||||
if (cmp(score, result.score)) { // Change current score to the new
|
||||
double absdiff = std::abs(score - result.score);
|
||||
|
||||
result.score = score;
|
||||
result.optimum = inp;
|
||||
|
||||
// Check if the required precision is reached.
|
||||
if (absdiff < stc.abs_score_diff() ||
|
||||
absdiff < stc.rel_score_diff() * std::abs(score))
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
for (size_t i = 0; i < gridsz; ++i) {
|
||||
idx[D] = i; // Mark the current grid position and dig down
|
||||
if (!run<D - 1>(idx, result, bounds, std::forward<Fn>(fn),
|
||||
std::forward<Cmp>(cmp)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class Fn, size_t N>
|
||||
Result<N> optimize(Fn&& fn,
|
||||
const Input<N> &/*initvals*/,
|
||||
const Bounds<N>& bounds)
|
||||
{
|
||||
std::array<size_t, N> idx = {};
|
||||
Result<N> result;
|
||||
|
||||
if (to_min) {
|
||||
result.score = std::numeric_limits<double>::max();
|
||||
run<int(N) - 1>(idx, result, bounds, std::forward<Fn>(fn),
|
||||
std::less<double>{});
|
||||
}
|
||||
else {
|
||||
result.score = std::numeric_limits<double>::lowest();
|
||||
run<int(N) - 1>(idx, result, bounds, std::forward<Fn>(fn),
|
||||
std::greater<double>{});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
using AlgBruteForce = detail::AlgBurteForce;
|
||||
|
||||
template<>
|
||||
class Optimizer<AlgBruteForce> {
|
||||
AlgBruteForce m_alg;
|
||||
|
||||
public:
|
||||
|
||||
Optimizer(const StopCriteria &cr = {}, size_t gridsz = 100)
|
||||
: m_alg{cr, gridsz}
|
||||
{}
|
||||
|
||||
Optimizer& to_max() { m_alg.to_min = false; return *this; }
|
||||
Optimizer& to_min() { m_alg.to_min = true; return *this; }
|
||||
|
||||
template<class Func, size_t N>
|
||||
Result<N> optimize(Func&& func,
|
||||
const Input<N> &initvals,
|
||||
const Bounds<N>& bounds)
|
||||
{
|
||||
return m_alg.optimize(std::forward<Func>(func), initvals, bounds);
|
||||
}
|
||||
|
||||
Optimizer &set_criteria(const StopCriteria &cr)
|
||||
{
|
||||
m_alg.stc = cr; return *this;
|
||||
}
|
||||
|
||||
const StopCriteria &get_criteria() const { return m_alg.stc; }
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::opt
|
||||
|
||||
#endif // BRUTEFORCEOPTIMIZER_HPP
|
||||
233
src/libslic3r/Optimize/NLoptOptimizer.hpp
Normal file
233
src/libslic3r/Optimize/NLoptOptimizer.hpp
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
#ifndef NLOPTOPTIMIZER_HPP
|
||||
#define NLOPTOPTIMIZER_HPP
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4244)
|
||||
#pragma warning(disable: 4267)
|
||||
#endif
|
||||
#include <nlopt.h>
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <libslic3r/Optimize/Optimizer.hpp>
|
||||
|
||||
namespace Slic3r { namespace opt {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Helper types for NLopt algorithm selection in template contexts
|
||||
template<nlopt_algorithm alg> struct NLoptAlg {};
|
||||
|
||||
// NLopt can combine multiple algorithms if one is global an other is a local
|
||||
// method. This is how template specializations can be informed about this fact.
|
||||
template<nlopt_algorithm gl_alg, nlopt_algorithm lc_alg = NLOPT_LN_NELDERMEAD>
|
||||
struct NLoptAlgComb {};
|
||||
|
||||
template<class M> struct IsNLoptAlg {
|
||||
static const constexpr bool value = false;
|
||||
};
|
||||
|
||||
template<nlopt_algorithm a> struct IsNLoptAlg<NLoptAlg<a>> {
|
||||
static const constexpr bool value = true;
|
||||
};
|
||||
|
||||
template<nlopt_algorithm a1, nlopt_algorithm a2>
|
||||
struct IsNLoptAlg<NLoptAlgComb<a1, a2>> {
|
||||
static const constexpr bool value = true;
|
||||
};
|
||||
|
||||
template<class M, class T = void>
|
||||
using NLoptOnly = std::enable_if_t<IsNLoptAlg<M>::value, T>;
|
||||
|
||||
|
||||
enum class OptDir { MIN, MAX }; // Where to optimize
|
||||
|
||||
struct NLopt { // Helper RAII class for nlopt_opt
|
||||
nlopt_opt ptr = nullptr;
|
||||
|
||||
template<class...A> explicit NLopt(A&&...a)
|
||||
{
|
||||
ptr = nlopt_create(std::forward<A>(a)...);
|
||||
}
|
||||
|
||||
NLopt(const NLopt&) = delete;
|
||||
NLopt(NLopt&&) = delete;
|
||||
NLopt& operator=(const NLopt&) = delete;
|
||||
NLopt& operator=(NLopt&&) = delete;
|
||||
|
||||
~NLopt() { nlopt_destroy(ptr); }
|
||||
};
|
||||
|
||||
template<class Method> class NLoptOpt {};
|
||||
|
||||
// Optimizers based on NLopt.
|
||||
template<nlopt_algorithm alg> class NLoptOpt<NLoptAlg<alg>> {
|
||||
protected:
|
||||
StopCriteria m_stopcr;
|
||||
OptDir m_dir;
|
||||
|
||||
template<class Fn> using TOptData =
|
||||
std::tuple<std::remove_reference_t<Fn>*, NLoptOpt*, nlopt_opt>;
|
||||
|
||||
template<class Fn, size_t N>
|
||||
static double optfunc(unsigned n, const double *params,
|
||||
double *gradient,
|
||||
void *data)
|
||||
{
|
||||
assert(n >= N);
|
||||
|
||||
auto tdata = static_cast<TOptData<Fn>*>(data);
|
||||
|
||||
if (std::get<1>(*tdata)->m_stopcr.stop_condition())
|
||||
nlopt_force_stop(std::get<2>(*tdata));
|
||||
|
||||
auto fnptr = std::get<0>(*tdata);
|
||||
auto funval = to_arr<N>(params);
|
||||
|
||||
double scoreval = 0.;
|
||||
using RetT = decltype((*fnptr)(funval));
|
||||
if constexpr (std::is_convertible_v<RetT, ScoreGradient<N>>) {
|
||||
ScoreGradient<N> score = (*fnptr)(funval);
|
||||
for (size_t i = 0; i < n; ++i) gradient[i] = (*score.gradient)[i];
|
||||
scoreval = score.score;
|
||||
} else {
|
||||
scoreval = (*fnptr)(funval);
|
||||
}
|
||||
|
||||
return scoreval;
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
void set_up(NLopt &nl, const Bounds<N>& bounds)
|
||||
{
|
||||
std::array<double, N> lb, ub;
|
||||
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
lb[i] = bounds[i].min();
|
||||
ub[i] = bounds[i].max();
|
||||
}
|
||||
|
||||
nlopt_set_lower_bounds(nl.ptr, lb.data());
|
||||
nlopt_set_upper_bounds(nl.ptr, ub.data());
|
||||
|
||||
double abs_diff = m_stopcr.abs_score_diff();
|
||||
double rel_diff = m_stopcr.rel_score_diff();
|
||||
double stopval = m_stopcr.stop_score();
|
||||
if(!std::isnan(abs_diff)) nlopt_set_ftol_abs(nl.ptr, abs_diff);
|
||||
if(!std::isnan(rel_diff)) nlopt_set_ftol_rel(nl.ptr, rel_diff);
|
||||
if(!std::isnan(stopval)) nlopt_set_stopval(nl.ptr, stopval);
|
||||
|
||||
if(this->m_stopcr.max_iterations() > 0)
|
||||
nlopt_set_maxeval(nl.ptr, this->m_stopcr.max_iterations());
|
||||
}
|
||||
|
||||
template<class Fn, size_t N>
|
||||
Result<N> optimize(NLopt &nl, Fn &&fn, const Input<N> &initvals)
|
||||
{
|
||||
Result<N> r;
|
||||
|
||||
TOptData<Fn> data = std::make_tuple(&fn, this, nl.ptr);
|
||||
|
||||
switch(m_dir) {
|
||||
case OptDir::MIN:
|
||||
nlopt_set_min_objective(nl.ptr, optfunc<Fn, N>, &data); break;
|
||||
case OptDir::MAX:
|
||||
nlopt_set_max_objective(nl.ptr, optfunc<Fn, N>, &data); break;
|
||||
}
|
||||
|
||||
r.optimum = initvals;
|
||||
r.resultcode = nlopt_optimize(nl.ptr, r.optimum.data(), &r.score);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
template<class Func, size_t N>
|
||||
Result<N> optimize(Func&& func,
|
||||
const Input<N> &initvals,
|
||||
const Bounds<N>& bounds)
|
||||
{
|
||||
NLopt nl{alg, N};
|
||||
set_up(nl, bounds);
|
||||
|
||||
return optimize(nl, std::forward<Func>(func), initvals);
|
||||
}
|
||||
|
||||
explicit NLoptOpt(StopCriteria stopcr = {}) : m_stopcr(stopcr) {}
|
||||
|
||||
void set_criteria(const StopCriteria &cr) { m_stopcr = cr; }
|
||||
const StopCriteria &get_criteria() const noexcept { return m_stopcr; }
|
||||
void set_dir(OptDir dir) noexcept { m_dir = dir; }
|
||||
|
||||
void seed(long s) { nlopt_srand(s); }
|
||||
};
|
||||
|
||||
template<nlopt_algorithm glob, nlopt_algorithm loc>
|
||||
class NLoptOpt<NLoptAlgComb<glob, loc>>: public NLoptOpt<NLoptAlg<glob>>
|
||||
{
|
||||
using Base = NLoptOpt<NLoptAlg<glob>>;
|
||||
public:
|
||||
|
||||
template<class Fn, size_t N>
|
||||
Result<N> optimize(Fn&& f,
|
||||
const Input<N> &initvals,
|
||||
const Bounds<N>& bounds)
|
||||
{
|
||||
NLopt nl_glob{glob, N}, nl_loc{loc, N};
|
||||
|
||||
Base::set_up(nl_glob, bounds);
|
||||
Base::set_up(nl_loc, bounds);
|
||||
nlopt_set_local_optimizer(nl_glob.ptr, nl_loc.ptr);
|
||||
|
||||
return Base::optimize(nl_glob, std::forward<Fn>(f), initvals);
|
||||
}
|
||||
|
||||
explicit NLoptOpt(StopCriteria stopcr = {}) : Base{stopcr} {}
|
||||
};
|
||||
|
||||
} // namespace detail;
|
||||
|
||||
// Optimizers based on NLopt.
|
||||
template<class M> class Optimizer<M, detail::NLoptOnly<M>> {
|
||||
detail::NLoptOpt<M> m_opt;
|
||||
|
||||
public:
|
||||
|
||||
Optimizer& to_max() { m_opt.set_dir(detail::OptDir::MAX); return *this; }
|
||||
Optimizer& to_min() { m_opt.set_dir(detail::OptDir::MIN); return *this; }
|
||||
|
||||
template<class Func, size_t N>
|
||||
Result<N> optimize(Func&& func,
|
||||
const Input<N> &initvals,
|
||||
const Bounds<N>& bounds)
|
||||
{
|
||||
return m_opt.optimize(std::forward<Func>(func), initvals, bounds);
|
||||
}
|
||||
|
||||
explicit Optimizer(StopCriteria stopcr = {}) : m_opt(stopcr) {}
|
||||
|
||||
Optimizer &set_criteria(const StopCriteria &cr)
|
||||
{
|
||||
m_opt.set_criteria(cr); return *this;
|
||||
}
|
||||
|
||||
const StopCriteria &get_criteria() const { return m_opt.get_criteria(); }
|
||||
|
||||
void seed(long s) { m_opt.seed(s); }
|
||||
};
|
||||
|
||||
// Predefinded NLopt algorithms
|
||||
using AlgNLoptGenetic = detail::NLoptAlgComb<NLOPT_GN_ESCH>;
|
||||
using AlgNLoptSubplex = detail::NLoptAlg<NLOPT_LN_SBPLX>;
|
||||
using AlgNLoptSimplex = detail::NLoptAlg<NLOPT_LN_NELDERMEAD>;
|
||||
using AlgNLoptDIRECT = detail::NLoptAlg<NLOPT_GN_DIRECT>;
|
||||
using AlgNLoptMLSL = detail::NLoptAlg<NLOPT_GN_MLSL>;
|
||||
|
||||
}} // namespace Slic3r::opt
|
||||
|
||||
#endif // NLOPTOPTIMIZER_HPP
|
||||
182
src/libslic3r/Optimize/Optimizer.hpp
Normal file
182
src/libslic3r/Optimize/Optimizer.hpp
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
#ifndef OPTIMIZER_HPP
|
||||
#define OPTIMIZER_HPP
|
||||
|
||||
#include <utility>
|
||||
#include <tuple>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <cassert>
|
||||
|
||||
namespace Slic3r { namespace opt {
|
||||
|
||||
// A type to hold the complete result of the optimization.
|
||||
template<size_t N> struct Result {
|
||||
int resultcode; // Method dependent
|
||||
std::array<double, N> optimum;
|
||||
double score;
|
||||
};
|
||||
|
||||
// An interval of possible input values for optimization
|
||||
class Bound {
|
||||
double m_min, m_max;
|
||||
|
||||
public:
|
||||
Bound(double min = std::numeric_limits<double>::min(),
|
||||
double max = std::numeric_limits<double>::max())
|
||||
: m_min(min), m_max(max)
|
||||
{}
|
||||
|
||||
double min() const noexcept { return m_min; }
|
||||
double max() const noexcept { return m_max; }
|
||||
};
|
||||
|
||||
// Helper types for optimization function input and bounds
|
||||
template<size_t N> using Input = std::array<double, N>;
|
||||
template<size_t N> using Bounds = std::array<Bound, N>;
|
||||
|
||||
// A type for specifying the stop criteria. Setter methods can be concatenated
|
||||
class StopCriteria {
|
||||
|
||||
// If the absolute value difference between two scores.
|
||||
double m_abs_score_diff = std::nan("");
|
||||
|
||||
// If the relative value difference between two scores.
|
||||
double m_rel_score_diff = std::nan("");
|
||||
|
||||
// Stop if this value or better is found.
|
||||
double m_stop_score = std::nan("");
|
||||
|
||||
// A predicate that if evaluates to true, the optimization should terminate
|
||||
// and the best result found prior to termination should be returned.
|
||||
std::function<bool()> m_stop_condition = [] { return false; };
|
||||
|
||||
// The max allowed number of iterations.
|
||||
unsigned m_max_iterations = 0;
|
||||
|
||||
public:
|
||||
|
||||
StopCriteria & abs_score_diff(double val)
|
||||
{
|
||||
m_abs_score_diff = val; return *this;
|
||||
}
|
||||
|
||||
double abs_score_diff() const { return m_abs_score_diff; }
|
||||
|
||||
StopCriteria & rel_score_diff(double val)
|
||||
{
|
||||
m_rel_score_diff = val; return *this;
|
||||
}
|
||||
|
||||
double rel_score_diff() const { return m_rel_score_diff; }
|
||||
|
||||
StopCriteria & stop_score(double val)
|
||||
{
|
||||
m_stop_score = val; return *this;
|
||||
}
|
||||
|
||||
double stop_score() const { return m_stop_score; }
|
||||
|
||||
StopCriteria & max_iterations(double val)
|
||||
{
|
||||
m_max_iterations = val; return *this;
|
||||
}
|
||||
|
||||
double max_iterations() const { return m_max_iterations; }
|
||||
|
||||
template<class Fn> StopCriteria & stop_condition(Fn &&cond)
|
||||
{
|
||||
m_stop_condition = cond; return *this;
|
||||
}
|
||||
|
||||
bool stop_condition() { return m_stop_condition(); }
|
||||
};
|
||||
|
||||
// Helper class to use optimization methods involving gradient.
|
||||
template<size_t N> struct ScoreGradient {
|
||||
double score;
|
||||
std::optional<std::array<double, N>> gradient;
|
||||
|
||||
ScoreGradient(double s, const std::array<double, N> &grad)
|
||||
: score{s}, gradient{grad}
|
||||
{}
|
||||
};
|
||||
|
||||
// Helper to be used in static_assert.
|
||||
template<class T> struct always_false { enum { value = false }; };
|
||||
|
||||
// Basic interface to optimizer object
|
||||
template<class Method, class Enable = void> class Optimizer {
|
||||
public:
|
||||
|
||||
Optimizer(const StopCriteria &)
|
||||
{
|
||||
static_assert (always_false<Method>::value,
|
||||
"Optimizer unimplemented for given method!");
|
||||
}
|
||||
|
||||
// Switch optimization towards function minimum
|
||||
Optimizer &to_min() { return *this; }
|
||||
|
||||
// Switch optimization towards function maximum
|
||||
Optimizer &to_max() { return *this; }
|
||||
|
||||
// Set criteria for successive optimizations
|
||||
Optimizer &set_criteria(const StopCriteria &) { return *this; }
|
||||
|
||||
// Get current criteria
|
||||
StopCriteria get_criteria() const { return {}; };
|
||||
|
||||
// Find function minimum or maximum for Func which has has signature:
|
||||
// double(const Input<N> &input) and input with dimension N
|
||||
//
|
||||
// Initial starting point can be given as the second parameter.
|
||||
//
|
||||
// For each dimension an interval (Bound) has to be given marking the bounds
|
||||
// for that dimension.
|
||||
//
|
||||
// initvals have to be within the specified bounds, otherwise its undefined
|
||||
// behavior.
|
||||
//
|
||||
// Func can return a score of type double or optionally a ScoreGradient
|
||||
// class to indicate the function gradient for a optimization methods that
|
||||
// make use of the gradient.
|
||||
template<class Func, size_t N>
|
||||
Result<N> optimize(Func&& /*func*/,
|
||||
const Input<N> &/*initvals*/,
|
||||
const Bounds<N>& /*bounds*/) { return {}; }
|
||||
|
||||
// optional for randomized methods:
|
||||
void seed(long /*s*/) {}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Helper to convert C style array to std::array. The copy should be optimized
|
||||
// away with modern compilers.
|
||||
template<size_t N, class T> auto to_arr(const T *a)
|
||||
{
|
||||
std::array<T, N> r;
|
||||
std::copy(a, a + N, std::begin(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
template<size_t N, class T> auto to_arr(const T (&a) [N])
|
||||
{
|
||||
return to_arr<N>(static_cast<const T *>(a));
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// Helper functions to create bounds, initial value
|
||||
template<size_t N> Bounds<N> bounds(const Bound (&b) [N]) { return detail::to_arr(b); }
|
||||
template<size_t N> Input<N> initvals(const double (&a) [N]) { return detail::to_arr(a); }
|
||||
template<size_t N> auto score_gradient(double s, const double (&grad)[N])
|
||||
{
|
||||
return ScoreGradient<N>(s, detail::to_arr(grad));
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::opt
|
||||
|
||||
#endif // OPTIMIZER_HPP
|
||||
100
src/libslic3r/PNGRead.cpp
Normal file
100
src/libslic3r/PNGRead.cpp
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
#include "PNGRead.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <cstdio>
|
||||
#include <png.h>
|
||||
|
||||
namespace Slic3r { namespace png {
|
||||
|
||||
struct PNGDescr {
|
||||
png_struct *png = nullptr; png_info *info = nullptr;
|
||||
|
||||
PNGDescr() = default;
|
||||
PNGDescr(const PNGDescr&) = delete;
|
||||
PNGDescr(PNGDescr&&) = delete;
|
||||
PNGDescr& operator=(const PNGDescr&) = delete;
|
||||
PNGDescr& operator=(PNGDescr&&) = delete;
|
||||
|
||||
~PNGDescr()
|
||||
{
|
||||
if (png && info) png_destroy_info_struct(png, &info);
|
||||
if (png) png_destroy_read_struct( &png, nullptr, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
bool is_png(const ReadBuf &rb)
|
||||
{
|
||||
static const constexpr int PNG_SIG_BYTES = 8;
|
||||
|
||||
#if PNG_LIBPNG_VER_MINOR <= 2
|
||||
// Earlier libpng versions had png_sig_cmp(png_bytep, ...) which is not
|
||||
// a const pointer. It is not possible to cast away the const qualifier from
|
||||
// the input buffer so... yes... life is challenging...
|
||||
png_byte buf[PNG_SIG_BYTES];
|
||||
auto inbuf = static_cast<const std::uint8_t *>(rb.buf);
|
||||
std::copy(inbuf, inbuf + PNG_SIG_BYTES, buf);
|
||||
#else
|
||||
auto buf = static_cast<png_const_bytep>(rb.buf);
|
||||
#endif
|
||||
|
||||
return rb.sz >= PNG_SIG_BYTES && !png_sig_cmp(buf, 0, PNG_SIG_BYTES);
|
||||
}
|
||||
|
||||
// Buffer read callback for libpng. It provides an allocated output buffer and
|
||||
// the amount of data it desires to read from the input.
|
||||
void png_read_callback(png_struct *png_ptr,
|
||||
png_bytep outBytes,
|
||||
png_size_t byteCountToRead)
|
||||
{
|
||||
// Retrieve our input buffer through the png_ptr
|
||||
auto reader = static_cast<IStream *>(png_get_io_ptr(png_ptr));
|
||||
|
||||
if (!reader || !reader->is_ok()) return;
|
||||
|
||||
reader->read(static_cast<std::uint8_t *>(outBytes), byteCountToRead);
|
||||
}
|
||||
|
||||
bool decode_png(IStream &in_buf, ImageGreyscale &out_img)
|
||||
{
|
||||
static const constexpr int PNG_SIG_BYTES = 8;
|
||||
|
||||
std::vector<png_byte> sig(PNG_SIG_BYTES, 0);
|
||||
in_buf.read(sig.data(), PNG_SIG_BYTES);
|
||||
if (!png_check_sig(sig.data(), PNG_SIG_BYTES))
|
||||
return false;
|
||||
|
||||
PNGDescr dsc;
|
||||
dsc.png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr,
|
||||
nullptr);
|
||||
|
||||
if(!dsc.png) return false;
|
||||
|
||||
dsc.info = png_create_info_struct(dsc.png);
|
||||
if(!dsc.info) return false;
|
||||
|
||||
png_set_read_fn(dsc.png, static_cast<void *>(&in_buf), png_read_callback);
|
||||
|
||||
// Tell that we have already read the first bytes to check the signature
|
||||
png_set_sig_bytes(dsc.png, PNG_SIG_BYTES);
|
||||
|
||||
png_read_info(dsc.png, dsc.info);
|
||||
|
||||
out_img.cols = png_get_image_width(dsc.png, dsc.info);
|
||||
out_img.rows = png_get_image_height(dsc.png, dsc.info);
|
||||
size_t color_type = png_get_color_type(dsc.png, dsc.info);
|
||||
size_t bit_depth = png_get_bit_depth(dsc.png, dsc.info);
|
||||
|
||||
if (color_type != PNG_COLOR_TYPE_GRAY || bit_depth != 8)
|
||||
return false;
|
||||
|
||||
out_img.buf.resize(out_img.rows * out_img.cols);
|
||||
|
||||
auto readbuf = static_cast<png_bytep>(out_img.buf.data());
|
||||
for (size_t r = 0; r < out_img.rows; ++r)
|
||||
png_read_row(dsc.png, readbuf + r * out_img.cols, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::png
|
||||
70
src/libslic3r/PNGRead.hpp
Normal file
70
src/libslic3r/PNGRead.hpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#ifndef PNGREAD_HPP
|
||||
#define PNGREAD_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <istream>
|
||||
|
||||
namespace Slic3r { namespace png {
|
||||
|
||||
// Interface for an input stream of encoded png image data.
|
||||
struct IStream {
|
||||
virtual ~IStream() = default;
|
||||
virtual size_t read(std::uint8_t *outp, size_t amount) = 0;
|
||||
virtual bool is_ok() const = 0;
|
||||
};
|
||||
|
||||
// The output format of decode_png: a 2D pixel matrix stored continuously row
|
||||
// after row (row major layout).
|
||||
template<class PxT> struct Image {
|
||||
std::vector<PxT> buf;
|
||||
size_t rows, cols;
|
||||
PxT get(size_t row, size_t col) const { return buf[row * cols + col]; }
|
||||
};
|
||||
|
||||
using ImageGreyscale = Image<uint8_t>;
|
||||
|
||||
// Only decodes true 8 bit grayscale png images. Returns false for other formats
|
||||
// TODO (if needed): implement transformation of rgb images into grayscale...
|
||||
bool decode_png(IStream &stream, ImageGreyscale &out_img);
|
||||
|
||||
// TODO (if needed)
|
||||
// struct RGB { uint8_t r, g, b; };
|
||||
// using ImageRGB = Image<RGB>;
|
||||
// bool decode_png(IStream &stream, ImageRGB &img);
|
||||
|
||||
|
||||
// Encoded png data buffer: a simple read-only buffer and its size.
|
||||
struct ReadBuf { const void *buf = nullptr; const size_t sz = 0; };
|
||||
|
||||
bool is_png(const ReadBuf &pngbuf);
|
||||
|
||||
template<class Img> bool decode_png(const ReadBuf &in_buf, Img &out_img)
|
||||
{
|
||||
struct ReadBufStream: public IStream {
|
||||
const ReadBuf &rbuf_ref; size_t pos = 0;
|
||||
|
||||
explicit ReadBufStream(const ReadBuf &buf): rbuf_ref{buf} {}
|
||||
|
||||
size_t read(std::uint8_t *outp, size_t amount) override
|
||||
{
|
||||
if (amount > rbuf_ref.sz - pos) return 0;
|
||||
|
||||
auto buf = static_cast<const std::uint8_t *>(rbuf_ref.buf);
|
||||
std::copy(buf + pos, buf + (pos + amount), outp);
|
||||
pos += amount;
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
bool is_ok() const override { return pos < rbuf_ref.sz; }
|
||||
} stream{in_buf};
|
||||
|
||||
return decode_png(stream, out_img);
|
||||
}
|
||||
|
||||
// TODO: std::istream of FILE* could be similarly adapted in case its needed...
|
||||
|
||||
}} // namespace Slic3r::png
|
||||
|
||||
#endif // PNGREAD_HPP
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#include "PlaceholderParser.hpp"
|
||||
#include "Exception.hpp"
|
||||
#include "Flow.hpp"
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
|
|
@ -1303,7 +1304,7 @@ static std::string process_macro(const std::string &templ, client::MyContext &co
|
|||
if (!context.error_message.empty()) {
|
||||
if (context.error_message.back() != '\n' && context.error_message.back() != '\r')
|
||||
context.error_message += '\n';
|
||||
throw std::runtime_error(context.error_message);
|
||||
throw Slic3r::RuntimeError(context.error_message);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
|
@ -1319,7 +1320,7 @@ std::string PlaceholderParser::process(const std::string &templ, unsigned int cu
|
|||
}
|
||||
|
||||
// Evaluate a boolean expression using the full expressive power of the PlaceholderParser boolean expression syntax.
|
||||
// Throws std::runtime_error on syntax or runtime error.
|
||||
// Throws Slic3r::RuntimeError on syntax or runtime error.
|
||||
bool PlaceholderParser::evaluate_boolean_expression(const std::string &templ, const DynamicConfig &config, const DynamicConfig *config_override)
|
||||
{
|
||||
client::MyContext context;
|
||||
|
|
|
|||
|
|
@ -40,11 +40,11 @@ public:
|
|||
const DynamicConfig* external_config() const { return m_external_config; }
|
||||
|
||||
// Fill in the template using a macro processing language.
|
||||
// Throws std::runtime_error on syntax or runtime error.
|
||||
// Throws Slic3r::RuntimeError on syntax or runtime error.
|
||||
std::string process(const std::string &templ, unsigned int current_extruder_id = 0, const DynamicConfig *config_override = nullptr) const;
|
||||
|
||||
// Evaluate a boolean expression using the full expressive power of the PlaceholderParser boolean expression syntax.
|
||||
// Throws std::runtime_error on syntax or runtime error.
|
||||
// Throws Slic3r::RuntimeError on syntax or runtime error.
|
||||
static bool evaluate_boolean_expression(const std::string &templ, const DynamicConfig &config, const DynamicConfig *config_override = nullptr);
|
||||
|
||||
// Update timestamp, year, month, day, hour, minute, second variables at the provided config.
|
||||
|
|
|
|||
|
|
@ -60,10 +60,13 @@ inline int64_t cross2(const Vec2i64 &v1, const Vec2i64 &v2) { return v1(0) * v2(
|
|||
inline float cross2(const Vec2f &v1, const Vec2f &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
|
||||
inline double cross2(const Vec2d &v1, const Vec2d &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
|
||||
|
||||
inline Vec2i32 to_2d(const Vec2i32 &pt3) { return Vec2i32(pt3(0), pt3(1)); }
|
||||
inline Vec2i64 to_2d(const Vec3i64 &pt3) { return Vec2i64(pt3(0), pt3(1)); }
|
||||
inline Vec2f to_2d(const Vec3f &pt3) { return Vec2f (pt3(0), pt3(1)); }
|
||||
inline Vec2d to_2d(const Vec3d &pt3) { return Vec2d (pt3(0), pt3(1)); }
|
||||
template<class T, int N> Eigen::Matrix<T, 2, 1, Eigen::DontAlign>
|
||||
to_2d(const Eigen::Matrix<T, N, 1, Eigen::DontAlign> &ptN) { return {ptN(0), ptN(1)}; }
|
||||
|
||||
//inline Vec2i32 to_2d(const Vec3i32 &pt3) { return Vec2i32(pt3(0), pt3(1)); }
|
||||
//inline Vec2i64 to_2d(const Vec3i64 &pt3) { return Vec2i64(pt3(0), pt3(1)); }
|
||||
//inline Vec2f to_2d(const Vec3f &pt3) { return Vec2f (pt3(0), pt3(1)); }
|
||||
//inline Vec2d to_2d(const Vec3d &pt3) { return Vec2d (pt3(0), pt3(1)); }
|
||||
|
||||
inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); }
|
||||
inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); }
|
||||
|
|
@ -85,6 +88,8 @@ inline std::string to_string(const Vec3d &pt) { return std::string("[") + std:
|
|||
std::vector<Vec3f> transform(const std::vector<Vec3f>& points, const Transform3f& t);
|
||||
Pointf3s transform(const Pointf3s& points, const Transform3d& t);
|
||||
|
||||
template<int N, class T> using Vec = Eigen::Matrix<T, N, 1, Eigen::DontAlign, N, 1>;
|
||||
|
||||
class Point : public Vec2crd
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "BoundingBox.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Exception.hpp"
|
||||
#include "Polygon.hpp"
|
||||
#include "Polyline.hpp"
|
||||
|
||||
|
|
@ -16,7 +17,7 @@ Polyline Polygon::split_at_vertex(const Point &point) const
|
|||
for (const Point &pt : this->points)
|
||||
if (pt == point)
|
||||
return this->split_at_index(int(&pt - &this->points.front()));
|
||||
throw std::invalid_argument("Point not found");
|
||||
throw Slic3r::InvalidArgument("Point not found");
|
||||
return Polyline();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,6 +86,14 @@ inline double total_length(const Polygons &polylines) {
|
|||
return total;
|
||||
}
|
||||
|
||||
inline double area(const Polygons &polys)
|
||||
{
|
||||
double s = 0.;
|
||||
for (auto &p : polys) s += p.area();
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
// Remove sticks (tentacles with zero area) from the polygon.
|
||||
extern bool remove_sticks(Polygon &poly);
|
||||
extern bool remove_sticks(Polygons &polys);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "BoundingBox.hpp"
|
||||
#include "Polyline.hpp"
|
||||
#include "Exception.hpp"
|
||||
#include "ExPolygon.hpp"
|
||||
#include "ExPolygonCollection.hpp"
|
||||
#include "Line.hpp"
|
||||
|
|
@ -19,7 +20,7 @@ Polyline::operator Polylines() const
|
|||
Polyline::operator Line() const
|
||||
{
|
||||
if (this->points.size() > 2)
|
||||
throw std::invalid_argument("Can't convert polyline with more than two points to a line");
|
||||
throw Slic3r::InvalidArgument("Can't convert polyline with more than two points to a line");
|
||||
return Line(this->points.front(), this->points.back());
|
||||
}
|
||||
|
||||
|
|
@ -207,7 +208,7 @@ BoundingBox get_extents(const Polylines &polylines)
|
|||
const Point& leftmost_point(const Polylines &polylines)
|
||||
{
|
||||
if (polylines.empty())
|
||||
throw std::invalid_argument("leftmost_point() called on empty PolylineCollection");
|
||||
throw Slic3r::InvalidArgument("leftmost_point() called on empty PolylineCollection");
|
||||
Polylines::const_iterator it = polylines.begin();
|
||||
const Point *p = &it->leftmost_point();
|
||||
for (++ it; it != polylines.end(); ++it) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
#include <cassert>
|
||||
|
||||
#include "Exception.hpp"
|
||||
#include "Preset.hpp"
|
||||
#include "AppConfig.hpp"
|
||||
#include "BitmapCache.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
|
@ -12,6 +10,16 @@
|
|||
#include <Windows.h>
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
// instead of #include "slic3r/GUI/I18N.hpp" :
|
||||
#ifndef L
|
||||
// !!! If you needed to translate some string,
|
||||
// !!! please use _L(string)
|
||||
// !!! _() - is a standard wxWidgets macro to translate
|
||||
// !!! L() is used only for marking localizable string
|
||||
// !!! It will be used in "xgettext" to create a Locating Message Catalog.
|
||||
#define L(s) s
|
||||
#endif /* L */
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
|
|
@ -19,6 +27,7 @@
|
|||
#include <boost/format.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <boost/nowide/cenv.hpp>
|
||||
|
|
@ -30,15 +39,9 @@
|
|||
#include <boost/locale.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <wx/image.h>
|
||||
#include <wx/choice.h>
|
||||
#include <wx/bmpcbox.h>
|
||||
#include <wx/wupdlock.h>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/PlaceholderParser.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "libslic3r.h"
|
||||
#include "Utils.hpp"
|
||||
#include "PlaceholderParser.hpp"
|
||||
|
||||
using boost::property_tree::ptree;
|
||||
|
||||
|
|
@ -105,7 +108,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
|
|||
const std::string id = path.stem().string();
|
||||
|
||||
if (! boost::filesystem::exists(path)) {
|
||||
throw std::runtime_error((boost::format("Cannot load Vendor Config Bundle `%1%`: File not found: `%2%`.") % id % path).str());
|
||||
throw Slic3r::RuntimeError((boost::format("Cannot load Vendor Config Bundle `%1%`: File not found: `%2%`.") % id % path).str());
|
||||
}
|
||||
|
||||
VendorProfile res(id);
|
||||
|
|
@ -115,7 +118,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
|
|||
{
|
||||
auto res = tree.find(key);
|
||||
if (res == tree.not_found()) {
|
||||
throw std::runtime_error((boost::format("Vendor Config Bundle `%1%` is not valid: Missing secion or key: `%2%`.") % id % key).str());
|
||||
throw Slic3r::RuntimeError((boost::format("Vendor Config Bundle `%1%` is not valid: Missing secion or key: `%2%`.") % id % key).str());
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
|
@ -127,7 +130,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
|
|||
auto config_version_str = get_or_throw(vendor_section, "config_version")->second.data();
|
||||
auto config_version = Semver::parse(config_version_str);
|
||||
if (! config_version) {
|
||||
throw std::runtime_error((boost::format("Vendor Config Bundle `%1%` is not valid: Cannot parse config_version: `%2%`.") % id % config_version_str).str());
|
||||
throw Slic3r::RuntimeError((boost::format("Vendor Config Bundle `%1%` is not valid: Cannot parse config_version: `%2%`.") % id % config_version_str).str());
|
||||
} else {
|
||||
res.config_version = std::move(*config_version);
|
||||
}
|
||||
|
|
@ -245,9 +248,9 @@ const std::string& Preset::suffix_modified()
|
|||
return g_suffix_modified;
|
||||
}
|
||||
|
||||
void Preset::update_suffix_modified()
|
||||
void Preset::update_suffix_modified(const std::string& new_suffix_modified)
|
||||
{
|
||||
g_suffix_modified = (" (" + _(L("modified")) + ")").ToUTF8().data();
|
||||
g_suffix_modified = new_suffix_modified;
|
||||
}
|
||||
// Remove an optional "(modified)" suffix from a name.
|
||||
// This converts a UI name to a unique preset identifier.
|
||||
|
|
@ -496,6 +499,7 @@ const std::vector<std::string>& Preset::sla_print_options()
|
|||
"support_head_penetration",
|
||||
"support_head_width",
|
||||
"support_pillar_diameter",
|
||||
"support_small_pillar_diameter_percent",
|
||||
"support_max_bridges_on_pillar",
|
||||
"support_pillar_connection_mode",
|
||||
"support_buildplate_only",
|
||||
|
|
@ -590,10 +594,7 @@ const std::vector<std::string>& Preset::sla_printer_options()
|
|||
PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name) :
|
||||
m_type(type),
|
||||
m_edited_preset(type, "", false),
|
||||
m_idx_selected(0),
|
||||
m_bitmap_main_frame(new wxBitmap),
|
||||
m_bitmap_add(new wxBitmap),
|
||||
m_bitmap_cache(new GUI::BitmapCache)
|
||||
m_idx_selected(0)
|
||||
{
|
||||
// Insert just the default preset.
|
||||
this->add_default_preset(keys, defaults, default_name);
|
||||
|
|
@ -602,12 +603,6 @@ PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::str
|
|||
|
||||
PresetCollection::~PresetCollection()
|
||||
{
|
||||
delete m_bitmap_main_frame;
|
||||
m_bitmap_main_frame = nullptr;
|
||||
delete m_bitmap_add;
|
||||
m_bitmap_add = nullptr;
|
||||
delete m_bitmap_cache;
|
||||
m_bitmap_cache = nullptr;
|
||||
}
|
||||
|
||||
void PresetCollection::reset(bool delete_files)
|
||||
|
|
@ -640,7 +635,9 @@ void PresetCollection::add_default_preset(const std::vector<std::string> &keys,
|
|||
// Throws an exception on error.
|
||||
void PresetCollection::load_presets(const std::string &dir_path, const std::string &subdir)
|
||||
{
|
||||
boost::filesystem::path dir = boost::filesystem::canonical(boost::filesystem::path(dir_path) / subdir).make_preferred();
|
||||
// Don't use boost::filesystem::canonical() on Windows, it is broken in regard to reparse points,
|
||||
// see https://github.com/prusa3d/PrusaSlicer/issues/732
|
||||
boost::filesystem::path dir = boost::filesystem::absolute(boost::filesystem::path(dir_path) / subdir).make_preferred();
|
||||
m_dir_path = dir.string();
|
||||
std::string errors_cummulative;
|
||||
// Store the loaded presets into a new vector, otherwise the binary search for already existing presets would be broken.
|
||||
|
|
@ -676,9 +673,9 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri
|
|||
preset.file << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
|
||||
preset.loaded = true;
|
||||
} catch (const std::ifstream::failure &err) {
|
||||
throw std::runtime_error(std::string("The selected preset cannot be loaded: ") + preset.file + "\n\tReason: " + err.what());
|
||||
throw Slic3r::RuntimeError(std::string("The selected preset cannot be loaded: ") + preset.file + "\n\tReason: " + err.what());
|
||||
} catch (const std::runtime_error &err) {
|
||||
throw std::runtime_error(std::string("Failed loading the preset file: ") + preset.file + "\n\tReason: " + err.what());
|
||||
throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + preset.file + "\n\tReason: " + err.what());
|
||||
}
|
||||
presets_loaded.emplace_back(preset);
|
||||
} catch (const std::runtime_error &err) {
|
||||
|
|
@ -690,7 +687,7 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri
|
|||
std::sort(m_presets.begin() + m_num_default_presets, m_presets.end());
|
||||
this->select_preset(first_visible_idx());
|
||||
if (! errors_cummulative.empty())
|
||||
throw std::runtime_error(errors_cummulative);
|
||||
throw Slic3r::RuntimeError(errors_cummulative);
|
||||
}
|
||||
|
||||
// Load a preset from an already parsed config file, insert it into the sorted sequence of presets
|
||||
|
|
@ -951,16 +948,6 @@ bool PresetCollection::delete_preset(const std::string& name)
|
|||
return true;
|
||||
}
|
||||
|
||||
void PresetCollection::load_bitmap_default(const std::string &file_name)
|
||||
{
|
||||
*m_bitmap_main_frame = create_scaled_bitmap(file_name);
|
||||
}
|
||||
|
||||
void PresetCollection::load_bitmap_add(const std::string &file_name)
|
||||
{
|
||||
*m_bitmap_add = create_scaled_bitmap(file_name);
|
||||
}
|
||||
|
||||
const Preset* PresetCollection::get_selected_preset_parent() const
|
||||
{
|
||||
if (this->get_selected_idx() == size_t(-1))
|
||||
|
|
@ -1119,279 +1106,15 @@ size_t PresetCollection::update_compatible_internal(const PresetWithVendorProfil
|
|||
// Delete the current preset, activate the first visible preset.
|
||||
//void PresetCollection::delete_current_preset();
|
||||
|
||||
// Update the wxChoice UI component from this list of presets.
|
||||
// Hide the
|
||||
void PresetCollection::update_plater_ui(GUI::PresetComboBox *ui)
|
||||
{
|
||||
if (ui == nullptr)
|
||||
return;
|
||||
|
||||
// Otherwise fill in the list from scratch.
|
||||
ui->Freeze();
|
||||
ui->Clear();
|
||||
size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected
|
||||
|
||||
const Preset &selected_preset = this->get_selected_preset();
|
||||
// Show wide icons if the currently selected preset is not compatible with the current printer,
|
||||
// and draw a red flag in front of the selected preset.
|
||||
bool wide_icons = ! selected_preset.is_compatible && m_bitmap_incompatible != nullptr;
|
||||
|
||||
/* It's supposed that standard size of an icon is 16px*16px for 100% scaled display.
|
||||
* So set sizes for solid_colored icons used for filament preset
|
||||
* and scale them in respect to em_unit value
|
||||
*/
|
||||
const float scale_f = ui->em_unit() * 0.1f;
|
||||
const int icon_height = 16 * scale_f + 0.5f;
|
||||
const int icon_width = 16 * scale_f + 0.5f;
|
||||
const int thin_space_icon_width = 4 * scale_f + 0.5f;
|
||||
const int wide_space_icon_width = 6 * scale_f + 0.5f;
|
||||
|
||||
std::map<wxString, wxBitmap*> nonsys_presets;
|
||||
wxString selected = "";
|
||||
wxString tooltip = "";
|
||||
if (!this->m_presets.front().is_visible)
|
||||
ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap));
|
||||
for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++ i) {
|
||||
const Preset &preset = this->m_presets[i];
|
||||
if (! preset.is_visible || (! preset.is_compatible && i != m_idx_selected))
|
||||
continue;
|
||||
|
||||
std::string bitmap_key = "";
|
||||
// !!! Temporary solution, till refactoring: create and use "sla_printer" icon instead of m_bitmap_main_frame
|
||||
wxBitmap main_bmp = m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap;
|
||||
if (m_type == Preset::TYPE_PRINTER && preset.printer_technology()==ptSLA ) {
|
||||
bitmap_key = "sla_printer";
|
||||
main_bmp = create_scaled_bitmap("sla_printer");
|
||||
}
|
||||
|
||||
// If the filament preset is not compatible and there is a "red flag" icon loaded, show it left
|
||||
// to the filament color image.
|
||||
if (wide_icons)
|
||||
bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt";
|
||||
bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst";
|
||||
wxBitmap *bmp = m_bitmap_cache->find(bitmap_key);
|
||||
if (bmp == nullptr) {
|
||||
// Create the bitmap with color bars.
|
||||
std::vector<wxBitmap> bmps;
|
||||
if (wide_icons)
|
||||
// Paint a red flag for incompatible presets.
|
||||
bmps.emplace_back(preset.is_compatible ? m_bitmap_cache->mkclear(icon_width, icon_height) : *m_bitmap_incompatible);
|
||||
// Paint the color bars.
|
||||
bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height));
|
||||
bmps.emplace_back(main_bmp);
|
||||
// Paint a lock at the system presets.
|
||||
bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height));
|
||||
bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmap_lock : m_bitmap_cache->mkclear(icon_width, icon_height));
|
||||
bmp = m_bitmap_cache->insert(bitmap_key, bmps);
|
||||
}
|
||||
|
||||
const std::string name = preset.alias.empty() ? preset.name : preset.alias;
|
||||
if (preset.is_default || preset.is_system) {
|
||||
ui->Append(wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? g_suffix_modified : "")).c_str()),
|
||||
(bmp == 0) ? main_bmp : *bmp);
|
||||
if (i == m_idx_selected ||
|
||||
// just in case: mark selected_preset_item as a first added element
|
||||
selected_preset_item == INT_MAX) {
|
||||
selected_preset_item = ui->GetCount() - 1;
|
||||
tooltip = wxString::FromUTF8(preset.name.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nonsys_presets.emplace(wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), bmp/*preset.is_compatible*/);
|
||||
if (i == m_idx_selected) {
|
||||
selected = wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? g_suffix_modified : "")).c_str());
|
||||
tooltip = wxString::FromUTF8(preset.name.c_str());
|
||||
}
|
||||
}
|
||||
if (i + 1 == m_num_default_presets)
|
||||
ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap));
|
||||
}
|
||||
if (!nonsys_presets.empty())
|
||||
{
|
||||
ui->set_label_marker(ui->Append(PresetCollection::separator(L("User presets")), wxNullBitmap));
|
||||
for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
|
||||
ui->Append(it->first, *it->second);
|
||||
if (it->first == selected ||
|
||||
// just in case: mark selected_preset_item as a first added element
|
||||
selected_preset_item == INT_MAX)
|
||||
selected_preset_item = ui->GetCount() - 1;
|
||||
}
|
||||
}
|
||||
if (m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_SLA_MATERIAL) {
|
||||
std::string bitmap_key = "";
|
||||
// If the filament preset is not compatible and there is a "red flag" icon loaded, show it left
|
||||
// to the filament color image.
|
||||
if (wide_icons)
|
||||
bitmap_key += "wide,";
|
||||
bitmap_key += "edit_preset_list";
|
||||
wxBitmap *bmp = m_bitmap_cache->find(bitmap_key);
|
||||
if (bmp == nullptr) {
|
||||
// Create the bitmap with color bars.
|
||||
std::vector<wxBitmap> bmps;
|
||||
if (wide_icons)
|
||||
// Paint a red flag for incompatible presets.
|
||||
bmps.emplace_back(m_bitmap_cache->mkclear(icon_width, icon_height));
|
||||
// Paint the color bars.
|
||||
bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height));
|
||||
bmps.emplace_back(*m_bitmap_main_frame);
|
||||
// Paint a lock at the system presets.
|
||||
bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height));
|
||||
// bmps.emplace_back(m_bitmap_add ? *m_bitmap_add : wxNullBitmap);
|
||||
bmps.emplace_back(create_scaled_bitmap("edit_uni"));
|
||||
bmp = m_bitmap_cache->insert(bitmap_key, bmps);
|
||||
}
|
||||
if (m_type == Preset::TYPE_SLA_MATERIAL)
|
||||
ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add/Remove materials")), *bmp), GUI::PresetComboBox::LABEL_ITEM_WIZARD_MATERIALS);
|
||||
else
|
||||
ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add/Remove printers")), *bmp), GUI::PresetComboBox::LABEL_ITEM_WIZARD_PRINTERS);
|
||||
}
|
||||
|
||||
/* But, if selected_preset_item is still equal to INT_MAX, it means that
|
||||
* there is no presets added to the list.
|
||||
* So, select last combobox item ("Add/Remove preset")
|
||||
*/
|
||||
if (selected_preset_item == INT_MAX)
|
||||
selected_preset_item = ui->GetCount() - 1;
|
||||
|
||||
ui->SetSelection(selected_preset_item);
|
||||
ui->SetToolTip(tooltip.IsEmpty() ? ui->GetString(selected_preset_item) : tooltip);
|
||||
ui->check_selection(selected_preset_item);
|
||||
ui->Thaw();
|
||||
|
||||
// Update control min size after rescale (changed Display DPI under MSW)
|
||||
if (ui->GetMinWidth() != 20 * ui->em_unit())
|
||||
ui->SetMinSize(wxSize(20 * ui->em_unit(), ui->GetSize().GetHeight()));
|
||||
}
|
||||
|
||||
size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible, const int em/* = 10*/)
|
||||
{
|
||||
if (ui == nullptr)
|
||||
return 0;
|
||||
ui->Freeze();
|
||||
ui->Clear();
|
||||
size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected
|
||||
|
||||
/* It's supposed that standard size of an icon is 16px*16px for 100% scaled display.
|
||||
* So set sizes for solid_colored(empty) icons used for preset
|
||||
* and scale them in respect to em_unit value
|
||||
*/
|
||||
const float scale_f = em * 0.1f;
|
||||
const int icon_height = 16 * scale_f + 0.5f;
|
||||
const int icon_width = 16 * scale_f + 0.5f;
|
||||
|
||||
std::map<wxString, wxBitmap*> nonsys_presets;
|
||||
wxString selected = "";
|
||||
if (!this->m_presets.front().is_visible)
|
||||
ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap);
|
||||
for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++i) {
|
||||
const Preset &preset = this->m_presets[i];
|
||||
if (! preset.is_visible || (! show_incompatible && ! preset.is_compatible && i != m_idx_selected))
|
||||
continue;
|
||||
std::string bitmap_key = "tab";
|
||||
|
||||
// !!! Temporary solution, till refactoring: create and use "sla_printer" icon instead of m_bitmap_main_frame
|
||||
wxBitmap main_bmp = m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap;
|
||||
if (m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA) {
|
||||
bitmap_key = "sla_printer";
|
||||
main_bmp = create_scaled_bitmap("sla_printer");
|
||||
}
|
||||
|
||||
bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt";
|
||||
bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst";
|
||||
wxBitmap *bmp = m_bitmap_cache->find(bitmap_key);
|
||||
if (bmp == nullptr) {
|
||||
// Create the bitmap with color bars.
|
||||
std::vector<wxBitmap> bmps;
|
||||
const wxBitmap* tmp_bmp = preset.is_compatible ? m_bitmap_compatible : m_bitmap_incompatible;
|
||||
bmps.emplace_back((tmp_bmp == 0) ? main_bmp : *tmp_bmp);
|
||||
// Paint a lock at the system presets.
|
||||
bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmap_lock : m_bitmap_cache->mkclear(icon_width, icon_height));
|
||||
bmp = m_bitmap_cache->insert(bitmap_key, bmps);
|
||||
}
|
||||
|
||||
if (preset.is_default || preset.is_system) {
|
||||
ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()),
|
||||
(bmp == 0) ? main_bmp : *bmp);
|
||||
if (i == m_idx_selected ||
|
||||
// just in case: mark selected_preset_item as a first added element
|
||||
selected_preset_item == INT_MAX)
|
||||
selected_preset_item = ui->GetCount() - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), bmp/*preset.is_compatible*/);
|
||||
if (i == m_idx_selected)
|
||||
selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str());
|
||||
}
|
||||
if (i + 1 == m_num_default_presets)
|
||||
ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap);
|
||||
}
|
||||
if (!nonsys_presets.empty())
|
||||
{
|
||||
ui->Append(PresetCollection::separator(L("User presets")), wxNullBitmap);
|
||||
for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
|
||||
ui->Append(it->first, *it->second);
|
||||
if (it->first == selected ||
|
||||
// just in case: mark selected_preset_item as a first added element
|
||||
selected_preset_item == INT_MAX)
|
||||
selected_preset_item = ui->GetCount() - 1;
|
||||
}
|
||||
}
|
||||
if (m_type == Preset::TYPE_PRINTER) {
|
||||
wxBitmap *bmp = m_bitmap_cache->find("edit_printer_list");
|
||||
if (bmp == nullptr) {
|
||||
// Create the bitmap with color bars.
|
||||
std::vector<wxBitmap> bmps;
|
||||
bmps.emplace_back(*m_bitmap_main_frame);
|
||||
// bmps.emplace_back(m_bitmap_add ? *m_bitmap_add : wxNullBitmap);
|
||||
bmps.emplace_back(create_scaled_bitmap("edit_uni"));
|
||||
bmp = m_bitmap_cache->insert("add_printer_tab", bmps);
|
||||
}
|
||||
ui->Append(PresetCollection::separator("Add a new printer"), *bmp);
|
||||
}
|
||||
|
||||
/* But, if selected_preset_item is still equal to INT_MAX, it means that
|
||||
* there is no presets added to the list.
|
||||
* So, select last combobox item ("Add/Remove preset")
|
||||
*/
|
||||
if (selected_preset_item == INT_MAX)
|
||||
selected_preset_item = ui->GetCount() - 1;
|
||||
|
||||
ui->SetSelection(selected_preset_item);
|
||||
ui->SetToolTip(ui->GetString(selected_preset_item));
|
||||
ui->Thaw();
|
||||
return selected_preset_item;
|
||||
}
|
||||
|
||||
// Update a dirty floag of the current preset, update the labels of the UI component accordingly.
|
||||
// Update a dirty flag of the current preset
|
||||
// Return true if the dirty flag changed.
|
||||
bool PresetCollection::update_dirty_ui(wxBitmapComboBox *ui)
|
||||
bool PresetCollection::update_dirty()
|
||||
{
|
||||
wxWindowUpdateLocker noUpdates(ui);
|
||||
// 1) Update the dirty flag of the current preset.
|
||||
bool was_dirty = this->get_selected_preset().is_dirty;
|
||||
bool is_dirty = current_is_dirty();
|
||||
this->get_selected_preset().is_dirty = is_dirty;
|
||||
this->get_edited_preset().is_dirty = is_dirty;
|
||||
// 2) Update the labels.
|
||||
for (unsigned int ui_id = 0; ui_id < ui->GetCount(); ++ ui_id) {
|
||||
std::string old_label = ui->GetString(ui_id).utf8_str().data();
|
||||
std::string preset_name = Preset::remove_suffix_modified(old_label);
|
||||
const Preset *preset = this->find_preset(preset_name, false);
|
||||
// The old_label could be the "----- system presets ------" or the "------- user presets --------" separator.
|
||||
// assert(preset != nullptr);
|
||||
if (preset != nullptr) {
|
||||
std::string new_label = preset->is_dirty ? preset->name + g_suffix_modified : preset->name;
|
||||
if (old_label != new_label)
|
||||
ui->SetString(ui_id, wxString::FromUTF8(new_label.c_str()));
|
||||
}
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
// wxWidgets on OSX do not upload the text of the combo box line automatically.
|
||||
// Force it to update by re-selecting.
|
||||
ui->SetSelection(ui->GetSelection());
|
||||
#endif /* __APPLE __ */
|
||||
|
||||
return was_dirty != is_dirty;
|
||||
}
|
||||
|
||||
|
|
@ -1605,16 +1328,6 @@ std::string PresetCollection::path_from_name(const std::string &new_name) const
|
|||
return (boost::filesystem::path(m_dir_path) / file_name).make_preferred().string();
|
||||
}
|
||||
|
||||
void PresetCollection::clear_bitmap_cache()
|
||||
{
|
||||
m_bitmap_cache->clear();
|
||||
}
|
||||
|
||||
wxString PresetCollection::separator(const std::string &label)
|
||||
{
|
||||
return wxString::FromUTF8(PresetCollection::separator_head()) + _(label) + wxString::FromUTF8(PresetCollection::separator_tail());
|
||||
}
|
||||
|
||||
const Preset& PrinterPresetCollection::default_preset_for(const DynamicPrintConfig &config) const
|
||||
{
|
||||
const ConfigOptionEnumGeneric *opt_printer_technology = config.opt<ConfigOptionEnumGeneric>("printer_technology");
|
||||
|
|
@ -1632,6 +1345,472 @@ const Preset* PrinterPresetCollection::find_by_model_id(const std::string &model
|
|||
return it != cend() ? &*it : nullptr;
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// *** PhysicalPrinter ***
|
||||
// -------------------------
|
||||
|
||||
std::string PhysicalPrinter::separator()
|
||||
{
|
||||
return " * ";
|
||||
}
|
||||
|
||||
const std::vector<std::string>& PhysicalPrinter::printer_options()
|
||||
{
|
||||
static std::vector<std::string> s_opts;
|
||||
if (s_opts.empty()) {
|
||||
s_opts = {
|
||||
"preset_name",
|
||||
"printer_technology",
|
||||
"printer_model",
|
||||
"host_type",
|
||||
"print_host",
|
||||
"printhost_apikey",
|
||||
"printhost_cafile",
|
||||
"printhost_authorization_type",
|
||||
// HTTP digest authentization (RFC 2617)
|
||||
"printhost_user",
|
||||
"printhost_password"
|
||||
};
|
||||
}
|
||||
return s_opts;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& PhysicalPrinter::print_host_options()
|
||||
{
|
||||
static std::vector<std::string> s_opts;
|
||||
if (s_opts.empty()) {
|
||||
s_opts = {
|
||||
"print_host",
|
||||
"printhost_apikey",
|
||||
"printhost_cafile"
|
||||
};
|
||||
}
|
||||
return s_opts;
|
||||
}
|
||||
|
||||
std::vector<std::string> PhysicalPrinter::presets_with_print_host_information(const PrinterPresetCollection& printer_presets)
|
||||
{
|
||||
std::vector<std::string> presets;
|
||||
for (const Preset& preset : printer_presets)
|
||||
if (has_print_host_information(preset.config))
|
||||
presets.emplace_back(preset.name);
|
||||
|
||||
return presets;
|
||||
}
|
||||
|
||||
bool PhysicalPrinter::has_print_host_information(const DynamicPrintConfig& config)
|
||||
{
|
||||
for (const std::string& opt : print_host_options())
|
||||
if (!config.opt_string(opt).empty())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::set<std::string>& PhysicalPrinter::get_preset_names() const
|
||||
{
|
||||
return preset_names;
|
||||
}
|
||||
|
||||
bool PhysicalPrinter::has_empty_config() const
|
||||
{
|
||||
return config.opt_string("print_host" ).empty() &&
|
||||
config.opt_string("printhost_apikey" ).empty() &&
|
||||
config.opt_string("printhost_cafile" ).empty() &&
|
||||
config.opt_string("printhost_user" ).empty() &&
|
||||
config.opt_string("printhost_password").empty();
|
||||
}
|
||||
|
||||
void PhysicalPrinter::update_preset_names_in_config()
|
||||
{
|
||||
if (!preset_names.empty()) {
|
||||
std::string name;
|
||||
for (auto el : preset_names)
|
||||
name += el + ";";
|
||||
name.pop_back();
|
||||
config.set_key_value("preset_name", new ConfigOptionString(name));
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicalPrinter::save(const std::string& file_name_from, const std::string& file_name_to)
|
||||
{
|
||||
// rename the file
|
||||
boost::nowide::rename(file_name_from.data(), file_name_to.data());
|
||||
this->file = file_name_to;
|
||||
// save configuration
|
||||
this->config.save(this->file);
|
||||
}
|
||||
|
||||
void PhysicalPrinter::update_from_preset(const Preset& preset)
|
||||
{
|
||||
config.apply_only(preset.config, printer_options(), true);
|
||||
// add preset names to the options list
|
||||
auto ret = preset_names.emplace(preset.name);
|
||||
update_preset_names_in_config();
|
||||
}
|
||||
|
||||
void PhysicalPrinter::update_from_config(const DynamicPrintConfig& new_config)
|
||||
{
|
||||
config.apply_only(new_config, printer_options(), false);
|
||||
|
||||
std::string str = config.opt_string("preset_name");
|
||||
std::set<std::string> values{};
|
||||
if (!str.empty()) {
|
||||
boost::split(values, str, boost::is_any_of(";"));
|
||||
for (const std::string& val : values)
|
||||
preset_names.emplace(val);
|
||||
}
|
||||
preset_names = values;
|
||||
}
|
||||
|
||||
void PhysicalPrinter::reset_presets()
|
||||
{
|
||||
return preset_names.clear();
|
||||
}
|
||||
|
||||
bool PhysicalPrinter::add_preset(const std::string& preset_name)
|
||||
{
|
||||
return preset_names.emplace(preset_name).second;
|
||||
}
|
||||
|
||||
bool PhysicalPrinter::delete_preset(const std::string& preset_name)
|
||||
{
|
||||
return preset_names.erase(preset_name) > 0;
|
||||
}
|
||||
|
||||
PhysicalPrinter::PhysicalPrinter(const std::string& name, const DynamicPrintConfig &default_config, const Preset& preset) :
|
||||
name(name), config(default_config)
|
||||
{
|
||||
update_from_preset(preset);
|
||||
}
|
||||
|
||||
void PhysicalPrinter::set_name(const std::string& name)
|
||||
{
|
||||
this->name = name;
|
||||
}
|
||||
|
||||
std::string PhysicalPrinter::get_full_name(std::string preset_name) const
|
||||
{
|
||||
return name + separator() + preset_name;
|
||||
}
|
||||
|
||||
std::string PhysicalPrinter::get_short_name(std::string full_name)
|
||||
{
|
||||
int pos = full_name.find(separator());
|
||||
if (pos > 0)
|
||||
boost::erase_tail(full_name, full_name.length() - pos);
|
||||
return full_name;
|
||||
}
|
||||
|
||||
std::string PhysicalPrinter::get_preset_name(std::string name)
|
||||
{
|
||||
int pos = name.find(separator());
|
||||
boost::erase_head(name, pos + 3);
|
||||
return Preset::remove_suffix_modified(name);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
// *** PhysicalPrinterCollection ***
|
||||
// -----------------------------------
|
||||
|
||||
PhysicalPrinterCollection::PhysicalPrinterCollection( const std::vector<std::string>& keys)
|
||||
{
|
||||
// Default config for a physical printer containing all key/value pairs of PhysicalPrinter::printer_options().
|
||||
for (const std::string &key : keys) {
|
||||
const ConfigOptionDef *opt = print_config_def.get(key);
|
||||
assert(opt);
|
||||
assert(opt->default_value);
|
||||
m_default_config.set_key_value(key, opt->default_value->clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Load all printers found in dir_path.
|
||||
// Throws an exception on error.
|
||||
void PhysicalPrinterCollection::load_printers(const std::string& dir_path, const std::string& subdir)
|
||||
{
|
||||
// Don't use boost::filesystem::canonical() on Windows, it is broken in regard to reparse points,
|
||||
// see https://github.com/prusa3d/PrusaSlicer/issues/732
|
||||
boost::filesystem::path dir = boost::filesystem::absolute(boost::filesystem::path(dir_path) / subdir).make_preferred();
|
||||
m_dir_path = dir.string();
|
||||
std::string errors_cummulative;
|
||||
// Store the loaded printers into a new vector, otherwise the binary search for already existing presets would be broken.
|
||||
std::deque<PhysicalPrinter> printers_loaded;
|
||||
for (auto& dir_entry : boost::filesystem::directory_iterator(dir))
|
||||
if (Slic3r::is_ini_file(dir_entry)) {
|
||||
std::string name = dir_entry.path().filename().string();
|
||||
// Remove the .ini suffix.
|
||||
name.erase(name.size() - 4);
|
||||
if (this->find_printer(name, false)) {
|
||||
// This happens when there's is a preset (most likely legacy one) with the same name as a system preset
|
||||
// that's already been loaded from a bundle.
|
||||
BOOST_LOG_TRIVIAL(warning) << "Printer already present, not loading: " << name;
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
PhysicalPrinter printer(name, this->default_config());
|
||||
printer.file = dir_entry.path().string();
|
||||
// Load the preset file, apply preset values on top of defaults.
|
||||
try {
|
||||
DynamicPrintConfig config;
|
||||
config.load_from_ini(printer.file);
|
||||
printer.update_from_config(config);
|
||||
printer.loaded = true;
|
||||
}
|
||||
catch (const std::ifstream::failure& err) {
|
||||
throw Slic3r::RuntimeError(std::string("The selected preset cannot be loaded: ") + printer.file + "\n\tReason: " + err.what());
|
||||
}
|
||||
catch (const std::runtime_error& err) {
|
||||
throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + printer.file + "\n\tReason: " + err.what());
|
||||
}
|
||||
printers_loaded.emplace_back(printer);
|
||||
}
|
||||
catch (const std::runtime_error& err) {
|
||||
errors_cummulative += err.what();
|
||||
errors_cummulative += "\n";
|
||||
}
|
||||
}
|
||||
m_printers.insert(m_printers.end(), std::make_move_iterator(printers_loaded.begin()), std::make_move_iterator(printers_loaded.end()));
|
||||
std::sort(m_printers.begin(), m_printers.end());
|
||||
if (!errors_cummulative.empty())
|
||||
throw Slic3r::RuntimeError(errors_cummulative);
|
||||
}
|
||||
|
||||
// if there is saved user presets, contains information about "Print Host upload",
|
||||
// Create default printers with this presets
|
||||
// Note! "Print Host upload" options will be cleared after physical printer creations
|
||||
void PhysicalPrinterCollection::load_printers_from_presets(PrinterPresetCollection& printer_presets)
|
||||
{
|
||||
int cnt=0;
|
||||
for (Preset& preset: printer_presets) {
|
||||
DynamicPrintConfig& config = preset.config;
|
||||
const std::vector<std::string>& options = PhysicalPrinter::print_host_options();
|
||||
|
||||
for(const std::string& option : options) {
|
||||
if (!config.opt_string(option).empty()) {
|
||||
// check if printer with those "Print Host upload" options already exist
|
||||
PhysicalPrinter* existed_printer = find_printer_with_same_config(config);
|
||||
if (existed_printer)
|
||||
// just add preset for this printer
|
||||
existed_printer->add_preset(preset.name);
|
||||
else {
|
||||
std::string new_printer_name = (boost::format("Printer %1%") % ++cnt ).str();
|
||||
while (find_printer(new_printer_name))
|
||||
new_printer_name = (boost::format("Printer %1%") % ++cnt).str();
|
||||
|
||||
// create new printer from this preset
|
||||
PhysicalPrinter printer(new_printer_name, this->default_config(), preset);
|
||||
printer.loaded = true;
|
||||
save_printer(printer);
|
||||
}
|
||||
|
||||
// erase "Print Host upload" information from the preset
|
||||
for (const std::string& opt : options)
|
||||
config.opt_string(opt).clear();
|
||||
// save changes for preset
|
||||
preset.save();
|
||||
|
||||
// update those changes for edited preset if it's equal to the preset
|
||||
Preset& edited = printer_presets.get_edited_preset();
|
||||
if (preset.name == edited.name) {
|
||||
for (const std::string& opt : options)
|
||||
edited.config.opt_string(opt).clear();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PhysicalPrinter* PhysicalPrinterCollection::find_printer( const std::string& name, bool first_visible_if_not_found)
|
||||
{
|
||||
auto it = this->find_printer_internal(name);
|
||||
// Ensure that a temporary copy is returned if the preset found is currently selected.
|
||||
return (it != m_printers.end() && it->name == name) ? &this->printer(it - m_printers.begin()) :
|
||||
first_visible_if_not_found ? &this->printer(0) : nullptr;
|
||||
}
|
||||
|
||||
PhysicalPrinter* PhysicalPrinterCollection::find_printer_with_same_config(const DynamicPrintConfig& config)
|
||||
{
|
||||
for (const PhysicalPrinter& printer :*this) {
|
||||
bool is_equal = true;
|
||||
for (const std::string& opt : PhysicalPrinter::print_host_options())
|
||||
if (is_equal && printer.config.opt_string(opt) != config.opt_string(opt))
|
||||
is_equal = false;
|
||||
|
||||
if (is_equal)
|
||||
return find_printer(printer.name);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
|
||||
std::string PhysicalPrinterCollection::path_from_name(const std::string& new_name) const
|
||||
{
|
||||
std::string file_name = boost::iends_with(new_name, ".ini") ? new_name : (new_name + ".ini");
|
||||
return (boost::filesystem::path(m_dir_path) / file_name).make_preferred().string();
|
||||
}
|
||||
|
||||
void PhysicalPrinterCollection::save_printer(PhysicalPrinter& edited_printer, const std::string& renamed_from/* = ""*/)
|
||||
{
|
||||
// controll and update preset_names in edited_printer config
|
||||
edited_printer.update_preset_names_in_config();
|
||||
|
||||
std::string name = renamed_from.empty() ? edited_printer.name : renamed_from;
|
||||
// 1) Find the printer with a new_name or create a new one,
|
||||
// initialize it with the edited config.
|
||||
auto it = this->find_printer_internal(name);
|
||||
if (it != m_printers.end() && it->name == name) {
|
||||
// Printer with the same name found.
|
||||
// Overwriting an existing preset.
|
||||
it->config = std::move(edited_printer.config);
|
||||
it->name = edited_printer.name;
|
||||
it->preset_names = edited_printer.preset_names;
|
||||
}
|
||||
else {
|
||||
// Creating a new printer.
|
||||
it = m_printers.insert(it, edited_printer);
|
||||
}
|
||||
assert(it != m_printers.end());
|
||||
|
||||
// 2) Save printer
|
||||
PhysicalPrinter& printer = *it;
|
||||
if (printer.file.empty())
|
||||
printer.file = this->path_from_name(printer.name);
|
||||
|
||||
if (printer.file == this->path_from_name(printer.name))
|
||||
printer.save();
|
||||
else
|
||||
// if printer was renamed, we should rename a file and than save the config
|
||||
printer.save(printer.file, this->path_from_name(printer.name));
|
||||
|
||||
// update idx_selected
|
||||
m_idx_selected = it - m_printers.begin();
|
||||
}
|
||||
|
||||
bool PhysicalPrinterCollection::delete_printer(const std::string& name)
|
||||
{
|
||||
auto it = this->find_printer_internal(name);
|
||||
if (it == m_printers.end())
|
||||
return false;
|
||||
|
||||
const PhysicalPrinter& printer = *it;
|
||||
// Erase the preset file.
|
||||
boost::nowide::remove(printer.file.c_str());
|
||||
m_printers.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PhysicalPrinterCollection::delete_selected_printer()
|
||||
{
|
||||
if (!has_selection())
|
||||
return false;
|
||||
const PhysicalPrinter& printer = this->get_selected_printer();
|
||||
|
||||
// Erase the preset file.
|
||||
boost::nowide::remove(printer.file.c_str());
|
||||
// Remove the preset from the list.
|
||||
m_printers.erase(m_printers.begin() + m_idx_selected);
|
||||
// unselect all printers
|
||||
unselect_printer();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PhysicalPrinterCollection::delete_preset_from_printers( const std::string& preset_name)
|
||||
{
|
||||
std::vector<std::string> printers_for_delete;
|
||||
for (PhysicalPrinter& printer : m_printers) {
|
||||
if (printer.preset_names.size() == 1 && *printer.preset_names.begin() == preset_name)
|
||||
printers_for_delete.emplace_back(printer.name);
|
||||
else if (printer.delete_preset(preset_name))
|
||||
save_printer(printer);
|
||||
}
|
||||
|
||||
if (!printers_for_delete.empty())
|
||||
for (const std::string& printer_name : printers_for_delete)
|
||||
delete_printer(printer_name);
|
||||
|
||||
unselect_printer();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get list of printers which have more than one preset and "preset_name" preset is one of them
|
||||
std::vector<std::string> PhysicalPrinterCollection::get_printers_with_preset(const std::string& preset_name)
|
||||
{
|
||||
std::vector<std::string> printers;
|
||||
|
||||
for (auto printer : m_printers) {
|
||||
if (printer.preset_names.size() == 1)
|
||||
continue;
|
||||
if (printer.preset_names.find(preset_name) != printer.preset_names.end())
|
||||
printers.emplace_back(printer.name);
|
||||
}
|
||||
|
||||
return printers;
|
||||
}
|
||||
|
||||
// Get list of printers which has only "preset_name" preset
|
||||
std::vector<std::string> PhysicalPrinterCollection::get_printers_with_only_preset(const std::string& preset_name)
|
||||
{
|
||||
std::vector<std::string> printers;
|
||||
|
||||
for (auto printer : m_printers)
|
||||
if (printer.preset_names.size() == 1 && *printer.preset_names.begin() == preset_name)
|
||||
printers.emplace_back(printer.name);
|
||||
|
||||
return printers;
|
||||
}
|
||||
|
||||
std::string PhysicalPrinterCollection::get_selected_full_printer_name() const
|
||||
{
|
||||
return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().get_full_name(m_selected_preset);
|
||||
}
|
||||
|
||||
void PhysicalPrinterCollection::select_printer(const std::string& full_name)
|
||||
{
|
||||
std::string printer_name = PhysicalPrinter::get_short_name(full_name);
|
||||
auto it = this->find_printer_internal(printer_name);
|
||||
if (it == m_printers.end()) {
|
||||
unselect_printer();
|
||||
return;
|
||||
}
|
||||
|
||||
// update idx_selected
|
||||
m_idx_selected = it - m_printers.begin();
|
||||
|
||||
// update name of the currently selected preset
|
||||
if (printer_name == full_name)
|
||||
// use first preset in the list
|
||||
m_selected_preset = *it->preset_names.begin();
|
||||
else
|
||||
m_selected_preset = it->get_preset_name(full_name);
|
||||
}
|
||||
|
||||
void PhysicalPrinterCollection::select_printer(const PhysicalPrinter& printer)
|
||||
{
|
||||
return select_printer(printer.name);
|
||||
}
|
||||
|
||||
bool PhysicalPrinterCollection::has_selection() const
|
||||
{
|
||||
return m_idx_selected != size_t(-1);
|
||||
}
|
||||
|
||||
void PhysicalPrinterCollection::unselect_printer()
|
||||
{
|
||||
m_idx_selected = size_t(-1);
|
||||
m_selected_preset.clear();
|
||||
}
|
||||
|
||||
bool PhysicalPrinterCollection::is_selected(PhysicalPrinterCollection::ConstIterator it, const std::string& preset_name) const
|
||||
{
|
||||
return m_idx_selected == it - m_printers.begin() &&
|
||||
m_selected_preset == preset_name;
|
||||
}
|
||||
|
||||
|
||||
namespace PresetUtils {
|
||||
const VendorProfile::PrinterModel* system_printer_model(const Preset &preset)
|
||||
{
|
||||
|
|
@ -1646,6 +1825,26 @@ namespace PresetUtils {
|
|||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
std::string system_printer_bed_model(const Preset& preset)
|
||||
{
|
||||
std::string out;
|
||||
const VendorProfile::PrinterModel* pm = PresetUtils::system_printer_model(preset);
|
||||
if (pm != nullptr && !pm->bed_model.empty())
|
||||
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_model;
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string system_printer_bed_texture(const Preset& preset)
|
||||
{
|
||||
std::string out;
|
||||
const VendorProfile::PrinterModel* pm = PresetUtils::system_printer_model(preset);
|
||||
if (pm != nullptr && !pm->bed_texture.empty())
|
||||
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_texture;
|
||||
return out;
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
} // namespace PresetUtils
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
@ -8,27 +8,14 @@
|
|||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/property_tree/ptree_fwd.hpp>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/Semver.hpp"
|
||||
|
||||
class wxBitmap;
|
||||
class wxBitmapComboBox;
|
||||
class wxChoice;
|
||||
class wxItemContainer;
|
||||
class wxString;
|
||||
class wxWindow;
|
||||
#include "PrintConfig.hpp"
|
||||
#include "Semver.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class AppConfig;
|
||||
class PresetBundle;
|
||||
|
||||
namespace GUI {
|
||||
class BitmapCache;
|
||||
class PresetComboBox;
|
||||
}
|
||||
|
||||
enum ConfigFileType
|
||||
{
|
||||
CONFIG_FILE_TYPE_UNKNOWN,
|
||||
|
|
@ -236,7 +223,7 @@ public:
|
|||
static const std::vector<std::string>& sla_material_options();
|
||||
static const std::vector<std::string>& sla_print_options();
|
||||
|
||||
static void update_suffix_modified();
|
||||
static void update_suffix_modified(const std::string& new_suffix_modified);
|
||||
static const std::string& suffix_modified();
|
||||
static std::string remove_suffix_modified(const std::string& name);
|
||||
static void normalize(DynamicPrintConfig &config);
|
||||
|
|
@ -322,18 +309,6 @@ public:
|
|||
// returns true if the preset was deleted successfully.
|
||||
bool delete_preset(const std::string& name);
|
||||
|
||||
// Load default bitmap to be placed at the wxBitmapComboBox of a MainFrame.
|
||||
void load_bitmap_default(const std::string &file_name);
|
||||
|
||||
// Load "add new printer" bitmap to be placed at the wxBitmapComboBox of a MainFrame.
|
||||
void load_bitmap_add(const std::string &file_name);
|
||||
|
||||
// Compatible & incompatible marks, to be placed at the wxBitmapComboBox items.
|
||||
void set_bitmap_compatible (const wxBitmap *bmp) { m_bitmap_compatible = bmp; }
|
||||
void set_bitmap_incompatible(const wxBitmap *bmp) { m_bitmap_incompatible = bmp; }
|
||||
void set_bitmap_lock (const wxBitmap *bmp) { m_bitmap_lock = bmp; }
|
||||
void set_bitmap_lock_open (const wxBitmap *bmp) { m_bitmap_lock_open = bmp; }
|
||||
|
||||
// Enable / disable the "- default -" preset.
|
||||
void set_default_suppressed(bool default_suppressed);
|
||||
bool is_default_suppressed() const { return m_default_suppressed; }
|
||||
|
|
@ -446,18 +421,9 @@ public:
|
|||
// Return a sorted list of system preset names.
|
||||
std::vector<std::string> system_preset_names() const;
|
||||
|
||||
// Update the choice UI from the list of presets.
|
||||
// If show_incompatible, all presets are shown, otherwise only the compatible presets are shown.
|
||||
// If an incompatible preset is selected, it is shown as well.
|
||||
size_t update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible, const int em = 10);
|
||||
// Update the choice UI from the list of presets.
|
||||
// Only the compatible presets are shown.
|
||||
// If an incompatible preset is selected, it is shown as well.
|
||||
void update_plater_ui(GUI::PresetComboBox *ui);
|
||||
|
||||
// Update a dirty floag of the current preset, update the labels of the UI component accordingly.
|
||||
// Update a dirty flag of the current preset
|
||||
// Return true if the dirty flag changed.
|
||||
bool update_dirty_ui(wxBitmapComboBox *ui);
|
||||
bool update_dirty();
|
||||
|
||||
// Select a profile by its name. Return true if the selection changed.
|
||||
// Without force, the selection is only updated if the index changes.
|
||||
|
|
@ -467,16 +433,7 @@ public:
|
|||
// Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
|
||||
std::string path_from_name(const std::string &new_name) const;
|
||||
|
||||
void clear_bitmap_cache();
|
||||
|
||||
#ifdef __linux__
|
||||
static const char* separator_head() { return "------- "; }
|
||||
static const char* separator_tail() { return " -------"; }
|
||||
#else /* __linux__ */
|
||||
static const char* separator_head() { return "————— "; }
|
||||
static const char* separator_tail() { return " —————"; }
|
||||
#endif /* __linux__ */
|
||||
static wxString separator(const std::string &label);
|
||||
size_t num_default_presets() { return m_num_default_presets; }
|
||||
|
||||
protected:
|
||||
// Select a preset, if it exists. If it does not exist, select an invalid (-1) index.
|
||||
|
|
@ -503,8 +460,7 @@ private:
|
|||
// If a preset does not exist, an iterator is returned indicating where to insert a preset with the same name.
|
||||
std::deque<Preset>::iterator find_preset_internal(const std::string &name)
|
||||
{
|
||||
Preset key(m_type, name);
|
||||
auto it = std::lower_bound(m_presets.begin() + m_num_default_presets, m_presets.end(), key);
|
||||
auto it = Slic3r::lower_bound_by_predicate(m_presets.begin() + m_num_default_presets, m_presets.end(), [&name](const auto& l) { return l.name < name; });
|
||||
if (it == m_presets.end() || it->name != name) {
|
||||
// Preset has not been not found in the sorted list of non-default presets. Try the defaults.
|
||||
for (size_t i = 0; i < m_num_default_presets; ++ i)
|
||||
|
|
@ -547,23 +503,10 @@ private:
|
|||
// Is the "- default -" preset suppressed?
|
||||
bool m_default_suppressed = true;
|
||||
size_t m_num_default_presets = 0;
|
||||
// Compatible & incompatible marks, to be placed at the wxBitmapComboBox items of a Plater.
|
||||
// These bitmaps are not owned by PresetCollection, but by a PresetBundle.
|
||||
const wxBitmap *m_bitmap_compatible = nullptr;
|
||||
const wxBitmap *m_bitmap_incompatible = nullptr;
|
||||
const wxBitmap *m_bitmap_lock = nullptr;
|
||||
const wxBitmap *m_bitmap_lock_open = nullptr;
|
||||
// Marks placed at the wxBitmapComboBox of a MainFrame.
|
||||
// These bitmaps are owned by PresetCollection.
|
||||
wxBitmap *m_bitmap_main_frame;
|
||||
// "Add printer profile" icon, owned by PresetCollection.
|
||||
wxBitmap *m_bitmap_add;
|
||||
|
||||
// Path to the directory to store the config files into.
|
||||
std::string m_dir_path;
|
||||
|
||||
// Caching color bitmaps for the filament combo box.
|
||||
GUI::BitmapCache *m_bitmap_cache = nullptr;
|
||||
|
||||
// to access select_preset_by_name_strict()
|
||||
friend class PresetBundle;
|
||||
};
|
||||
|
|
@ -583,8 +526,214 @@ public:
|
|||
namespace PresetUtils {
|
||||
// PrinterModel of a system profile, from which this preset is derived, or null if it is not derived from a system profile.
|
||||
const VendorProfile::PrinterModel* system_printer_model(const Preset &preset);
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
std::string system_printer_bed_model(const Preset& preset);
|
||||
std::string system_printer_bed_texture(const Preset& preset);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
} // namespace PresetUtils
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
class PhysicalPrinter
|
||||
{
|
||||
public:
|
||||
PhysicalPrinter(const std::string& name, const DynamicPrintConfig &default_config) : name(name), config(default_config) {}
|
||||
PhysicalPrinter(const std::string& name, const DynamicPrintConfig &default_config, const Preset& preset);
|
||||
void set_name(const std::string &name);
|
||||
|
||||
// Name of the Physical Printer, usually derived form the file name.
|
||||
std::string name;
|
||||
// File name of the Physical Printer.
|
||||
std::string file;
|
||||
// Configuration data, loaded from a file, or set from the defaults.
|
||||
DynamicPrintConfig config;
|
||||
// set of presets used with this physical printer
|
||||
std::set<std::string> preset_names;
|
||||
|
||||
// Has this profile been loaded?
|
||||
bool loaded = false;
|
||||
|
||||
static std::string separator();
|
||||
static const std::vector<std::string>& printer_options();
|
||||
static const std::vector<std::string>& print_host_options();
|
||||
static std::vector<std::string> presets_with_print_host_information(const PrinterPresetCollection& printer_presets);
|
||||
static bool has_print_host_information(const DynamicPrintConfig& config);
|
||||
|
||||
const std::set<std::string>& get_preset_names() const;
|
||||
|
||||
bool has_empty_config() const;
|
||||
void update_preset_names_in_config();
|
||||
|
||||
void save() { this->config.save(this->file); }
|
||||
void save(const std::string& file_name_from, const std::string& file_name_to);
|
||||
|
||||
void update_from_preset(const Preset& preset);
|
||||
void update_from_config(const DynamicPrintConfig &new_config);
|
||||
|
||||
// add preset to the preset_names
|
||||
// return false, if preset with this name is already exist in the set
|
||||
bool add_preset(const std::string& preset_name);
|
||||
bool delete_preset(const std::string& preset_name);
|
||||
void reset_presets();
|
||||
|
||||
// Return a printer technology, return ptFFF if the printer technology is not set.
|
||||
static PrinterTechnology printer_technology(const DynamicPrintConfig& cfg) {
|
||||
auto* opt = cfg.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology");
|
||||
// The following assert may trigger when importing some legacy profile,
|
||||
// but it is safer to keep it here to capture the cases where the "printer_technology" key is queried, where it should not.
|
||||
return (opt == nullptr) ? ptFFF : opt->value;
|
||||
}
|
||||
PrinterTechnology printer_technology() const { return printer_technology(this->config); }
|
||||
|
||||
// Sort lexicographically by a preset name. The preset name shall be unique across a single PresetCollection.
|
||||
bool operator<(const PhysicalPrinter& other) const { return this->name < other.name; }
|
||||
|
||||
// get full printer name included a name of the preset
|
||||
std::string get_full_name(std::string preset_name) const;
|
||||
|
||||
// get printer name from the full name uncluded preset name
|
||||
static std::string get_short_name(std::string full_name);
|
||||
|
||||
// get preset name from the full name uncluded printer name
|
||||
static std::string get_preset_name(std::string full_name);
|
||||
|
||||
protected:
|
||||
friend class PhysicalPrinterCollection;
|
||||
};
|
||||
|
||||
|
||||
// ---------------------------------
|
||||
// *** PhysicalPrinterCollection ***
|
||||
// ---------------------------------
|
||||
|
||||
// Collections of physical printers
|
||||
class PhysicalPrinterCollection
|
||||
{
|
||||
public:
|
||||
PhysicalPrinterCollection(const std::vector<std::string>& keys);
|
||||
~PhysicalPrinterCollection() {}
|
||||
|
||||
typedef std::deque<PhysicalPrinter>::iterator Iterator;
|
||||
typedef std::deque<PhysicalPrinter>::const_iterator ConstIterator;
|
||||
Iterator begin() { return m_printers.begin(); }
|
||||
ConstIterator begin() const { return m_printers.cbegin(); }
|
||||
ConstIterator cbegin() const { return m_printers.cbegin(); }
|
||||
Iterator end() { return m_printers.end(); }
|
||||
ConstIterator end() const { return m_printers.cend(); }
|
||||
ConstIterator cend() const { return m_printers.cend(); }
|
||||
|
||||
bool empty() const {return m_printers.empty(); }
|
||||
|
||||
void reset(bool delete_files) {};
|
||||
|
||||
const std::deque<PhysicalPrinter>& operator()() const { return m_printers; }
|
||||
|
||||
// Load ini files of the particular type from the provided directory path.
|
||||
void load_printers(const std::string& dir_path, const std::string& subdir);
|
||||
void load_printers_from_presets(PrinterPresetCollection &printer_presets);
|
||||
|
||||
// Save the printer under a new name. If the name is different from the old one,
|
||||
// a new printer is stored into the list of printers.
|
||||
// New printer is activated.
|
||||
void save_printer(PhysicalPrinter& printer, const std::string& renamed_from = "");
|
||||
|
||||
// Delete the current preset, activate the first visible preset.
|
||||
// returns true if the preset was deleted successfully.
|
||||
bool delete_printer(const std::string& name);
|
||||
// Delete the selected preset
|
||||
// returns true if the preset was deleted successfully.
|
||||
bool delete_selected_printer();
|
||||
// Delete preset_name preset from all printers:
|
||||
// If there is last preset for the printer and first_check== false, then delete this printer
|
||||
// returns true if all presets were deleted successfully.
|
||||
bool delete_preset_from_printers(const std::string& preset_name);
|
||||
|
||||
// Get list of printers which have more than one preset and "preset_name" preset is one of them
|
||||
std::vector<std::string> get_printers_with_preset( const std::string &preset_name);
|
||||
// Get list of printers which has only "preset_name" preset
|
||||
std::vector<std::string> get_printers_with_only_preset( const std::string &preset_name);
|
||||
|
||||
// Return the selected preset, without the user modifications applied.
|
||||
PhysicalPrinter& get_selected_printer() { return m_printers[m_idx_selected]; }
|
||||
const PhysicalPrinter& get_selected_printer() const { return m_printers[m_idx_selected]; }
|
||||
|
||||
size_t get_selected_idx() const { return m_idx_selected; }
|
||||
// Returns the name of the selected preset, or an empty string if no preset is selected.
|
||||
std::string get_selected_printer_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().name; }
|
||||
// Returns the config of the selected printer, or nullptr if no printer is selected.
|
||||
DynamicPrintConfig* get_selected_printer_config() { return (m_idx_selected == size_t(-1)) ? nullptr : &(this->get_selected_printer().config); }
|
||||
// Returns the config of the selected printer, or nullptr if no printer is selected.
|
||||
PrinterTechnology get_selected_printer_technology() { return (m_idx_selected == size_t(-1)) ? PrinterTechnology::ptAny : this->get_selected_printer().printer_technology(); }
|
||||
|
||||
// Each physical printer can have a several related preset,
|
||||
// so, use the next functions to get an exact names of selections in the list:
|
||||
// Returns the full name of the selected printer, or an empty string if no preset is selected.
|
||||
std::string get_selected_full_printer_name() const;
|
||||
// Returns the printer model of the selected preset, or an empty string if no preset is selected.
|
||||
std::string get_selected_printer_preset_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : m_selected_preset; }
|
||||
|
||||
// Select printer by the full printer name, which contains name of printer, separator and name of selected preset
|
||||
// If full_name doesn't contain name of selected preset, then select first preset in the list for this printer
|
||||
void select_printer(const std::string& full_name);
|
||||
void select_printer(const PhysicalPrinter& printer);
|
||||
bool has_selection() const;
|
||||
void unselect_printer() ;
|
||||
bool is_selected(ConstIterator it, const std::string &preset_name) const;
|
||||
|
||||
// Return a printer by an index. If the printer is active, a temporary copy is returned.
|
||||
PhysicalPrinter& printer(size_t idx) { return m_printers[idx]; }
|
||||
const PhysicalPrinter& printer(size_t idx) const { return const_cast<PhysicalPrinterCollection*>(this)->printer(idx); }
|
||||
|
||||
// Return a preset by its name. If the preset is active, a temporary copy is returned.
|
||||
// If a preset is not found by its name, null is returned.
|
||||
PhysicalPrinter* find_printer(const std::string& name, bool first_visible_if_not_found = false);
|
||||
const PhysicalPrinter* find_printer(const std::string& name, bool first_visible_if_not_found = false) const
|
||||
{
|
||||
return const_cast<PhysicalPrinterCollection*>(this)->find_printer(name, first_visible_if_not_found);
|
||||
}
|
||||
|
||||
// Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
|
||||
std::string path_from_name(const std::string& new_name) const;
|
||||
|
||||
const DynamicPrintConfig& default_config() const { return m_default_config; }
|
||||
|
||||
private:
|
||||
PhysicalPrinterCollection& operator=(const PhysicalPrinterCollection& other);
|
||||
|
||||
// Find a preset position in the sorted list of presets.
|
||||
// The "-- default -- " preset is always the first, so it needs
|
||||
// to be handled differently.
|
||||
// If a preset does not exist, an iterator is returned indicating where to insert a preset with the same name.
|
||||
std::deque<PhysicalPrinter>::iterator find_printer_internal(const std::string& name)
|
||||
{
|
||||
return Slic3r::lower_bound_by_predicate(m_printers.begin(), m_printers.end(), [&name](const auto& l) { return l.name < name; });
|
||||
}
|
||||
std::deque<PhysicalPrinter>::const_iterator find_printer_internal(const std::string& name) const
|
||||
{
|
||||
return const_cast<PhysicalPrinterCollection*>(this)->find_printer_internal(name);
|
||||
}
|
||||
|
||||
PhysicalPrinter* find_printer_with_same_config( const DynamicPrintConfig &config);
|
||||
|
||||
// List of printers
|
||||
// Use deque to force the container to allocate an object per each entry,
|
||||
// so that the addresses of the presets don't change during resizing of the container.
|
||||
std::deque<PhysicalPrinter> m_printers;
|
||||
|
||||
// Default config for a physical printer containing all key/value pairs of PhysicalPrinter::printer_options().
|
||||
DynamicPrintConfig m_default_config;
|
||||
|
||||
// Selected printer.
|
||||
size_t m_idx_selected = size_t(-1);
|
||||
// The name of the preset which is currently select for this printer
|
||||
std::string m_selected_preset;
|
||||
|
||||
// Path to the directory to store the config files into.
|
||||
std::string m_dir_path;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Preset_hpp_ */
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
#include <cassert>
|
||||
|
||||
#include "PresetBundle.hpp"
|
||||
#include "BitmapCache.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "libslic3r.h"
|
||||
#include "Utils.hpp"
|
||||
#include "Model.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <fstream>
|
||||
#include <unordered_set>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
|
@ -21,17 +21,6 @@
|
|||
#include <boost/locale.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <wx/dcmemory.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/choice.h>
|
||||
#include <wx/bmpcbox.h>
|
||||
#include <wx/wupdlock.h>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
|
||||
// Store the print/filament/printer presets into a "presets" subdirectory of the Slic3rPE config dir.
|
||||
// This breaks compatibility with the upstream Slic3r if the --datadir is used to switch between the two versions.
|
||||
|
|
@ -53,15 +42,8 @@ PresetBundle::PresetBundle() :
|
|||
sla_materials(Preset::TYPE_SLA_MATERIAL, Preset::sla_material_options(), static_cast<const SLAMaterialConfig&>(SLAFullPrintConfig::defaults())),
|
||||
sla_prints(Preset::TYPE_SLA_PRINT, Preset::sla_print_options(), static_cast<const SLAPrintObjectConfig&>(SLAFullPrintConfig::defaults())),
|
||||
printers(Preset::TYPE_PRINTER, Preset::printer_options(), static_cast<const HostConfig&>(FullPrintConfig::defaults()), "- default FFF -"),
|
||||
m_bitmapCompatible(new wxBitmap),
|
||||
m_bitmapIncompatible(new wxBitmap),
|
||||
m_bitmapLock(new wxBitmap),
|
||||
m_bitmapLockOpen(new wxBitmap),
|
||||
m_bitmapCache(new GUI::BitmapCache)
|
||||
physical_printers(PhysicalPrinter::printer_options())
|
||||
{
|
||||
if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
|
||||
wxImage::AddHandler(new wxPNGHandler);
|
||||
|
||||
// The following keys are handled by the UI, they do not have a counterpart in any StaticPrintConfig derived classes,
|
||||
// therefore they need to be handled differently. As they have no counterpart in StaticPrintConfig, they are not being
|
||||
// initialized based on PrintConfigDef(), but to empty values (zeros, empty vectors, empty strings).
|
||||
|
|
@ -112,16 +94,6 @@ PresetBundle::PresetBundle() :
|
|||
preset.inherits();
|
||||
}
|
||||
|
||||
// Load the default preset bitmaps.
|
||||
// #ys_FIXME_to_delete we'll load them later, using em_unit()
|
||||
// this->prints .load_bitmap_default("cog");
|
||||
// this->sla_prints .load_bitmap_default("package_green.png");
|
||||
// this->filaments .load_bitmap_default("spool.png");
|
||||
// this->sla_materials.load_bitmap_default("package_green.png");
|
||||
// this->printers .load_bitmap_default("printer_empty.png");
|
||||
// this->printers .load_bitmap_add("add.png");
|
||||
// this->load_compatible_bitmaps();
|
||||
|
||||
// Re-activate the default presets, so their "edited" preset copies will be updated with the additional configuration values above.
|
||||
this->prints .select_preset(0);
|
||||
this->sla_prints .select_preset(0);
|
||||
|
|
@ -134,20 +106,6 @@ PresetBundle::PresetBundle() :
|
|||
|
||||
PresetBundle::~PresetBundle()
|
||||
{
|
||||
assert(m_bitmapCompatible != nullptr);
|
||||
assert(m_bitmapIncompatible != nullptr);
|
||||
assert(m_bitmapLock != nullptr);
|
||||
assert(m_bitmapLockOpen != nullptr);
|
||||
delete m_bitmapCompatible;
|
||||
m_bitmapCompatible = nullptr;
|
||||
delete m_bitmapIncompatible;
|
||||
m_bitmapIncompatible = nullptr;
|
||||
delete m_bitmapLock;
|
||||
m_bitmapLock = nullptr;
|
||||
delete m_bitmapLockOpen;
|
||||
m_bitmapLockOpen = nullptr;
|
||||
delete m_bitmapCache;
|
||||
m_bitmapCache = nullptr;
|
||||
}
|
||||
|
||||
void PresetBundle::reset(bool delete_files)
|
||||
|
|
@ -182,14 +140,16 @@ void PresetBundle::setup_directories()
|
|||
data_dir / "presets" / "filament",
|
||||
data_dir / "presets" / "sla_print",
|
||||
data_dir / "presets" / "sla_material",
|
||||
data_dir / "presets" / "printer"
|
||||
data_dir / "presets" / "printer",
|
||||
data_dir / "presets" / "physical_printer"
|
||||
#else
|
||||
// Store the print/filament/printer presets at the same location as the upstream Slic3r.
|
||||
data_dir / "print",
|
||||
data_dir / "filament",
|
||||
data_dir / "sla_print",
|
||||
data_dir / "sla_material",
|
||||
data_dir / "printer"
|
||||
data_dir / "printer",
|
||||
data_dir / "physical_printer"
|
||||
#endif
|
||||
};
|
||||
for (const boost::filesystem::path &path : paths) {
|
||||
|
|
@ -197,7 +157,7 @@ void PresetBundle::setup_directories()
|
|||
subdir.make_preferred();
|
||||
if (! boost::filesystem::is_directory(subdir) &&
|
||||
! boost::filesystem::create_directory(subdir))
|
||||
throw std::runtime_error(std::string("Slic3r was unable to create its data directory at ") + subdir.string());
|
||||
throw Slic3r::RuntimeError(std::string("Slic3r was unable to create its data directory at ") + subdir.string());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -239,10 +199,15 @@ void PresetBundle::load_presets(AppConfig &config, const std::string &preferred_
|
|||
} catch (const std::runtime_error &err) {
|
||||
errors_cummulative += err.what();
|
||||
}
|
||||
try {
|
||||
this->physical_printers.load_printers(dir_user_presets, "physical_printer");
|
||||
} catch (const std::runtime_error &err) {
|
||||
errors_cummulative += err.what();
|
||||
}
|
||||
this->update_multi_material_filament_presets();
|
||||
this->update_compatible(PresetSelectCompatibleType::Never);
|
||||
if (! errors_cummulative.empty())
|
||||
throw std::runtime_error(errors_cummulative);
|
||||
throw Slic3r::RuntimeError(errors_cummulative);
|
||||
|
||||
this->load_selections(config, preferred_model_id);
|
||||
}
|
||||
|
|
@ -362,6 +327,32 @@ const std::string& PresetBundle::get_preset_name_by_alias( const Preset::Type& p
|
|||
return presets.get_preset_name_by_alias(alias);
|
||||
}
|
||||
|
||||
void PresetBundle::save_changes_for_preset(const std::string& new_name, Preset::Type type,
|
||||
const std::vector<std::string>& unselected_options)
|
||||
{
|
||||
PresetCollection& presets = type == Preset::TYPE_PRINT ? prints :
|
||||
type == Preset::TYPE_SLA_PRINT ? sla_prints :
|
||||
type == Preset::TYPE_FILAMENT ? filaments :
|
||||
type == Preset::TYPE_SLA_MATERIAL ? sla_materials : printers;
|
||||
|
||||
// if we want to save just some from selected options
|
||||
if (!unselected_options.empty()) {
|
||||
// revert unselected options to the old values
|
||||
presets.get_edited_preset().config.apply_only(presets.get_selected_preset().config, unselected_options);
|
||||
}
|
||||
|
||||
// Save the preset into Slic3r::data_dir / presets / section_name / preset_name.ini
|
||||
presets.save_current_preset(new_name);
|
||||
// Mark the print & filament enabled if they are compatible with the currently selected preset.
|
||||
// If saving the preset changes compatibility with other presets, keep the now incompatible dependent presets selected, however with a "red flag" icon showing that they are no more compatible.
|
||||
update_compatible(PresetSelectCompatibleType::Never);
|
||||
|
||||
if (type == Preset::TYPE_FILAMENT) {
|
||||
// synchronize the first filament presets.
|
||||
set_filament_preset(0, filaments.get_selected_preset_name());
|
||||
}
|
||||
}
|
||||
|
||||
void PresetBundle::load_installed_filaments(AppConfig &config)
|
||||
{
|
||||
if (! config.has_section(AppConfig::SECTION_FILAMENTS)) {
|
||||
|
|
@ -465,6 +456,13 @@ void PresetBundle::load_selections(AppConfig &config, const std::string &preferr
|
|||
// exist.
|
||||
this->update_compatible(PresetSelectCompatibleType::Always);
|
||||
this->update_multi_material_filament_presets();
|
||||
|
||||
// Parse the initial physical printer name.
|
||||
std::string initial_physical_printer_name = remove_ini_suffix(config.get("extras", "physical_printer"));
|
||||
|
||||
// Activate physical printer from the config
|
||||
if (!initial_physical_printer_name.empty())
|
||||
physical_printers.select_printer(initial_physical_printer_name);
|
||||
}
|
||||
|
||||
// Export selections (current print, current filaments, current printer) into config.ini
|
||||
|
|
@ -484,36 +482,8 @@ void PresetBundle::export_selections(AppConfig &config)
|
|||
config.set("presets", "sla_print", sla_prints.get_selected_preset_name());
|
||||
config.set("presets", "sla_material", sla_materials.get_selected_preset_name());
|
||||
config.set("presets", "printer", printers.get_selected_preset_name());
|
||||
}
|
||||
|
||||
void PresetBundle::load_compatible_bitmaps()
|
||||
{
|
||||
*m_bitmapCompatible = create_scaled_bitmap("flag_green");
|
||||
*m_bitmapIncompatible = create_scaled_bitmap("flag_red");
|
||||
*m_bitmapLock = create_scaled_bitmap("lock_closed");
|
||||
*m_bitmapLockOpen = create_scaled_bitmap("lock_open");
|
||||
|
||||
prints .set_bitmap_compatible(m_bitmapCompatible);
|
||||
filaments .set_bitmap_compatible(m_bitmapCompatible);
|
||||
sla_prints .set_bitmap_compatible(m_bitmapCompatible);
|
||||
sla_materials.set_bitmap_compatible(m_bitmapCompatible);
|
||||
|
||||
prints .set_bitmap_incompatible(m_bitmapIncompatible);
|
||||
filaments .set_bitmap_incompatible(m_bitmapIncompatible);
|
||||
sla_prints .set_bitmap_incompatible(m_bitmapIncompatible);
|
||||
sla_materials.set_bitmap_incompatible(m_bitmapIncompatible);
|
||||
|
||||
prints .set_bitmap_lock(m_bitmapLock);
|
||||
filaments .set_bitmap_lock(m_bitmapLock);
|
||||
sla_prints .set_bitmap_lock(m_bitmapLock);
|
||||
sla_materials.set_bitmap_lock(m_bitmapLock);
|
||||
printers .set_bitmap_lock(m_bitmapLock);
|
||||
|
||||
prints .set_bitmap_lock_open(m_bitmapLock);
|
||||
filaments .set_bitmap_lock_open(m_bitmapLock);
|
||||
sla_prints .set_bitmap_lock_open(m_bitmapLock);
|
||||
sla_materials.set_bitmap_lock_open(m_bitmapLock);
|
||||
printers .set_bitmap_lock_open(m_bitmapLock);
|
||||
config.set("extras", "physical_printer", physical_printers.get_selected_full_printer_name());
|
||||
}
|
||||
|
||||
DynamicPrintConfig PresetBundle::full_config() const
|
||||
|
|
@ -709,21 +679,21 @@ void PresetBundle::load_config_file(const std::string &path)
|
|||
boost::nowide::ifstream ifs(path);
|
||||
boost::property_tree::read_ini(ifs, tree);
|
||||
} catch (const std::ifstream::failure &err) {
|
||||
throw std::runtime_error(std::string("The Config Bundle cannot be loaded: ") + path + "\n\tReason: " + err.what());
|
||||
throw Slic3r::RuntimeError(std::string("The Config Bundle cannot be loaded: ") + path + "\n\tReason: " + err.what());
|
||||
} catch (const boost::property_tree::file_parser_error &err) {
|
||||
throw std::runtime_error((boost::format("Failed loading the Config Bundle \"%1%\": %2% at line %3%")
|
||||
throw Slic3r::RuntimeError((boost::format("Failed loading the Config Bundle \"%1%\": %2% at line %3%")
|
||||
% err.filename() % err.message() % err.line()).str());
|
||||
} catch (const std::runtime_error &err) {
|
||||
throw std::runtime_error(std::string("Failed loading the preset file: ") + path + "\n\tReason: " + err.what());
|
||||
throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + path + "\n\tReason: " + err.what());
|
||||
}
|
||||
|
||||
// 2) Continue based on the type of the configuration file.
|
||||
ConfigFileType config_file_type = guess_config_file_type(tree);
|
||||
switch (config_file_type) {
|
||||
case CONFIG_FILE_TYPE_UNKNOWN:
|
||||
throw std::runtime_error(std::string("Unknown configuration file type: ") + path);
|
||||
throw Slic3r::RuntimeError(std::string("Unknown configuration file type: ") + path);
|
||||
case CONFIG_FILE_TYPE_APP_CONFIG:
|
||||
throw std::runtime_error(std::string("Invalid configuration file: ") + path + ". This is an application config file.");
|
||||
throw Slic3r::RuntimeError(std::string("Invalid configuration file: ") + path + ". This is an application config file.");
|
||||
case CONFIG_FILE_TYPE_CONFIG:
|
||||
{
|
||||
// Initialize a config from full defaults.
|
||||
|
|
@ -886,8 +856,6 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
|
|||
// 4) Load the project config values (the per extruder wipe matrix etc).
|
||||
this->project_config.apply_only(config, s_project_options);
|
||||
|
||||
update_custom_gcode_per_print_z_from_config(GUI::wxGetApp().plater()->model().custom_gcode_per_print_z, &this->project_config);
|
||||
|
||||
break;
|
||||
}
|
||||
case ptSLA:
|
||||
|
|
@ -1544,207 +1512,11 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst
|
|||
// an optional "(modified)" suffix will be removed from the filament name.
|
||||
void PresetBundle::set_filament_preset(size_t idx, const std::string &name)
|
||||
{
|
||||
if (name.find_first_of(PresetCollection::separator_head()) == 0)
|
||||
return;
|
||||
|
||||
if (idx >= filament_presets.size())
|
||||
if (idx >= filament_presets.size())
|
||||
filament_presets.resize(idx + 1, filaments.default_preset().name);
|
||||
filament_presets[idx] = Preset::remove_suffix_modified(name);
|
||||
}
|
||||
|
||||
void PresetBundle::load_default_preset_bitmaps()
|
||||
{
|
||||
// Clear bitmap cache, before load new scaled default preset bitmaps
|
||||
m_bitmapCache->clear();
|
||||
this->prints.clear_bitmap_cache();
|
||||
this->sla_prints.clear_bitmap_cache();
|
||||
this->filaments.clear_bitmap_cache();
|
||||
this->sla_materials.clear_bitmap_cache();
|
||||
this->printers.clear_bitmap_cache();
|
||||
|
||||
this->prints.load_bitmap_default("cog");
|
||||
this->sla_prints.load_bitmap_default("cog");
|
||||
this->filaments.load_bitmap_default("spool.png");
|
||||
this->sla_materials.load_bitmap_default("resin");
|
||||
this->printers.load_bitmap_default("printer");
|
||||
this->printers.load_bitmap_add("add.png");
|
||||
this->load_compatible_bitmaps();
|
||||
}
|
||||
|
||||
void PresetBundle::update_plater_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui)
|
||||
{
|
||||
if (ui == nullptr || this->printers.get_edited_preset().printer_technology() == ptSLA ||
|
||||
this->filament_presets.size() <= idx_extruder )
|
||||
return;
|
||||
|
||||
unsigned char rgb[3];
|
||||
std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder);
|
||||
if (!m_bitmapCache->parse_color(extruder_color, rgb))
|
||||
// Extruder color is not defined.
|
||||
extruder_color.clear();
|
||||
|
||||
// Fill in the list from scratch.
|
||||
ui->Freeze();
|
||||
ui->Clear();
|
||||
size_t selected_preset_item = INT_MAX; // some value meaning that no one item is selected
|
||||
|
||||
const Preset *selected_preset = this->filaments.find_preset(this->filament_presets[idx_extruder]);
|
||||
// Show wide icons if the currently selected preset is not compatible with the current printer,
|
||||
// and draw a red flag in front of the selected preset.
|
||||
bool wide_icons = selected_preset != nullptr && ! selected_preset->is_compatible && m_bitmapIncompatible != nullptr;
|
||||
assert(selected_preset != nullptr);
|
||||
std::map<wxString, wxBitmap*> nonsys_presets;
|
||||
wxString selected_str = "";
|
||||
if (!this->filaments().front().is_visible)
|
||||
ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap));
|
||||
|
||||
/* It's supposed that standard size of an icon is 16px*16px for 100% scaled display.
|
||||
* So set sizes for solid_colored icons used for filament preset
|
||||
* and scale them in respect to em_unit value
|
||||
*/
|
||||
const float scale_f = ui->em_unit() * 0.1f;
|
||||
|
||||
// To avoid the errors of number rounding for different combination of monitor configuration,
|
||||
// let use scaled 8px, as a smallest icon unit
|
||||
const int icon_unit = 8 * scale_f + 0.5f;
|
||||
const int normal_icon_width = 2 * icon_unit; //16 * scale_f + 0.5f;
|
||||
const int thin_icon_width = icon_unit; //8 * scale_f + 0.5f;
|
||||
const int wide_icon_width = 3 * icon_unit; //24 * scale_f + 0.5f;
|
||||
|
||||
const int space_icon_width = 2 * scale_f + 0.5f;
|
||||
|
||||
// To avoid asserts, each added bitmap to wxBitmapCombobox should be the same size, so
|
||||
// set a bitmap height to m_bitmapLock->GetHeight()
|
||||
//
|
||||
// To avoid asserts, each added bitmap to wxBitmapCombobox should be the same size.
|
||||
// But for some display scaling (for example 125% or 175%) normal_icon_width differs from icon width.
|
||||
// So:
|
||||
// for nonsystem presets set a width of empty bitmap to m_bitmapLock->GetWidth()
|
||||
// for compatible presets set a width of empty bitmap to m_bitmapIncompatible->GetWidth()
|
||||
//
|
||||
// Note, under OSX we should use a Scaled Height/Width because of Retina scale
|
||||
#ifdef __APPLE__
|
||||
const int icon_height = m_bitmapLock->GetScaledHeight();
|
||||
const int lock_icon_width = m_bitmapLock->GetScaledWidth();
|
||||
const int flag_icon_width = m_bitmapIncompatible->GetScaledWidth();
|
||||
#else
|
||||
const int icon_height = m_bitmapLock->GetHeight();
|
||||
const int lock_icon_width = m_bitmapLock->GetWidth();
|
||||
const int flag_icon_width = m_bitmapIncompatible->GetWidth();
|
||||
#endif
|
||||
|
||||
wxString tooltip = "";
|
||||
|
||||
for (int i = this->filaments().front().is_visible ? 0 : 1; i < int(this->filaments().size()); ++i) {
|
||||
const Preset &preset = this->filaments.preset(i);
|
||||
bool selected = this->filament_presets[idx_extruder] == preset.name;
|
||||
if (! preset.is_visible || (! preset.is_compatible && ! selected))
|
||||
continue;
|
||||
// Assign an extruder color to the selected item if the extruder color is defined.
|
||||
std::string filament_rgb = preset.config.opt_string("filament_colour", 0);
|
||||
std::string extruder_rgb = (selected && !extruder_color.empty()) ? extruder_color : filament_rgb;
|
||||
bool single_bar = filament_rgb == extruder_rgb;
|
||||
std::string bitmap_key = single_bar ? filament_rgb : filament_rgb + extruder_rgb;
|
||||
// If the filament preset is not compatible and there is a "red flag" icon loaded, show it left
|
||||
// to the filament color image.
|
||||
if (wide_icons)
|
||||
bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt";
|
||||
bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst";
|
||||
if (preset.is_dirty)
|
||||
bitmap_key += ",drty";
|
||||
wxBitmap *bitmap = m_bitmapCache->find(bitmap_key);
|
||||
if (bitmap == nullptr) {
|
||||
// Create the bitmap with color bars.
|
||||
std::vector<wxBitmap> bmps;
|
||||
if (wide_icons)
|
||||
// Paint a red flag for incompatible presets.
|
||||
bmps.emplace_back(preset.is_compatible ? m_bitmapCache->mkclear(flag_icon_width, icon_height) : *m_bitmapIncompatible);
|
||||
// Paint the color bars.
|
||||
m_bitmapCache->parse_color(filament_rgb, rgb);
|
||||
bmps.emplace_back(m_bitmapCache->mksolid(single_bar ? wide_icon_width : normal_icon_width, icon_height, rgb));
|
||||
if (! single_bar) {
|
||||
m_bitmapCache->parse_color(extruder_rgb, rgb);
|
||||
bmps.emplace_back(m_bitmapCache->mksolid(thin_icon_width, icon_height, rgb));
|
||||
}
|
||||
// Paint a lock at the system presets.
|
||||
bmps.emplace_back(m_bitmapCache->mkclear(space_icon_width, icon_height));
|
||||
bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmapLock : m_bitmapCache->mkclear(lock_icon_width, icon_height));
|
||||
// (preset.is_dirty ? *m_bitmapLockOpen : *m_bitmapLock) : m_bitmapCache->mkclear(16, 16));
|
||||
bitmap = m_bitmapCache->insert(bitmap_key, bmps);
|
||||
}
|
||||
|
||||
const std::string name = preset.alias.empty() ? preset.name : preset.alias;
|
||||
if (preset.is_default || preset.is_system) {
|
||||
ui->Append(wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()),
|
||||
(bitmap == 0) ? wxNullBitmap : *bitmap);
|
||||
if (selected ||
|
||||
// just in case: mark selected_preset_item as a first added element
|
||||
selected_preset_item == INT_MAX ) {
|
||||
selected_preset_item = ui->GetCount() - 1;
|
||||
tooltip = wxString::FromUTF8(preset.name.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nonsys_presets.emplace(wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()),
|
||||
(bitmap == 0) ? &wxNullBitmap : bitmap);
|
||||
if (selected) {
|
||||
selected_str = wxString::FromUTF8((/*preset.*/name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str());
|
||||
tooltip = wxString::FromUTF8(preset.name.c_str());
|
||||
}
|
||||
}
|
||||
if (preset.is_default)
|
||||
ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap));
|
||||
}
|
||||
|
||||
if (!nonsys_presets.empty())
|
||||
{
|
||||
ui->set_label_marker(ui->Append(PresetCollection::separator(L("User presets")), wxNullBitmap));
|
||||
for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
|
||||
ui->Append(it->first, *it->second);
|
||||
if (it->first == selected_str ||
|
||||
// just in case: mark selected_preset_item as a first added element
|
||||
selected_preset_item == INT_MAX) {
|
||||
selected_preset_item = ui->GetCount() - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string bitmap_key = "";
|
||||
if (wide_icons)
|
||||
bitmap_key += "wide,";
|
||||
bitmap_key += "edit_preset_list";
|
||||
wxBitmap* bmp = m_bitmapCache->find(bitmap_key);
|
||||
if (bmp == nullptr) {
|
||||
// Create the bitmap with color bars.
|
||||
std::vector<wxBitmap> bmps;
|
||||
if (wide_icons)
|
||||
// Paint a red flag for incompatible presets.
|
||||
bmps.emplace_back(m_bitmapCache->mkclear(flag_icon_width, icon_height));
|
||||
// Paint the color bars + a lock at the system presets.
|
||||
bmps.emplace_back(m_bitmapCache->mkclear(wide_icon_width+space_icon_width, icon_height));
|
||||
bmps.emplace_back(create_scaled_bitmap("edit_uni"));
|
||||
bmp = m_bitmapCache->insert(bitmap_key, bmps);
|
||||
}
|
||||
ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add/Remove filaments")), *bmp), GUI::PresetComboBox::LABEL_ITEM_WIZARD_FILAMENTS);
|
||||
|
||||
/* But, if selected_preset_item is still equal to INT_MAX, it means that
|
||||
* there is no presets added to the list.
|
||||
* So, select last combobox item ("Add/Remove filaments")
|
||||
*/
|
||||
if (selected_preset_item == INT_MAX)
|
||||
selected_preset_item = ui->GetCount() - 1;
|
||||
|
||||
ui->SetSelection(selected_preset_item);
|
||||
ui->SetToolTip(tooltip.IsEmpty() ? ui->GetString(selected_preset_item) : tooltip);
|
||||
ui->check_selection(selected_preset_item);
|
||||
ui->Thaw();
|
||||
|
||||
// Update control min size after rescale (changed Display DPI under MSW)
|
||||
if (ui->GetMinWidth() != 20 * ui->em_unit())
|
||||
ui->SetMinSize(wxSize(20 * ui->em_unit(), ui->GetSize().GetHeight()));
|
||||
}
|
||||
|
||||
void PresetBundle::set_default_suppressed(bool default_suppressed)
|
||||
{
|
||||
prints.set_default_suppressed(default_suppressed);
|
||||
|
|
@ -1,22 +1,15 @@
|
|||
#ifndef slic3r_PresetBundle_hpp_
|
||||
#define slic3r_PresetBundle_hpp_
|
||||
|
||||
#include "AppConfig.hpp"
|
||||
#include "Preset.hpp"
|
||||
#include "AppConfig.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
class wxWindow;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace GUI {
|
||||
class BitmapCache;
|
||||
};
|
||||
|
||||
// Bundle of Print + Filament + Printer presets.
|
||||
class PresetBundle
|
||||
{
|
||||
|
|
@ -45,6 +38,7 @@ public:
|
|||
PresetCollection& materials(PrinterTechnology pt) { return pt == ptFFF ? this->filaments : this->sla_materials; }
|
||||
const PresetCollection& materials(PrinterTechnology pt) const { return pt == ptFFF ? this->filaments : this->sla_materials; }
|
||||
PrinterPresetCollection printers;
|
||||
PhysicalPrinterCollection physical_printers;
|
||||
// Filament preset names for a multi-extruder or multi-material print.
|
||||
// extruders.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size()
|
||||
std::vector<std::string> filament_presets;
|
||||
|
|
@ -110,9 +104,6 @@ public:
|
|||
// Export a config bundle file containing all the presets and the names of the active presets.
|
||||
void export_configbundle(const std::string &path, bool export_system_settings = false);
|
||||
|
||||
// Update a filament selection combo box on the plater for an idx_extruder.
|
||||
void update_plater_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui);
|
||||
|
||||
// Enable / disable the "- default -" preset.
|
||||
void set_default_suppressed(bool default_suppressed);
|
||||
|
||||
|
|
@ -132,8 +123,6 @@ public:
|
|||
void update_compatible(PresetSelectCompatibleType select_other_print_if_incompatible, PresetSelectCompatibleType select_other_filament_if_incompatible);
|
||||
void update_compatible(PresetSelectCompatibleType select_other_if_incompatible) { this->update_compatible(select_other_if_incompatible, select_other_if_incompatible); }
|
||||
|
||||
void load_default_preset_bitmaps();
|
||||
|
||||
// Set the is_visible flag for printer vendors, printer models and printer variants
|
||||
// based on the user configuration.
|
||||
// If the "vendor" section is missing, enable all models and variants of the particular vendor.
|
||||
|
|
@ -141,6 +130,10 @@ public:
|
|||
|
||||
const std::string& get_preset_name_by_alias(const Preset::Type& preset_type, const std::string& alias) const;
|
||||
|
||||
// Save current preset of a required type under a new name. If the name is different from the old one,
|
||||
// Unselected option would be reverted to the beginning values
|
||||
void save_changes_for_preset(const std::string& new_name, Preset::Type type, const std::vector<std::string>& unselected_options);
|
||||
|
||||
static const char *PRUSA_BUNDLE;
|
||||
private:
|
||||
std::string load_system_presets();
|
||||
|
|
@ -163,21 +156,9 @@ private:
|
|||
// If it is not an external config, then the config will be stored into the user profile directory.
|
||||
void load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config);
|
||||
void load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree);
|
||||
void load_compatible_bitmaps();
|
||||
|
||||
DynamicPrintConfig full_fff_config() const;
|
||||
DynamicPrintConfig full_sla_config() const;
|
||||
|
||||
// Indicator, that the preset is compatible with the selected printer.
|
||||
wxBitmap *m_bitmapCompatible;
|
||||
// Indicator, that the preset is NOT compatible with the selected printer.
|
||||
wxBitmap *m_bitmapIncompatible;
|
||||
// Indicator, that the preset is system and not modified.
|
||||
wxBitmap *m_bitmapLock;
|
||||
// Indicator, that the preset is system and user modified.
|
||||
wxBitmap *m_bitmapLockOpen;
|
||||
// Caching color bitmaps for the filament combo box.
|
||||
GUI::BitmapCache *m_bitmapCache;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#include "clipper/clipper_z.hpp"
|
||||
|
||||
#include "Exception.hpp"
|
||||
#include "Print.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
|
|
@ -404,6 +405,7 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst,
|
|||
mv_dst.name = mv_src.name;
|
||||
static_cast<DynamicPrintConfig&>(mv_dst.config) = static_cast<const DynamicPrintConfig&>(mv_src.config);
|
||||
mv_dst.m_supported_facets = mv_src.m_supported_facets;
|
||||
mv_dst.m_seam_facets = mv_src.m_seam_facets;
|
||||
//FIXME what to do with the materials?
|
||||
// mv_dst.m_material_id = mv_src.m_material_id;
|
||||
++ i_src;
|
||||
|
|
@ -867,6 +869,9 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||
model_volume_list_update_supports(model_object, model_object_new);
|
||||
}
|
||||
}
|
||||
if (model_custom_seam_data_changed(model_object, model_object_new)) {
|
||||
update_apply_status(this->invalidate_step(psGCodeExport));
|
||||
}
|
||||
if (! model_parts_differ && ! modifiers_differ) {
|
||||
// Synchronize Object's config.
|
||||
bool object_config_changed = model_object.config != model_object_new.config;
|
||||
|
|
@ -1504,7 +1509,7 @@ BoundingBox Print::total_bounding_box() const
|
|||
double Print::skirt_first_layer_height() const
|
||||
{
|
||||
if (m_objects.empty())
|
||||
throw std::invalid_argument("skirt_first_layer_height() can't be called without PrintObjects");
|
||||
throw Slic3r::InvalidArgument("skirt_first_layer_height() can't be called without PrintObjects");
|
||||
return m_objects.front()->config().get_abs_value("first_layer_height");
|
||||
}
|
||||
|
||||
|
|
@ -1580,7 +1585,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
|
|||
// Slicing process, running at a background thread.
|
||||
void Print::process()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Staring the slicing process." << log_memory_info();
|
||||
BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info();
|
||||
for (PrintObject *obj : m_objects)
|
||||
obj->make_perimeters();
|
||||
this->set_status(70, L("Infilling layers"));
|
||||
|
|
@ -1600,7 +1605,7 @@ void Print::process()
|
|||
// Initialize the tool ordering, so it could be used by the G-code preview slider for planning tool changes and filament switches.
|
||||
m_tool_ordering = ToolOrdering(*this, -1, false);
|
||||
if (m_tool_ordering.empty() || m_tool_ordering.last_extruder() == unsigned(-1))
|
||||
throw std::runtime_error("The print is empty. The model is not printable with current print settings.");
|
||||
throw Slic3r::SlicingError("The print is empty. The model is not printable with current print settings.");
|
||||
}
|
||||
this->set_done(psWipeTower);
|
||||
}
|
||||
|
|
@ -1633,13 +1638,21 @@ void Print::process()
|
|||
// The export_gcode may die for various reasons (fails to process output_filename_format,
|
||||
// write error into the G-code, cannot execute post-processing scripts).
|
||||
// It is up to the caller to show an error message.
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
std::string Print::export_gcode(const std::string& path_template, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||
#else
|
||||
std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
{
|
||||
// output everything to a G-code file
|
||||
// The following call may die if the output_filename_format template substitution fails.
|
||||
std::string path = this->output_filepath(path_template);
|
||||
std::string message;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
if (!path.empty() && result == nullptr) {
|
||||
#else
|
||||
if (! path.empty() && preview_data == nullptr) {
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
// Only show the path if preview_data is not set -> running from command line.
|
||||
message = L("Exporting G-code");
|
||||
message += " to ";
|
||||
|
|
@ -1650,7 +1663,11 @@ std::string Print::export_gcode(const std::string& path_template, GCodePreviewDa
|
|||
|
||||
// The following line may die for multiple reasons.
|
||||
GCode gcode;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
gcode.do_export(this, path.c_str(), result, thumbnail_cb);
|
||||
#else
|
||||
gcode.do_export(this, path.c_str(), preview_data, thumbnail_cb);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
return path.c_str();
|
||||
}
|
||||
|
||||
|
|
@ -2181,16 +2198,16 @@ DynamicConfig PrintStatistics::config() const
|
|||
DynamicConfig config;
|
||||
std::string normal_print_time = short_time(this->estimated_normal_print_time);
|
||||
std::string silent_print_time = short_time(this->estimated_silent_print_time);
|
||||
config.set_key_value("print_time", new ConfigOptionString(normal_print_time));
|
||||
config.set_key_value("normal_print_time", new ConfigOptionString(normal_print_time));
|
||||
config.set_key_value("silent_print_time", new ConfigOptionString(silent_print_time));
|
||||
config.set_key_value("used_filament", new ConfigOptionFloat (this->total_used_filament / 1000.));
|
||||
config.set_key_value("extruded_volume", new ConfigOptionFloat (this->total_extruded_volume));
|
||||
config.set_key_value("total_cost", new ConfigOptionFloat (this->total_cost));
|
||||
config.set_key_value("print_time", new ConfigOptionString(normal_print_time));
|
||||
config.set_key_value("normal_print_time", new ConfigOptionString(normal_print_time));
|
||||
config.set_key_value("silent_print_time", new ConfigOptionString(silent_print_time));
|
||||
config.set_key_value("used_filament", new ConfigOptionFloat(this->total_used_filament / 1000.));
|
||||
config.set_key_value("extruded_volume", new ConfigOptionFloat(this->total_extruded_volume));
|
||||
config.set_key_value("total_cost", new ConfigOptionFloat(this->total_cost));
|
||||
config.set_key_value("total_toolchanges", new ConfigOptionInt(this->total_toolchanges));
|
||||
config.set_key_value("total_weight", new ConfigOptionFloat (this->total_weight));
|
||||
config.set_key_value("total_wipe_tower_cost", new ConfigOptionFloat (this->total_wipe_tower_cost));
|
||||
config.set_key_value("total_wipe_tower_filament", new ConfigOptionFloat (this->total_wipe_tower_filament));
|
||||
config.set_key_value("total_weight", new ConfigOptionFloat(this->total_weight));
|
||||
config.set_key_value("total_wipe_tower_cost", new ConfigOptionFloat(this->total_wipe_tower_cost));
|
||||
config.set_key_value("total_wipe_tower_filament", new ConfigOptionFloat(this->total_wipe_tower_filament));
|
||||
return config;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@
|
|||
#include "GCode/ToolOrdering.hpp"
|
||||
#include "GCode/WipeTower.hpp"
|
||||
#include "GCode/ThumbnailData.hpp"
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include "GCode/GCodeProcessor.hpp"
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
#include "libslic3r.h"
|
||||
|
||||
|
|
@ -20,11 +23,17 @@ class Print;
|
|||
class PrintObject;
|
||||
class ModelObject;
|
||||
class GCode;
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
class GCodePreviewData;
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
enum class SlicingMode : uint32_t;
|
||||
class Layer;
|
||||
class SupportLayer;
|
||||
|
||||
namespace FillAdaptive_Internal {
|
||||
struct Octree;
|
||||
};
|
||||
|
||||
// Print step IDs for keeping track of the print state.
|
||||
enum PrintStep {
|
||||
psSkirt,
|
||||
|
|
@ -186,10 +195,8 @@ public:
|
|||
std::vector<ExPolygons> slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); }
|
||||
std::vector<ExPolygons> slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); }
|
||||
|
||||
// Helpers to project custom supports on slices
|
||||
void project_and_append_custom_supports(FacetSupportType type, std::vector<ExPolygons>& expolys) const;
|
||||
void project_and_append_custom_enforcers(std::vector<ExPolygons>& enforcers) const { project_and_append_custom_supports(FacetSupportType::ENFORCER, enforcers); }
|
||||
void project_and_append_custom_blockers(std::vector<ExPolygons>& blockers) const { project_and_append_custom_supports(FacetSupportType::BLOCKER, blockers); }
|
||||
// Helpers to project custom facets on slices
|
||||
void project_and_append_custom_facets(bool seam, EnforcerBlockerType type, std::vector<ExPolygons>& expolys) const;
|
||||
|
||||
private:
|
||||
// to be called from Print only.
|
||||
|
|
@ -232,6 +239,7 @@ private:
|
|||
void discover_horizontal_shells();
|
||||
void combine_infill();
|
||||
void _generate_support_material();
|
||||
std::pair<std::unique_ptr<FillAdaptive_Internal::Octree>, std::unique_ptr<FillAdaptive_Internal::Octree>> prepare_adaptive_infill_data();
|
||||
|
||||
// XYZ in scaled coordinates
|
||||
Vec3crd m_size;
|
||||
|
|
@ -300,8 +308,10 @@ struct PrintStatistics
|
|||
PrintStatistics() { clear(); }
|
||||
std::string estimated_normal_print_time;
|
||||
std::string estimated_silent_print_time;
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
std::vector<std::pair<CustomGCode::Type, std::string>> estimated_normal_custom_gcode_print_times;
|
||||
std::vector<std::pair<CustomGCode::Type, std::string>> estimated_silent_custom_gcode_print_times;
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
double total_used_filament;
|
||||
double total_extruded_volume;
|
||||
double total_cost;
|
||||
|
|
@ -319,10 +329,12 @@ struct PrintStatistics
|
|||
std::string finalize_output_path(const std::string &path_in) const;
|
||||
|
||||
void clear() {
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
estimated_normal_print_time.clear();
|
||||
estimated_silent_print_time.clear();
|
||||
estimated_normal_custom_gcode_print_times.clear();
|
||||
estimated_silent_custom_gcode_print_times.clear();
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
total_used_filament = 0.;
|
||||
total_extruded_volume = 0.;
|
||||
total_cost = 0.;
|
||||
|
|
@ -362,7 +374,11 @@ public:
|
|||
void process() override;
|
||||
// Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file.
|
||||
// If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r).
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
std::string export_gcode(const std::string& path_template, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
|
||||
#else
|
||||
std::string export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
// methods for handling state
|
||||
bool is_step_done(PrintStep step) const { return Inherited::is_step_done(step); }
|
||||
|
|
@ -399,7 +415,7 @@ public:
|
|||
// in the notification center.
|
||||
const PrintObject* get_object(ObjectID object_id) const {
|
||||
auto it = std::find_if(m_objects.begin(), m_objects.end(),
|
||||
[object_id](const PrintObject *obj) { return *static_cast<const ObjectID*>(obj) == object_id; });
|
||||
[object_id](const PrintObject *obj) { return obj->id() == object_id; });
|
||||
return (it == m_objects.end()) ? nullptr : *it;
|
||||
}
|
||||
const PrintRegionPtrs& regions() const { return m_regions; }
|
||||
|
|
@ -417,6 +433,7 @@ public:
|
|||
const Polygon& first_layer_convex_hull() const { return m_first_layer_convex_hull; }
|
||||
|
||||
const PrintStatistics& print_statistics() const { return m_print_statistics; }
|
||||
PrintStatistics& print_statistics() { return m_print_statistics; }
|
||||
|
||||
// Wipe tower support.
|
||||
bool has_wipe_tower() const;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include "Exception.hpp"
|
||||
#include "PrintBase.hpp"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
|
@ -68,7 +69,7 @@ std::string PrintBase::output_filename(const std::string &format, const std::str
|
|||
filename = boost::filesystem::change_extension(filename, default_ext);
|
||||
return filename.string();
|
||||
} catch (std::runtime_error &err) {
|
||||
throw std::runtime_error(L("Failed processing of the output_filename_format template.") + "\n" + err.what());
|
||||
throw Slic3r::RuntimeError(L("Failed processing of the output_filename_format template.") + "\n" + err.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -93,7 +94,7 @@ void PrintBase::status_update_warnings(ObjectID object_id, int step, PrintStateB
|
|||
if (this->m_status_callback)
|
||||
m_status_callback(SlicingStatus(*this, step));
|
||||
else if (! message.empty())
|
||||
printf("%s warning: %s\n", (object_id == ObjectID(*this)) ? "print" : "print object", message.c_str());
|
||||
printf("%s warning: %s\n", (object_id == this->id()) ? "print" : "print object", message.c_str());
|
||||
}
|
||||
|
||||
tbb::mutex& PrintObjectBase::state_mutex(PrintBase *print)
|
||||
|
|
@ -108,7 +109,7 @@ std::function<void()> PrintObjectBase::cancel_callback(PrintBase *print)
|
|||
|
||||
void PrintObjectBase::status_update_warnings(PrintBase *print, int step, PrintStateBase::WarningLevel warning_level, const std::string &message)
|
||||
{
|
||||
print->status_update_warnings(*this, step, warning_level, message);
|
||||
print->status_update_warnings(this->id(), step, warning_level, message);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -304,7 +304,7 @@ private:
|
|||
|
||||
class PrintBase;
|
||||
|
||||
class PrintObjectBase : public ObjectID
|
||||
class PrintObjectBase : public ObjectBase
|
||||
{
|
||||
public:
|
||||
const ModelObject* model_object() const { return m_model_object; }
|
||||
|
|
@ -335,7 +335,7 @@ protected:
|
|||
* The PrintBase class will abstract this flow for different technologies.
|
||||
*
|
||||
*/
|
||||
class PrintBase : public ObjectID
|
||||
class PrintBase : public ObjectBase
|
||||
{
|
||||
public:
|
||||
PrintBase() : m_placeholder_parser(&m_full_print_config) { this->restart(); }
|
||||
|
|
@ -386,9 +386,9 @@ public:
|
|||
struct SlicingStatus {
|
||||
SlicingStatus(int percent, const std::string &text, unsigned int flags = 0) : percent(percent), text(text), flags(flags) {}
|
||||
SlicingStatus(const PrintBase &print, int warning_step) :
|
||||
flags(UPDATE_PRINT_STEP_WARNINGS), warning_object_id(print), warning_step(warning_step) {}
|
||||
flags(UPDATE_PRINT_STEP_WARNINGS), warning_object_id(print.id()), warning_step(warning_step) {}
|
||||
SlicingStatus(const PrintObjectBase &print_object, int warning_step) :
|
||||
flags(UPDATE_PRINT_OBJECT_STEP_WARNINGS), warning_object_id(print_object), warning_step(warning_step) {}
|
||||
flags(UPDATE_PRINT_OBJECT_STEP_WARNINGS), warning_object_id(print_object.id()), warning_step(warning_step) {}
|
||||
int percent { -1 };
|
||||
std::string text;
|
||||
// Bitmap of flags.
|
||||
|
|
@ -507,9 +507,9 @@ protected:
|
|||
bool set_started(PrintStepEnum step) { return m_state.set_started(step, this->state_mutex(), [this](){ this->throw_if_canceled(); }); }
|
||||
PrintStateBase::TimeStamp set_done(PrintStepEnum step) {
|
||||
std::pair<PrintStateBase::TimeStamp, bool> status = m_state.set_done(step, this->state_mutex(), [this](){ this->throw_if_canceled(); });
|
||||
if (status.second)
|
||||
this->status_update_warnings(*this, static_cast<int>(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string());
|
||||
return status.first;
|
||||
if (status.second)
|
||||
this->status_update_warnings(this->id(), static_cast<int>(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string());
|
||||
return status.first;
|
||||
}
|
||||
bool invalidate_step(PrintStepEnum step)
|
||||
{ return m_state.invalidate(step, this->cancel_callback()); }
|
||||
|
|
@ -530,7 +530,7 @@ protected:
|
|||
std::pair<PrintStepEnum, bool> active_step = m_state.active_step_add_warning(warning_level, message, message_id, this->state_mutex());
|
||||
if (active_step.second)
|
||||
// Update UI.
|
||||
this->status_update_warnings(*this, static_cast<int>(active_step.first), warning_level, message);
|
||||
this->status_update_warnings(this->id(), static_cast<int>(active_step.first), warning_level, message);
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -556,9 +556,9 @@ protected:
|
|||
{ return m_state.set_started(step, PrintObjectBase::state_mutex(m_print), [this](){ this->throw_if_canceled(); }); }
|
||||
PrintStateBase::TimeStamp set_done(PrintObjectStepEnum step) {
|
||||
std::pair<PrintStateBase::TimeStamp, bool> status = m_state.set_done(step, PrintObjectBase::state_mutex(m_print), [this](){ this->throw_if_canceled(); });
|
||||
if (status.second)
|
||||
this->status_update_warnings(m_print, static_cast<int>(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string());
|
||||
return status.first;
|
||||
if (status.second)
|
||||
this->status_update_warnings(m_print, static_cast<int>(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string());
|
||||
return status.first;
|
||||
}
|
||||
|
||||
bool invalidate_step(PrintObjectStepEnum step)
|
||||
|
|
|
|||
|
|
@ -130,6 +130,37 @@ void PrintConfigDef::init_common_params()
|
|||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0.2));
|
||||
|
||||
// Options used by physical printers
|
||||
|
||||
def = this->add("printhost_user", coString);
|
||||
def->label = L("User");
|
||||
// def->tooltip = L("");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionString(""));
|
||||
|
||||
def = this->add("printhost_password", coString);
|
||||
def->label = L("Password");
|
||||
// def->tooltip = L("");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionString(""));
|
||||
|
||||
def = this->add("preset_name", coString);
|
||||
def->label = L("Printer preset name");
|
||||
def->tooltip = L("Related printer preset name");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionString(""));
|
||||
|
||||
def = this->add("printhost_authorization_type", coEnum);
|
||||
def->label = L("Authorization Type");
|
||||
// def->tooltip = L("");
|
||||
def->enum_keys_map = &ConfigOptionEnum<AuthorizationType>::get_enum_values();
|
||||
def->enum_values.push_back("key");
|
||||
def->enum_values.push_back("user");
|
||||
def->enum_labels.push_back("KeyPassword");
|
||||
def->enum_labels.push_back("UserPassword");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionEnum<AuthorizationType>(atKeyPassword));
|
||||
}
|
||||
|
||||
void PrintConfigDef::init_fff_params()
|
||||
|
|
@ -850,6 +881,8 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_values.push_back("hilbertcurve");
|
||||
def->enum_values.push_back("archimedeanchords");
|
||||
def->enum_values.push_back("octagramspiral");
|
||||
def->enum_values.push_back("adaptivecubic");
|
||||
def->enum_values.push_back("supportcubic");
|
||||
def->enum_labels.push_back(L("Rectilinear"));
|
||||
def->enum_labels.push_back(L("Grid"));
|
||||
def->enum_labels.push_back(L("Triangles"));
|
||||
|
|
@ -863,6 +896,8 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_labels.push_back(L("Hilbert Curve"));
|
||||
def->enum_labels.push_back(L("Archimedean Chords"));
|
||||
def->enum_labels.push_back(L("Octagram Spiral"));
|
||||
def->enum_labels.push_back(L("Adaptive Cubic"));
|
||||
def->enum_labels.push_back(L("Support Cubic"));
|
||||
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipStars));
|
||||
|
||||
def = this->add("first_layer_acceleration", coFloat);
|
||||
|
|
@ -1101,6 +1136,7 @@ void PrintConfigDef::init_fff_params()
|
|||
|
||||
def = this->add("ironing_type", coEnum);
|
||||
def->label = L("Ironing Type");
|
||||
def->category = L("Ironing");
|
||||
def->tooltip = L("Ironing Type");
|
||||
def->enum_keys_map = &ConfigOptionEnum<IroningType>::get_enum_values();
|
||||
def->enum_values.push_back("top");
|
||||
|
|
@ -1124,7 +1160,8 @@ void PrintConfigDef::init_fff_params()
|
|||
|
||||
def = this->add("ironing_spacing", coFloat);
|
||||
def->label = L("Spacing between ironing passes");
|
||||
def->tooltip = L("Distance between ironing lins");
|
||||
def->category = L("Ironing");
|
||||
def->tooltip = L("Distance between ironing lines");
|
||||
def->sidetext = L("mm");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
|
|
@ -2715,7 +2752,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
def = this->add("support_head_front_diameter", coFloat);
|
||||
def->label = L("Support head front diameter");
|
||||
def->label = L("Pinhead front diameter");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("Diameter of the pointing side of the head");
|
||||
def->sidetext = L("mm");
|
||||
|
|
@ -2724,7 +2761,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def->set_default_value(new ConfigOptionFloat(0.4));
|
||||
|
||||
def = this->add("support_head_penetration", coFloat);
|
||||
def->label = L("Support head penetration");
|
||||
def->label = L("Head penetration");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("How much the pinhead has to penetrate the model surface");
|
||||
def->sidetext = L("mm");
|
||||
|
|
@ -2733,7 +2770,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def->set_default_value(new ConfigOptionFloat(0.2));
|
||||
|
||||
def = this->add("support_head_width", coFloat);
|
||||
def->label = L("Support head width");
|
||||
def->label = L("Pinhead width");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("Width from the back sphere center to the front sphere center");
|
||||
def->sidetext = L("mm");
|
||||
|
|
@ -2743,7 +2780,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def->set_default_value(new ConfigOptionFloat(1.0));
|
||||
|
||||
def = this->add("support_pillar_diameter", coFloat);
|
||||
def->label = L("Support pillar diameter");
|
||||
def->label = L("Pillar diameter");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("Diameter in mm of the support pillars");
|
||||
def->sidetext = L("mm");
|
||||
|
|
@ -2751,6 +2788,17 @@ void PrintConfigDef::init_sla_params()
|
|||
def->max = 15;
|
||||
def->mode = comSimple;
|
||||
def->set_default_value(new ConfigOptionFloat(1.0));
|
||||
|
||||
def = this->add("support_small_pillar_diameter_percent", coPercent);
|
||||
def->label = L("Small pillar diameter percent");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("The percentage of smaller pillars compared to the normal pillar diameter "
|
||||
"which are used in problematic areas where a normal pilla cannot fit.");
|
||||
def->sidetext = L("%");
|
||||
def->min = 1;
|
||||
def->max = 100;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionPercent(50));
|
||||
|
||||
def = this->add("support_max_bridges_on_pillar", coInt);
|
||||
def->label = L("Max bridges on a pillar");
|
||||
|
|
@ -2763,7 +2811,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def->set_default_value(new ConfigOptionInt(3));
|
||||
|
||||
def = this->add("support_pillar_connection_mode", coEnum);
|
||||
def->label = L("Support pillar connection mode");
|
||||
def->label = L("Pillar connection mode");
|
||||
def->tooltip = L("Controls the bridge type between two neighboring pillars."
|
||||
" Can be zig-zag, cross (double zig-zag) or dynamic which"
|
||||
" will automatically switch between the first two depending"
|
||||
|
|
@ -3489,6 +3537,12 @@ CLIActionsConfigDef::CLIActionsConfigDef()
|
|||
def->cli = "export-gcode|gcode|g";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("gcodeviewer", coBool);
|
||||
def->label = L("G-code viewer");
|
||||
def->tooltip = L("Visualize an already sliced and saved G-code");
|
||||
def->cli = "gcodeviewer";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("slice", coBool);
|
||||
def->label = L("Slice");
|
||||
def->tooltip = L("Slice the model as FFF or SLA based on the printer_technology configuration value.");
|
||||
|
|
|
|||
|
|
@ -33,9 +33,13 @@ enum PrintHostType {
|
|||
htOctoPrint, htDuet, htFlashAir, htAstroBox
|
||||
};
|
||||
|
||||
enum AuthorizationType {
|
||||
atKeyPassword, atUserPassword
|
||||
};
|
||||
|
||||
enum InfillPattern : int {
|
||||
ipRectilinear, ipMonotonous, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
|
||||
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipCount,
|
||||
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipCount,
|
||||
};
|
||||
|
||||
enum class IroningType {
|
||||
|
|
@ -110,6 +114,15 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<PrintHostType>::g
|
|||
return keys_map;
|
||||
}
|
||||
|
||||
template<> inline const t_config_enum_values& ConfigOptionEnum<AuthorizationType>::get_enum_values() {
|
||||
static t_config_enum_values keys_map;
|
||||
if (keys_map.empty()) {
|
||||
keys_map["key"] = atKeyPassword;
|
||||
keys_map["user"] = atUserPassword;
|
||||
}
|
||||
return keys_map;
|
||||
}
|
||||
|
||||
template<> inline const t_config_enum_values& ConfigOptionEnum<InfillPattern>::get_enum_values() {
|
||||
static t_config_enum_values keys_map;
|
||||
if (keys_map.empty()) {
|
||||
|
|
@ -127,6 +140,8 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<InfillPattern>::g
|
|||
keys_map["hilbertcurve"] = ipHilbertCurve;
|
||||
keys_map["archimedeanchords"] = ipArchimedeanChords;
|
||||
keys_map["octagramspiral"] = ipOctagramSpiral;
|
||||
keys_map["adaptivecubic"] = ipAdaptiveCubic;
|
||||
keys_map["supportcubic"] = ipSupportCubic;
|
||||
}
|
||||
return keys_map;
|
||||
}
|
||||
|
|
@ -227,9 +242,13 @@ class DynamicPrintConfig : public DynamicConfig
|
|||
public:
|
||||
DynamicPrintConfig() {}
|
||||
DynamicPrintConfig(const DynamicPrintConfig &rhs) : DynamicConfig(rhs) {}
|
||||
DynamicPrintConfig(DynamicPrintConfig &&rhs) noexcept : DynamicConfig(std::move(rhs)) {}
|
||||
explicit DynamicPrintConfig(const StaticPrintConfig &rhs);
|
||||
explicit DynamicPrintConfig(const ConfigBase &rhs) : DynamicConfig(rhs) {}
|
||||
|
||||
DynamicPrintConfig& operator=(const DynamicPrintConfig &rhs) { DynamicConfig::operator=(rhs); return *this; }
|
||||
DynamicPrintConfig& operator=(DynamicPrintConfig &&rhs) noexcept { DynamicConfig::operator=(std::move(rhs)); return *this; }
|
||||
|
||||
static DynamicPrintConfig full_print_config();
|
||||
static DynamicPrintConfig* new_from_defaults_keys(const std::vector<std::string> &keys);
|
||||
|
||||
|
|
@ -1019,6 +1038,10 @@ public:
|
|||
|
||||
// Radius in mm of the support pillars.
|
||||
ConfigOptionFloat support_pillar_diameter /*= 0.8*/;
|
||||
|
||||
// The percentage of smaller pillars compared to the normal pillar diameter
|
||||
// which are used in problematic areas where a normal pilla cannot fit.
|
||||
ConfigOptionPercent support_small_pillar_diameter_percent;
|
||||
|
||||
// How much bridge (supporting another pinhead) can be placed on a pillar.
|
||||
ConfigOptionInt support_max_bridges_on_pillar;
|
||||
|
|
@ -1143,6 +1166,7 @@ protected:
|
|||
OPT_PTR(support_head_penetration);
|
||||
OPT_PTR(support_head_width);
|
||||
OPT_PTR(support_pillar_diameter);
|
||||
OPT_PTR(support_small_pillar_diameter_percent);
|
||||
OPT_PTR(support_max_bridges_on_pillar);
|
||||
OPT_PTR(support_pillar_connection_mode);
|
||||
OPT_PTR(support_buildplate_only);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include "Exception.hpp"
|
||||
#include "Print.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
|
|
@ -9,6 +10,9 @@
|
|||
#include "Surface.hpp"
|
||||
#include "Slicing.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "AABBTreeIndirect.hpp"
|
||||
#include "Fill/FillAdaptive.hpp"
|
||||
#include "Format/STL.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
|
@ -135,7 +139,7 @@ void PrintObject::slice()
|
|||
}
|
||||
});
|
||||
if (m_layers.empty())
|
||||
throw std::runtime_error("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n");
|
||||
throw Slic3r::SlicingError("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n");
|
||||
this->set_done(posSlice);
|
||||
}
|
||||
|
||||
|
|
@ -369,13 +373,15 @@ void PrintObject::infill()
|
|||
this->prepare_infill();
|
||||
|
||||
if (this->set_started(posInfill)) {
|
||||
auto [adaptive_fill_octree, support_fill_octree] = this->prepare_adaptive_infill_data();
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start";
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||
[this](const tbb::blocked_range<size_t>& range) {
|
||||
[this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
||||
m_print->throw_if_canceled();
|
||||
m_layers[layer_idx]->make_fills();
|
||||
m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get());
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
@ -421,13 +427,83 @@ void PrintObject::generate_support_material()
|
|||
// therefore they cannot be printed without supports.
|
||||
for (const Layer *layer : m_layers)
|
||||
if (layer->empty())
|
||||
throw std::runtime_error("Levitating objects cannot be printed without supports.");
|
||||
throw Slic3r::SlicingError("Levitating objects cannot be printed without supports.");
|
||||
#endif
|
||||
}
|
||||
this->set_done(posSupportMaterial);
|
||||
}
|
||||
}
|
||||
|
||||
//#define ADAPTIVE_SUPPORT_SIMPLE
|
||||
|
||||
std::pair<std::unique_ptr<FillAdaptive_Internal::Octree>, std::unique_ptr<FillAdaptive_Internal::Octree>> PrintObject::prepare_adaptive_infill_data()
|
||||
{
|
||||
using namespace FillAdaptive_Internal;
|
||||
|
||||
auto [adaptive_line_spacing, support_line_spacing] = adaptive_fill_line_spacing(*this);
|
||||
|
||||
std::unique_ptr<Octree> adaptive_fill_octree = {}, support_fill_octree = {};
|
||||
|
||||
if (adaptive_line_spacing == 0. && support_line_spacing == 0.)
|
||||
return std::make_pair(std::move(adaptive_fill_octree), std::move(support_fill_octree));
|
||||
|
||||
TriangleMesh mesh = this->model_object()->raw_mesh();
|
||||
mesh.transform(m_trafo, true);
|
||||
// Apply XY shift
|
||||
mesh.translate(- unscale<float>(m_center_offset.x()), - unscale<float>(m_center_offset.y()), 0);
|
||||
// Center of the first cube in octree
|
||||
Vec3d mesh_origin = mesh.bounding_box().center();
|
||||
|
||||
#ifdef ADAPTIVE_SUPPORT_SIMPLE
|
||||
if (mesh.its.vertices.empty())
|
||||
{
|
||||
mesh.require_shared_vertices();
|
||||
}
|
||||
|
||||
Vec3f vertical(0, 0, 1);
|
||||
|
||||
indexed_triangle_set its_set;
|
||||
its_set.vertices = mesh.its.vertices;
|
||||
|
||||
// Filter out non overhanging faces
|
||||
for (size_t i = 0; i < mesh.its.indices.size(); ++i) {
|
||||
stl_triangle_vertex_indices vertex_idx = mesh.its.indices[i];
|
||||
|
||||
auto its_calculate_normal = [](const stl_triangle_vertex_indices &index, const std::vector<stl_vertex> &vertices) {
|
||||
stl_normal normal = (vertices[index.y()] - vertices[index.x()]).cross(vertices[index.z()] - vertices[index.x()]);
|
||||
return normal;
|
||||
};
|
||||
|
||||
stl_normal normal = its_calculate_normal(vertex_idx, mesh.its.vertices);
|
||||
stl_normalize_vector(normal);
|
||||
|
||||
if(normal.dot(vertical) >= 0.707) {
|
||||
its_set.indices.push_back(vertex_idx);
|
||||
}
|
||||
}
|
||||
|
||||
mesh = TriangleMesh(its_set);
|
||||
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
Slic3r::store_stl(debug_out_path("overhangs.stl").c_str(), &mesh, false);
|
||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||
#endif /* ADAPTIVE_SUPPORT_SIMPLE */
|
||||
|
||||
Vec3d rotation = Vec3d((5.0 * M_PI) / 4.0, Geometry::deg2rad(215.264), M_PI / 6.0);
|
||||
Transform3d rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation, Vec3d::Ones(), Vec3d::Ones()).inverse();
|
||||
|
||||
if (adaptive_line_spacing != 0.) {
|
||||
// Rotate mesh and build octree on it with axis-aligned (standart base) cubes
|
||||
mesh.transform(rotation_matrix);
|
||||
adaptive_fill_octree = FillAdaptive::build_octree(mesh, adaptive_line_spacing, rotation_matrix * mesh_origin);
|
||||
}
|
||||
|
||||
if (support_line_spacing != 0.)
|
||||
support_fill_octree = FillSupportCubic::build_octree(mesh, support_line_spacing, rotation_matrix * mesh_origin, rotation_matrix);
|
||||
|
||||
return std::make_pair(std::move(adaptive_fill_octree), std::move(support_fill_octree));
|
||||
}
|
||||
|
||||
void PrintObject::clear_layers()
|
||||
{
|
||||
for (Layer *l : m_layers)
|
||||
|
|
@ -2669,18 +2745,20 @@ void PrintObject::_generate_support_material()
|
|||
}
|
||||
|
||||
|
||||
void PrintObject::project_and_append_custom_supports(
|
||||
FacetSupportType type, std::vector<ExPolygons>& expolys) const
|
||||
void PrintObject::project_and_append_custom_facets(
|
||||
bool seam, EnforcerBlockerType type, std::vector<ExPolygons>& expolys) const
|
||||
{
|
||||
for (const ModelVolume* mv : this->model_object()->volumes) {
|
||||
const std::vector<int> custom_facets = mv->m_supported_facets.get_facets(type);
|
||||
if (custom_facets.empty())
|
||||
const indexed_triangle_set custom_facets = seam
|
||||
? mv->m_seam_facets.get_facets(*mv, type)
|
||||
: mv->m_supported_facets.get_facets(*mv, type);
|
||||
if (! mv->is_model_part() || custom_facets.indices.empty())
|
||||
continue;
|
||||
|
||||
const TriangleMesh& mesh = mv->mesh();
|
||||
const Transform3f& tr1 = mv->get_matrix().cast<float>();
|
||||
const Transform3f& tr2 = this->trafo().cast<float>();
|
||||
const Transform3f tr = tr2 * tr1;
|
||||
const float tr_det_sign = (tr.matrix().determinant() > 0. ? 1.f : -1.f);
|
||||
|
||||
|
||||
// The projection will be at most a pentagon. Let's minimize heap
|
||||
|
|
@ -2705,11 +2783,11 @@ void PrintObject::project_and_append_custom_supports(
|
|||
};
|
||||
|
||||
// Vector to collect resulting projections from each triangle.
|
||||
std::vector<TriangleProjections> projections_of_triangles(custom_facets.size());
|
||||
std::vector<TriangleProjections> projections_of_triangles(custom_facets.indices.size());
|
||||
|
||||
// Iterate over all triangles.
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, custom_facets.size()),
|
||||
tbb::blocked_range<size_t>(0, custom_facets.indices.size()),
|
||||
[&](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
|
||||
|
||||
|
|
@ -2717,10 +2795,11 @@ void PrintObject::project_and_append_custom_supports(
|
|||
|
||||
// Transform the triangle into worlds coords.
|
||||
for (int i=0; i<3; ++i)
|
||||
facet[i] = tr * mesh.its.vertices[mesh.its.indices[custom_facets[idx]](i)];
|
||||
facet[i] = tr * custom_facets.vertices[custom_facets.indices[idx](i)];
|
||||
|
||||
// Ignore triangles with upward-pointing normal.
|
||||
if ((facet[1]-facet[0]).cross(facet[2]-facet[0]).z() > 0.)
|
||||
// Ignore triangles with upward-pointing normal. Don't forget about mirroring.
|
||||
float z_comp = (facet[1]-facet[0]).cross(facet[2]-facet[0]).z();
|
||||
if (! seam && tr_det_sign * z_comp > 0.)
|
||||
continue;
|
||||
|
||||
// Sort the three vertices according to z-coordinate.
|
||||
|
|
@ -2732,7 +2811,7 @@ void PrintObject::project_and_append_custom_supports(
|
|||
std::array<Vec2f, 3> trianglef;
|
||||
for (int i=0; i<3; ++i) {
|
||||
trianglef[i] = Vec2f(facet[i].x(), facet[i].y());
|
||||
trianglef[i] += Vec2f(unscale<float>(this->center_offset().x()),
|
||||
trianglef[i] -= Vec2f(unscale<float>(this->center_offset().x()),
|
||||
unscale<float>(this->center_offset().y()));
|
||||
}
|
||||
|
||||
|
|
@ -2822,8 +2901,9 @@ void PrintObject::project_and_append_custom_supports(
|
|||
// Now append the collected polygons to respective layers.
|
||||
for (auto& trg : projections_of_triangles) {
|
||||
int layer_id = trg.first_layer_id;
|
||||
|
||||
for (const LightPolygon& poly : trg.polygons) {
|
||||
if (layer_id >= int(expolys.size()))
|
||||
break; // part of triangle could be projected above top layer
|
||||
expolys[layer_id].emplace_back(std::move(poly.pts));
|
||||
++layer_id;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include "Exception.hpp"
|
||||
#include "Print.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
@ -13,7 +14,7 @@ unsigned int PrintRegion::extruder(FlowRole role) const
|
|||
else if (role == frSolidInfill || role == frTopSolidInfill)
|
||||
extruder = m_config.solid_infill_extruder;
|
||||
else
|
||||
throw std::invalid_argument("Unknown role");
|
||||
throw Slic3r::InvalidArgument("Unknown role");
|
||||
return extruder;
|
||||
}
|
||||
|
||||
|
|
@ -40,7 +41,7 @@ Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool fir
|
|||
} else if (role == frTopSolidInfill) {
|
||||
config_width = m_config.top_infill_extrusion_width;
|
||||
} else {
|
||||
throw std::invalid_argument("Unknown role");
|
||||
throw Slic3r::InvalidArgument("Unknown role");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -128,12 +128,13 @@ protected:
|
|||
}
|
||||
|
||||
public:
|
||||
template<class GammaFn> AGGRaster(const Resolution &res,
|
||||
template<class GammaFn>
|
||||
AGGRaster(const Resolution &res,
|
||||
const PixelDim & pd,
|
||||
const Trafo & trafo,
|
||||
const TColor & foreground,
|
||||
const TColor & background,
|
||||
GammaFn && gammafn)
|
||||
const TColor & foreground,
|
||||
const TColor & background,
|
||||
GammaFn && gammafn)
|
||||
: m_resolution(res)
|
||||
, m_pxdim_scaled(SCALING_FACTOR / pd.w_mm, SCALING_FACTOR / pd.h_mm)
|
||||
, m_buf(res.pixels())
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
#ifndef SLA_BOOSTADAPTER_HPP
|
||||
#define SLA_BOOSTADAPTER_HPP
|
||||
|
||||
#include <libslic3r/SLA/Common.hpp>
|
||||
#include <libslic3r/Point.hpp>
|
||||
#include <libslic3r/BoundingBox.hpp>
|
||||
|
||||
#include <boost/geometry.hpp>
|
||||
|
||||
namespace boost {
|
||||
|
|
|
|||
152
src/libslic3r/SLA/Clustering.cpp
Normal file
152
src/libslic3r/SLA/Clustering.cpp
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
#include "Clustering.hpp"
|
||||
#include "boost/geometry/index/rtree.hpp"
|
||||
|
||||
#include <libslic3r/SLA/SpatIndex.hpp>
|
||||
#include <libslic3r/SLA/BoostAdapter.hpp>
|
||||
|
||||
namespace Slic3r { namespace sla {
|
||||
|
||||
namespace bgi = boost::geometry::index;
|
||||
using Index3D = bgi::rtree< PointIndexEl, bgi::rstar<16, 4> /* ? */ >;
|
||||
|
||||
namespace {
|
||||
|
||||
bool cmp_ptidx_elements(const PointIndexEl& e1, const PointIndexEl& e2)
|
||||
{
|
||||
return e1.second < e2.second;
|
||||
};
|
||||
|
||||
ClusteredPoints cluster(Index3D &sindex,
|
||||
unsigned max_points,
|
||||
std::function<std::vector<PointIndexEl>(
|
||||
const Index3D &, const PointIndexEl &)> qfn)
|
||||
{
|
||||
using Elems = std::vector<PointIndexEl>;
|
||||
|
||||
// Recursive function for visiting all the points in a given distance to
|
||||
// each other
|
||||
std::function<void(Elems&, Elems&)> group =
|
||||
[&sindex, &group, max_points, qfn](Elems& pts, Elems& cluster)
|
||||
{
|
||||
for(auto& p : pts) {
|
||||
std::vector<PointIndexEl> tmp = qfn(sindex, p);
|
||||
|
||||
std::sort(tmp.begin(), tmp.end(), cmp_ptidx_elements);
|
||||
|
||||
Elems newpts;
|
||||
std::set_difference(tmp.begin(), tmp.end(),
|
||||
cluster.begin(), cluster.end(),
|
||||
std::back_inserter(newpts), cmp_ptidx_elements);
|
||||
|
||||
int c = max_points && newpts.size() + cluster.size() > max_points?
|
||||
int(max_points - cluster.size()) : int(newpts.size());
|
||||
|
||||
cluster.insert(cluster.end(), newpts.begin(), newpts.begin() + c);
|
||||
std::sort(cluster.begin(), cluster.end(), cmp_ptidx_elements);
|
||||
|
||||
if(!newpts.empty() && (!max_points || cluster.size() < max_points))
|
||||
group(newpts, cluster);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<Elems> clusters;
|
||||
for(auto it = sindex.begin(); it != sindex.end();) {
|
||||
Elems cluster = {};
|
||||
Elems pts = {*it};
|
||||
group(pts, cluster);
|
||||
|
||||
for(auto& c : cluster) sindex.remove(c);
|
||||
it = sindex.begin();
|
||||
|
||||
clusters.emplace_back(cluster);
|
||||
}
|
||||
|
||||
ClusteredPoints result;
|
||||
for(auto& cluster : clusters) {
|
||||
result.emplace_back();
|
||||
for(auto c : cluster) result.back().emplace_back(c.second);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<PointIndexEl> distance_queryfn(const Index3D& sindex,
|
||||
const PointIndexEl& p,
|
||||
double dist,
|
||||
unsigned max_points)
|
||||
{
|
||||
std::vector<PointIndexEl> tmp; tmp.reserve(max_points);
|
||||
sindex.query(
|
||||
bgi::nearest(p.first, max_points),
|
||||
std::back_inserter(tmp)
|
||||
);
|
||||
|
||||
for(auto it = tmp.begin(); it < tmp.end(); ++it)
|
||||
if((p.first - it->first).norm() > dist) it = tmp.erase(it);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Clustering a set of points by the given criteria
|
||||
ClusteredPoints cluster(
|
||||
const std::vector<unsigned>& indices,
|
||||
std::function<Vec3d(unsigned)> pointfn,
|
||||
double dist,
|
||||
unsigned max_points)
|
||||
{
|
||||
// A spatial index for querying the nearest points
|
||||
Index3D sindex;
|
||||
|
||||
// Build the index
|
||||
for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx));
|
||||
|
||||
return cluster(sindex, max_points,
|
||||
[dist, max_points](const Index3D& sidx, const PointIndexEl& p)
|
||||
{
|
||||
return distance_queryfn(sidx, p, dist, max_points);
|
||||
});
|
||||
}
|
||||
|
||||
// Clustering a set of points by the given criteria
|
||||
ClusteredPoints cluster(
|
||||
const std::vector<unsigned>& indices,
|
||||
std::function<Vec3d(unsigned)> pointfn,
|
||||
std::function<bool(const PointIndexEl&, const PointIndexEl&)> predicate,
|
||||
unsigned max_points)
|
||||
{
|
||||
// A spatial index for querying the nearest points
|
||||
Index3D sindex;
|
||||
|
||||
// Build the index
|
||||
for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx));
|
||||
|
||||
return cluster(sindex, max_points,
|
||||
[max_points, predicate](const Index3D& sidx, const PointIndexEl& p)
|
||||
{
|
||||
std::vector<PointIndexEl> tmp; tmp.reserve(max_points);
|
||||
sidx.query(bgi::satisfies([p, predicate](const PointIndexEl& e){
|
||||
return predicate(p, e);
|
||||
}), std::back_inserter(tmp));
|
||||
return tmp;
|
||||
});
|
||||
}
|
||||
|
||||
ClusteredPoints cluster(const Eigen::MatrixXd& pts, double dist, unsigned max_points)
|
||||
{
|
||||
// A spatial index for querying the nearest points
|
||||
Index3D sindex;
|
||||
|
||||
// Build the index
|
||||
for(Eigen::Index i = 0; i < pts.rows(); i++)
|
||||
sindex.insert(std::make_pair(Vec3d(pts.row(i)), unsigned(i)));
|
||||
|
||||
return cluster(sindex, max_points,
|
||||
[dist, max_points](const Index3D& sidx, const PointIndexEl& p)
|
||||
{
|
||||
return distance_queryfn(sidx, p, dist, max_points);
|
||||
});
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::sla
|
||||
|
|
@ -2,7 +2,8 @@
|
|||
#define SLA_CLUSTERING_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <libslic3r/SLA/Common.hpp>
|
||||
|
||||
#include <libslic3r/Point.hpp>
|
||||
#include <libslic3r/SLA/SpatIndex.hpp>
|
||||
|
||||
namespace Slic3r { namespace sla {
|
||||
|
|
@ -16,7 +17,7 @@ ClusteredPoints cluster(const std::vector<unsigned>& indices,
|
|||
double dist,
|
||||
unsigned max_points);
|
||||
|
||||
ClusteredPoints cluster(const PointSet& points,
|
||||
ClusteredPoints cluster(const Eigen::MatrixXd& points,
|
||||
double dist,
|
||||
unsigned max_points);
|
||||
|
||||
|
|
@ -26,5 +27,56 @@ ClusteredPoints cluster(
|
|||
std::function<bool(const PointIndexEl&, const PointIndexEl&)> predicate,
|
||||
unsigned max_points);
|
||||
|
||||
}}
|
||||
// This function returns the position of the centroid in the input 'clust'
|
||||
// vector of point indices.
|
||||
template<class DistFn, class PointFn>
|
||||
long cluster_centroid(const ClusterEl &clust, PointFn pointfn, DistFn df)
|
||||
{
|
||||
switch(clust.size()) {
|
||||
case 0: /* empty cluster */ return -1;
|
||||
case 1: /* only one element */ return 0;
|
||||
case 2: /* if two elements, there is no center */ return 0;
|
||||
default: ;
|
||||
}
|
||||
|
||||
// The function works by calculating for each point the average distance
|
||||
// from all the other points in the cluster. We create a selector bitmask of
|
||||
// the same size as the cluster. The bitmask will have two true bits and
|
||||
// false bits for the rest of items and we will loop through all the
|
||||
// permutations of the bitmask (combinations of two points). Get the
|
||||
// distance for the two points and add the distance to the averages.
|
||||
// The point with the smallest average than wins.
|
||||
|
||||
// The complexity should be O(n^2) but we will mostly apply this function
|
||||
// for small clusters only (cca 3 elements)
|
||||
|
||||
std::vector<bool> sel(clust.size(), false); // create full zero bitmask
|
||||
std::fill(sel.end() - 2, sel.end(), true); // insert the two ones
|
||||
std::vector<double> avgs(clust.size(), 0.0); // store the average distances
|
||||
|
||||
do {
|
||||
std::array<size_t, 2> idx;
|
||||
for(size_t i = 0, j = 0; i < clust.size(); i++)
|
||||
if(sel[i]) idx[j++] = i;
|
||||
|
||||
double d = df(pointfn(clust[idx[0]]),
|
||||
pointfn(clust[idx[1]]));
|
||||
|
||||
// add the distance to the sums for both associated points
|
||||
for(auto i : idx) avgs[i] += d;
|
||||
|
||||
// now continue with the next permutation of the bitmask with two 1s
|
||||
} while(std::next_permutation(sel.begin(), sel.end()));
|
||||
|
||||
// Divide by point size in the cluster to get the average (may be redundant)
|
||||
for(auto& a : avgs) a /= clust.size();
|
||||
|
||||
// get the lowest average distance and return the index
|
||||
auto minit = std::min_element(avgs.begin(), avgs.end());
|
||||
return long(minit - avgs.begin());
|
||||
}
|
||||
|
||||
|
||||
}} // namespace Slic3r::sla
|
||||
|
||||
#endif // CLUSTERING_HPP
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
#ifndef SLA_COMMON_HPP
|
||||
#define SLA_COMMON_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
#include <functional>
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Typedefs from Point.hpp
|
||||
typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> Vec3f;
|
||||
typedef Eigen::Matrix<double, 3, 1, Eigen::DontAlign> Vec3d;
|
||||
typedef Eigen::Matrix<int, 3, 1, Eigen::DontAlign> Vec3i;
|
||||
typedef Eigen::Matrix<int, 4, 1, Eigen::DontAlign> Vec4i;
|
||||
|
||||
namespace sla {
|
||||
|
||||
using PointSet = Eigen::MatrixXd;
|
||||
|
||||
} // namespace sla
|
||||
} // namespace Slic3r
|
||||
|
||||
|
||||
#endif // SLASUPPORTTREE_HPP
|
||||
|
|
@ -4,6 +4,12 @@
|
|||
#include <tbb/spin_mutex.h>
|
||||
#include <tbb/mutex.h>
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/parallel_reduce.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
#include <libslic3r/libslic3r.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace sla {
|
||||
|
|
@ -17,18 +23,59 @@ template<bool> struct _ccr {};
|
|||
template<> struct _ccr<true>
|
||||
{
|
||||
using SpinningMutex = tbb::spin_mutex;
|
||||
using BlockingMutex = tbb::mutex;
|
||||
|
||||
template<class It, class Fn>
|
||||
static inline void enumerate(It from, It to, Fn fn)
|
||||
using BlockingMutex = tbb::mutex;
|
||||
|
||||
template<class Fn, class It>
|
||||
static IteratorOnly<It, void> loop_(const tbb::blocked_range<It> &range, Fn &&fn)
|
||||
{
|
||||
auto iN = to - from;
|
||||
size_t N = iN < 0 ? 0 : size_t(iN);
|
||||
|
||||
tbb::parallel_for(size_t(0), N, [from, fn](size_t n) {
|
||||
fn(*(from + decltype(iN)(n)), n);
|
||||
for (auto &el : range) fn(el);
|
||||
}
|
||||
|
||||
template<class Fn, class I>
|
||||
static IntegerOnly<I, void> loop_(const tbb::blocked_range<I> &range, Fn &&fn)
|
||||
{
|
||||
for (I i = range.begin(); i < range.end(); ++i) fn(i);
|
||||
}
|
||||
|
||||
template<class It, class Fn>
|
||||
static void for_each(It from, It to, Fn &&fn, size_t granularity = 1)
|
||||
{
|
||||
tbb::parallel_for(tbb::blocked_range{from, to, granularity},
|
||||
[&fn, from](const auto &range) {
|
||||
loop_(range, std::forward<Fn>(fn));
|
||||
});
|
||||
}
|
||||
|
||||
template<class I, class MergeFn, class T, class AccessFn>
|
||||
static T reduce(I from,
|
||||
I to,
|
||||
const T &init,
|
||||
MergeFn &&mergefn,
|
||||
AccessFn &&access,
|
||||
size_t granularity = 1
|
||||
)
|
||||
{
|
||||
return tbb::parallel_reduce(
|
||||
tbb::blocked_range{from, to, granularity}, init,
|
||||
[&](const auto &range, T subinit) {
|
||||
T acc = subinit;
|
||||
loop_(range, [&](auto &i) { acc = mergefn(acc, access(i)); });
|
||||
return acc;
|
||||
},
|
||||
std::forward<MergeFn>(mergefn));
|
||||
}
|
||||
|
||||
template<class I, class MergeFn, class T>
|
||||
static IteratorOnly<I, T> reduce(I from,
|
||||
I to,
|
||||
const T & init,
|
||||
MergeFn &&mergefn,
|
||||
size_t granularity = 1)
|
||||
{
|
||||
return reduce(
|
||||
from, to, init, std::forward<MergeFn>(mergefn),
|
||||
[](typename I::value_type &i) { return i; }, granularity);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct _ccr<false>
|
||||
|
|
@ -39,11 +86,52 @@ private:
|
|||
public:
|
||||
using SpinningMutex = _Mtx;
|
||||
using BlockingMutex = _Mtx;
|
||||
|
||||
template<class It, class Fn>
|
||||
static inline void enumerate(It from, It to, Fn fn)
|
||||
|
||||
template<class Fn, class It>
|
||||
static IteratorOnly<It, void> loop_(It from, It to, Fn &&fn)
|
||||
{
|
||||
for (auto it = from; it != to; ++it) fn(*it, size_t(it - from));
|
||||
for (auto it = from; it != to; ++it) fn(*it);
|
||||
}
|
||||
|
||||
template<class Fn, class I>
|
||||
static IntegerOnly<I, void> loop_(I from, I to, Fn &&fn)
|
||||
{
|
||||
for (I i = from; i < to; ++i) fn(i);
|
||||
}
|
||||
|
||||
template<class It, class Fn>
|
||||
static void for_each(It from,
|
||||
It to,
|
||||
Fn &&fn,
|
||||
size_t /* ignore granularity */ = 1)
|
||||
{
|
||||
loop_(from, to, std::forward<Fn>(fn));
|
||||
}
|
||||
|
||||
template<class I, class MergeFn, class T, class AccessFn>
|
||||
static T reduce(I from,
|
||||
I to,
|
||||
const T & init,
|
||||
MergeFn &&mergefn,
|
||||
AccessFn &&access,
|
||||
size_t /*granularity*/ = 1
|
||||
)
|
||||
{
|
||||
T acc = init;
|
||||
loop_(from, to, [&](auto &i) { acc = mergefn(acc, access(i)); });
|
||||
return acc;
|
||||
}
|
||||
|
||||
template<class I, class MergeFn, class T>
|
||||
static IteratorOnly<I, T> reduce(I from,
|
||||
I to,
|
||||
const T &init,
|
||||
MergeFn &&mergefn,
|
||||
size_t /*granularity*/ = 1
|
||||
)
|
||||
{
|
||||
return reduce(from, to, init, std::forward<MergeFn>(mergefn),
|
||||
[](typename I::value_type &i) { return i; });
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include <libslic3r/SLA/Contour3D.hpp>
|
||||
#include <libslic3r/SLA/EigenMesh3D.hpp>
|
||||
#include <libslic3r/SLA/IndexedMesh.hpp>
|
||||
|
||||
#include <libslic3r/Format/objparser.hpp>
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ Contour3D::Contour3D(TriangleMesh &&trmesh)
|
|||
faces3.swap(trmesh.its.indices);
|
||||
}
|
||||
|
||||
Contour3D::Contour3D(const EigenMesh3D &emesh) {
|
||||
Contour3D::Contour3D(const IndexedMesh &emesh) {
|
||||
points.reserve(emesh.vertices().size());
|
||||
faces3.reserve(emesh.indices().size());
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
#ifndef SLA_CONTOUR3D_HPP
|
||||
#define SLA_CONTOUR3D_HPP
|
||||
|
||||
#include <libslic3r/SLA/Common.hpp>
|
||||
|
||||
#include <libslic3r/TriangleMesh.hpp>
|
||||
|
||||
namespace Slic3r { namespace sla {
|
||||
namespace Slic3r {
|
||||
|
||||
class EigenMesh3D;
|
||||
// Used for quads (TODO: remove this, and convert quads to triangles in OpenVDBUtils)
|
||||
using Vec4i = Eigen::Matrix<int, 4, 1, Eigen::DontAlign>;
|
||||
|
||||
namespace sla {
|
||||
|
||||
class IndexedMesh;
|
||||
|
||||
/// Dumb vertex mesh consisting of triangles (or) quads. Capable of merging with
|
||||
/// other meshes of this type and converting to and from other mesh formats.
|
||||
|
|
@ -19,7 +22,7 @@ struct Contour3D {
|
|||
Contour3D() = default;
|
||||
Contour3D(const TriangleMesh &trmesh);
|
||||
Contour3D(TriangleMesh &&trmesh);
|
||||
Contour3D(const EigenMesh3D &emesh);
|
||||
Contour3D(const IndexedMesh &emesh);
|
||||
|
||||
Contour3D& merge(const Contour3D& ctr);
|
||||
Contour3D& merge(const Pointf3s& triangles);
|
||||
|
|
|
|||
|
|
@ -3,11 +3,10 @@
|
|||
#include <libslic3r/OpenVDBUtils.hpp>
|
||||
#include <libslic3r/TriangleMesh.hpp>
|
||||
#include <libslic3r/SLA/Hollowing.hpp>
|
||||
#include <libslic3r/SLA/Contour3D.hpp>
|
||||
#include <libslic3r/SLA/EigenMesh3D.hpp>
|
||||
#include <libslic3r/SLA/SupportTreeBuilder.hpp>
|
||||
#include <libslic3r/SLA/IndexedMesh.hpp>
|
||||
#include <libslic3r/ClipperUtils.hpp>
|
||||
#include <libslic3r/SimplifyMesh.hpp>
|
||||
#include <libslic3r/SLA/SupportTreeMesher.hpp>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
|
|
@ -160,7 +159,7 @@ bool DrainHole::get_intersections(const Vec3f& s, const Vec3f& dir,
|
|||
const Eigen::ParametrizedLine<float, 3> ray(s, dir.normalized());
|
||||
|
||||
for (size_t i=0; i<2; ++i)
|
||||
out[i] = std::make_pair(sla::EigenMesh3D::hit_result::infty(), Vec3d::Zero());
|
||||
out[i] = std::make_pair(sla::IndexedMesh::hit_result::infty(), Vec3d::Zero());
|
||||
|
||||
const float sqr_radius = pow(radius, 2.f);
|
||||
|
||||
|
|
@ -274,4 +273,13 @@ void cut_drainholes(std::vector<ExPolygons> & obj_slices,
|
|||
obj_slices[i] = diff_ex(obj_slices[i], hole_slices[i]);
|
||||
}
|
||||
|
||||
void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg)
|
||||
{
|
||||
std::unique_ptr<Slic3r::TriangleMesh> inter_ptr =
|
||||
Slic3r::sla::generate_interior(mesh);
|
||||
|
||||
if (inter_ptr) mesh.merge(*inter_ptr);
|
||||
mesh.require_shared_vertices();
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::sla
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
#define SLA_HOLLOWING_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <libslic3r/SLA/Common.hpp>
|
||||
#include <libslic3r/SLA/Contour3D.hpp>
|
||||
#include <libslic3r/SLA/JobController.hpp>
|
||||
|
||||
|
|
@ -59,10 +58,14 @@ struct DrainHole
|
|||
|
||||
using DrainHoles = std::vector<DrainHole>;
|
||||
|
||||
constexpr float HoleStickOutLength = 1.f;
|
||||
|
||||
std::unique_ptr<TriangleMesh> generate_interior(const TriangleMesh &mesh,
|
||||
const HollowingConfig & = {},
|
||||
const JobController &ctl = {});
|
||||
|
||||
void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg);
|
||||
|
||||
void cut_drainholes(std::vector<ExPolygons> & obj_slices,
|
||||
const std::vector<float> &slicegrid,
|
||||
float closing_radius,
|
||||
|
|
|
|||
|
|
@ -1,187 +1,18 @@
|
|||
#include <cmath>
|
||||
#include <libslic3r/SLA/Common.hpp>
|
||||
#include <libslic3r/SLA/Concurrency.hpp>
|
||||
#include <libslic3r/SLA/SpatIndex.hpp>
|
||||
#include <libslic3r/SLA/EigenMesh3D.hpp>
|
||||
#include <libslic3r/SLA/Contour3D.hpp>
|
||||
#include <libslic3r/SLA/Clustering.hpp>
|
||||
#include "IndexedMesh.hpp"
|
||||
#include "Concurrency.hpp"
|
||||
|
||||
#include <libslic3r/AABBTreeIndirect.hpp>
|
||||
#include <libslic3r/TriangleMesh.hpp>
|
||||
|
||||
// for concave hull merging decisions
|
||||
#include <libslic3r/SLA/BoostAdapter.hpp>
|
||||
#include "boost/geometry/index/rtree.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4244)
|
||||
#pragma warning(disable: 4267)
|
||||
#endif
|
||||
|
||||
|
||||
#include <igl/remove_duplicate_vertices.h>
|
||||
#include <numeric>
|
||||
|
||||
#ifdef SLIC3R_HOLE_RAYCASTER
|
||||
#include <libslic3r/SLA/Hollowing.hpp>
|
||||
#include <libslic3r/SLA/Hollowing.hpp>
|
||||
#endif
|
||||
|
||||
namespace Slic3r { namespace sla {
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
namespace sla {
|
||||
|
||||
|
||||
/* **************************************************************************
|
||||
* PointIndex implementation
|
||||
* ************************************************************************** */
|
||||
|
||||
class PointIndex::Impl {
|
||||
public:
|
||||
using BoostIndex = boost::geometry::index::rtree< PointIndexEl,
|
||||
boost::geometry::index::rstar<16, 4> /* ? */ >;
|
||||
|
||||
BoostIndex m_store;
|
||||
};
|
||||
|
||||
PointIndex::PointIndex(): m_impl(new Impl()) {}
|
||||
PointIndex::~PointIndex() {}
|
||||
|
||||
PointIndex::PointIndex(const PointIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {}
|
||||
PointIndex::PointIndex(PointIndex&& cpy): m_impl(std::move(cpy.m_impl)) {}
|
||||
|
||||
PointIndex& PointIndex::operator=(const PointIndex &cpy)
|
||||
{
|
||||
m_impl.reset(new Impl(*cpy.m_impl));
|
||||
return *this;
|
||||
}
|
||||
|
||||
PointIndex& PointIndex::operator=(PointIndex &&cpy)
|
||||
{
|
||||
m_impl.swap(cpy.m_impl);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void PointIndex::insert(const PointIndexEl &el)
|
||||
{
|
||||
m_impl->m_store.insert(el);
|
||||
}
|
||||
|
||||
bool PointIndex::remove(const PointIndexEl& el)
|
||||
{
|
||||
return m_impl->m_store.remove(el) == 1;
|
||||
}
|
||||
|
||||
std::vector<PointIndexEl>
|
||||
PointIndex::query(std::function<bool(const PointIndexEl &)> fn) const
|
||||
{
|
||||
namespace bgi = boost::geometry::index;
|
||||
|
||||
std::vector<PointIndexEl> ret;
|
||||
m_impl->m_store.query(bgi::satisfies(fn), std::back_inserter(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<PointIndexEl> PointIndex::nearest(const Vec3d &el, unsigned k = 1) const
|
||||
{
|
||||
namespace bgi = boost::geometry::index;
|
||||
std::vector<PointIndexEl> ret; ret.reserve(k);
|
||||
m_impl->m_store.query(bgi::nearest(el, k), std::back_inserter(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t PointIndex::size() const
|
||||
{
|
||||
return m_impl->m_store.size();
|
||||
}
|
||||
|
||||
void PointIndex::foreach(std::function<void (const PointIndexEl &)> fn)
|
||||
{
|
||||
for(auto& el : m_impl->m_store) fn(el);
|
||||
}
|
||||
|
||||
void PointIndex::foreach(std::function<void (const PointIndexEl &)> fn) const
|
||||
{
|
||||
for(const auto &el : m_impl->m_store) fn(el);
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* BoxIndex implementation
|
||||
* ************************************************************************** */
|
||||
|
||||
class BoxIndex::Impl {
|
||||
public:
|
||||
using BoostIndex = boost::geometry::index::
|
||||
rtree<BoxIndexEl, boost::geometry::index::rstar<16, 4> /* ? */>;
|
||||
|
||||
BoostIndex m_store;
|
||||
};
|
||||
|
||||
BoxIndex::BoxIndex(): m_impl(new Impl()) {}
|
||||
BoxIndex::~BoxIndex() {}
|
||||
|
||||
BoxIndex::BoxIndex(const BoxIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {}
|
||||
BoxIndex::BoxIndex(BoxIndex&& cpy): m_impl(std::move(cpy.m_impl)) {}
|
||||
|
||||
BoxIndex& BoxIndex::operator=(const BoxIndex &cpy)
|
||||
{
|
||||
m_impl.reset(new Impl(*cpy.m_impl));
|
||||
return *this;
|
||||
}
|
||||
|
||||
BoxIndex& BoxIndex::operator=(BoxIndex &&cpy)
|
||||
{
|
||||
m_impl.swap(cpy.m_impl);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void BoxIndex::insert(const BoxIndexEl &el)
|
||||
{
|
||||
m_impl->m_store.insert(el);
|
||||
}
|
||||
|
||||
bool BoxIndex::remove(const BoxIndexEl& el)
|
||||
{
|
||||
return m_impl->m_store.remove(el) == 1;
|
||||
}
|
||||
|
||||
std::vector<BoxIndexEl> BoxIndex::query(const BoundingBox &qrbb,
|
||||
BoxIndex::QueryType qt)
|
||||
{
|
||||
namespace bgi = boost::geometry::index;
|
||||
|
||||
std::vector<BoxIndexEl> ret; ret.reserve(m_impl->m_store.size());
|
||||
|
||||
switch (qt) {
|
||||
case qtIntersects:
|
||||
m_impl->m_store.query(bgi::intersects(qrbb), std::back_inserter(ret));
|
||||
break;
|
||||
case qtWithin:
|
||||
m_impl->m_store.query(bgi::within(qrbb), std::back_inserter(ret));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t BoxIndex::size() const
|
||||
{
|
||||
return m_impl->m_store.size();
|
||||
}
|
||||
|
||||
void BoxIndex::foreach(std::function<void (const BoxIndexEl &)> fn)
|
||||
{
|
||||
for(auto& el : m_impl->m_store) fn(el);
|
||||
}
|
||||
|
||||
|
||||
/* ****************************************************************************
|
||||
* EigenMesh3D implementation
|
||||
* ****************************************************************************/
|
||||
|
||||
|
||||
class EigenMesh3D::AABBImpl {
|
||||
class IndexedMesh::AABBImpl {
|
||||
private:
|
||||
AABBTreeIndirect::Tree3f m_tree;
|
||||
|
||||
|
|
@ -189,7 +20,7 @@ public:
|
|||
void init(const TriangleMesh& tm)
|
||||
{
|
||||
m_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(
|
||||
tm.its.vertices, tm.its.indices);
|
||||
tm.its.vertices, tm.its.indices);
|
||||
}
|
||||
|
||||
void intersect_ray(const TriangleMesh& tm,
|
||||
|
|
@ -215,9 +46,9 @@ public:
|
|||
size_t idx_unsigned = 0;
|
||||
Vec3d closest_vec3d(closest);
|
||||
double dist = AABBTreeIndirect::squared_distance_to_indexed_triangle_set(
|
||||
tm.its.vertices,
|
||||
tm.its.indices,
|
||||
m_tree, point, idx_unsigned, closest_vec3d);
|
||||
tm.its.vertices,
|
||||
tm.its.indices,
|
||||
m_tree, point, idx_unsigned, closest_vec3d);
|
||||
i = int(idx_unsigned);
|
||||
closest = closest_vec3d;
|
||||
return dist;
|
||||
|
|
@ -226,72 +57,71 @@ public:
|
|||
|
||||
static const constexpr double MESH_EPS = 1e-6;
|
||||
|
||||
EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh)
|
||||
IndexedMesh::IndexedMesh(const TriangleMesh& tmesh)
|
||||
: m_aabb(new AABBImpl()), m_tm(&tmesh)
|
||||
{
|
||||
auto&& bb = tmesh.bounding_box();
|
||||
m_ground_level += bb.min(Z);
|
||||
|
||||
|
||||
// Build the AABB accelaration tree
|
||||
m_aabb->init(tmesh);
|
||||
}
|
||||
|
||||
EigenMesh3D::~EigenMesh3D() {}
|
||||
IndexedMesh::~IndexedMesh() {}
|
||||
|
||||
EigenMesh3D::EigenMesh3D(const EigenMesh3D &other):
|
||||
IndexedMesh::IndexedMesh(const IndexedMesh &other):
|
||||
m_tm(other.m_tm), m_ground_level(other.m_ground_level),
|
||||
m_aabb( new AABBImpl(*other.m_aabb) ) {}
|
||||
|
||||
|
||||
EigenMesh3D &EigenMesh3D::operator=(const EigenMesh3D &other)
|
||||
IndexedMesh &IndexedMesh::operator=(const IndexedMesh &other)
|
||||
{
|
||||
m_tm = other.m_tm;
|
||||
m_ground_level = other.m_ground_level;
|
||||
m_aabb.reset(new AABBImpl(*other.m_aabb)); return *this;
|
||||
}
|
||||
|
||||
EigenMesh3D &EigenMesh3D::operator=(EigenMesh3D &&other) = default;
|
||||
IndexedMesh &IndexedMesh::operator=(IndexedMesh &&other) = default;
|
||||
|
||||
EigenMesh3D::EigenMesh3D(EigenMesh3D &&other) = default;
|
||||
IndexedMesh::IndexedMesh(IndexedMesh &&other) = default;
|
||||
|
||||
|
||||
|
||||
const std::vector<Vec3f>& EigenMesh3D::vertices() const
|
||||
const std::vector<Vec3f>& IndexedMesh::vertices() const
|
||||
{
|
||||
return m_tm->its.vertices;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const std::vector<Vec3i>& EigenMesh3D::indices() const
|
||||
const std::vector<Vec3i>& IndexedMesh::indices() const
|
||||
{
|
||||
return m_tm->its.indices;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const Vec3f& EigenMesh3D::vertices(size_t idx) const
|
||||
const Vec3f& IndexedMesh::vertices(size_t idx) const
|
||||
{
|
||||
return m_tm->its.vertices[idx];
|
||||
}
|
||||
|
||||
|
||||
|
||||
const Vec3i& EigenMesh3D::indices(size_t idx) const
|
||||
const Vec3i& IndexedMesh::indices(size_t idx) const
|
||||
{
|
||||
return m_tm->its.indices[idx];
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vec3d EigenMesh3D::normal_by_face_id(int face_id) const {
|
||||
Vec3d IndexedMesh::normal_by_face_id(int face_id) const {
|
||||
return m_tm->stl.facet_start[face_id].normal.cast<double>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
EigenMesh3D::hit_result
|
||||
EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const
|
||||
IndexedMesh::hit_result
|
||||
IndexedMesh::query_ray_hit(const Vec3d &s, const Vec3d &dir) const
|
||||
{
|
||||
assert(is_approx(dir.norm(), 1.));
|
||||
igl::Hit hit;
|
||||
|
|
@ -319,13 +149,13 @@ EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const
|
|||
return ret;
|
||||
}
|
||||
|
||||
std::vector<EigenMesh3D::hit_result>
|
||||
EigenMesh3D::query_ray_hits(const Vec3d &s, const Vec3d &dir) const
|
||||
std::vector<IndexedMesh::hit_result>
|
||||
IndexedMesh::query_ray_hits(const Vec3d &s, const Vec3d &dir) const
|
||||
{
|
||||
std::vector<EigenMesh3D::hit_result> outs;
|
||||
std::vector<IndexedMesh::hit_result> outs;
|
||||
std::vector<igl::Hit> hits;
|
||||
m_aabb->intersect_ray(*m_tm, s, dir, hits);
|
||||
|
||||
|
||||
// The sort is necessary, the hits are not always sorted.
|
||||
std::sort(hits.begin(), hits.end(),
|
||||
[](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; });
|
||||
|
|
@ -334,13 +164,13 @@ EigenMesh3D::query_ray_hits(const Vec3d &s, const Vec3d &dir) const
|
|||
// along an axis of a cube due to floating-point approximations in igl (?)
|
||||
hits.erase(std::unique(hits.begin(), hits.end(),
|
||||
[](const igl::Hit& a, const igl::Hit& b)
|
||||
{ return a.t == b.t; }),
|
||||
{ return a.t == b.t; }),
|
||||
hits.end());
|
||||
|
||||
// Convert the igl::Hit into hit_result
|
||||
outs.reserve(hits.size());
|
||||
for (const igl::Hit& hit : hits) {
|
||||
outs.emplace_back(EigenMesh3D::hit_result(*this));
|
||||
outs.emplace_back(IndexedMesh::hit_result(*this));
|
||||
outs.back().m_t = double(hit.t);
|
||||
outs.back().m_dir = dir;
|
||||
outs.back().m_source = s;
|
||||
|
|
@ -355,8 +185,8 @@ EigenMesh3D::query_ray_hits(const Vec3d &s, const Vec3d &dir) const
|
|||
|
||||
|
||||
#ifdef SLIC3R_HOLE_RAYCASTER
|
||||
EigenMesh3D::hit_result EigenMesh3D::filter_hits(
|
||||
const std::vector<EigenMesh3D::hit_result>& object_hits) const
|
||||
IndexedMesh::hit_result IndexedMesh::filter_hits(
|
||||
const std::vector<IndexedMesh::hit_result>& object_hits) const
|
||||
{
|
||||
assert(! m_holes.empty());
|
||||
hit_result out(*this);
|
||||
|
|
@ -377,7 +207,7 @@ EigenMesh3D::hit_result EigenMesh3D::filter_hits(
|
|||
};
|
||||
std::vector<HoleHit> hole_isects;
|
||||
hole_isects.reserve(m_holes.size());
|
||||
|
||||
|
||||
auto sf = s.cast<float>();
|
||||
auto dirf = dir.cast<float>();
|
||||
|
||||
|
|
@ -452,7 +282,7 @@ EigenMesh3D::hit_result EigenMesh3D::filter_hits(
|
|||
#endif
|
||||
|
||||
|
||||
double EigenMesh3D::squared_distance(const Vec3d &p, int& i, Vec3d& c) const {
|
||||
double IndexedMesh::squared_distance(const Vec3d &p, int& i, Vec3d& c) const {
|
||||
double sqdst = 0;
|
||||
Eigen::Matrix<double, 1, 3> pp = p;
|
||||
Eigen::Matrix<double, 1, 3> cc;
|
||||
|
|
@ -461,31 +291,19 @@ double EigenMesh3D::squared_distance(const Vec3d &p, int& i, Vec3d& c) const {
|
|||
return sqdst;
|
||||
}
|
||||
|
||||
/* ****************************************************************************
|
||||
* Misc functions
|
||||
* ****************************************************************************/
|
||||
|
||||
namespace {
|
||||
|
||||
bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2,
|
||||
double eps = 0.05)
|
||||
static bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2,
|
||||
double eps = 0.05)
|
||||
{
|
||||
using Line3D = Eigen::ParametrizedLine<double, 3>;
|
||||
|
||||
|
||||
auto line = Line3D::Through(e1, e2);
|
||||
double d = line.distance(p);
|
||||
return std::abs(d) < eps;
|
||||
}
|
||||
|
||||
template<class Vec> double distance(const Vec& pp1, const Vec& pp2) {
|
||||
auto p = pp2 - pp1;
|
||||
return std::sqrt(p.transpose() * p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PointSet normals(const PointSet& points,
|
||||
const EigenMesh3D& mesh,
|
||||
const IndexedMesh& mesh,
|
||||
double eps,
|
||||
std::function<void()> thr, // throw on cancel
|
||||
const std::vector<unsigned>& pt_indices)
|
||||
|
|
@ -502,10 +320,10 @@ PointSet normals(const PointSet& points,
|
|||
PointSet ret(range.size(), 3);
|
||||
|
||||
// for (size_t ridx = 0; ridx < range.size(); ++ridx)
|
||||
ccr::enumerate(
|
||||
range.begin(), range.end(),
|
||||
[&ret, &mesh, &points, thr, eps](unsigned el, size_t ridx) {
|
||||
ccr::for_each(size_t(0), range.size(),
|
||||
[&ret, &mesh, &points, thr, eps, &range](size_t ridx) {
|
||||
thr();
|
||||
unsigned el = range[ridx];
|
||||
auto eidx = Eigen::Index(el);
|
||||
int faceid = 0;
|
||||
Vec3d p;
|
||||
|
|
@ -531,11 +349,11 @@ PointSet normals(const PointSet& points,
|
|||
// ic will mark a single vertex.
|
||||
int ia = -1, ib = -1, ic = -1;
|
||||
|
||||
if (std::abs(distance(p, p1)) < eps) {
|
||||
if (std::abs((p - p1).norm()) < eps) {
|
||||
ic = trindex(0);
|
||||
} else if (std::abs(distance(p, p2)) < eps) {
|
||||
} else if (std::abs((p - p2).norm()) < eps) {
|
||||
ic = trindex(1);
|
||||
} else if (std::abs(distance(p, p3)) < eps) {
|
||||
} else if (std::abs((p - p3).norm()) < eps) {
|
||||
ic = trindex(2);
|
||||
} else if (point_on_edge(p, p1, p2, eps)) {
|
||||
ia = trindex(0);
|
||||
|
|
@ -612,148 +430,4 @@ PointSet normals(const PointSet& points,
|
|||
return ret;
|
||||
}
|
||||
|
||||
namespace bgi = boost::geometry::index;
|
||||
using Index3D = bgi::rtree< PointIndexEl, bgi::rstar<16, 4> /* ? */ >;
|
||||
|
||||
namespace {
|
||||
|
||||
bool cmp_ptidx_elements(const PointIndexEl& e1, const PointIndexEl& e2)
|
||||
{
|
||||
return e1.second < e2.second;
|
||||
};
|
||||
|
||||
ClusteredPoints cluster(Index3D &sindex,
|
||||
unsigned max_points,
|
||||
std::function<std::vector<PointIndexEl>(
|
||||
const Index3D &, const PointIndexEl &)> qfn)
|
||||
{
|
||||
using Elems = std::vector<PointIndexEl>;
|
||||
|
||||
// Recursive function for visiting all the points in a given distance to
|
||||
// each other
|
||||
std::function<void(Elems&, Elems&)> group =
|
||||
[&sindex, &group, max_points, qfn](Elems& pts, Elems& cluster)
|
||||
{
|
||||
for(auto& p : pts) {
|
||||
std::vector<PointIndexEl> tmp = qfn(sindex, p);
|
||||
|
||||
std::sort(tmp.begin(), tmp.end(), cmp_ptidx_elements);
|
||||
|
||||
Elems newpts;
|
||||
std::set_difference(tmp.begin(), tmp.end(),
|
||||
cluster.begin(), cluster.end(),
|
||||
std::back_inserter(newpts), cmp_ptidx_elements);
|
||||
|
||||
int c = max_points && newpts.size() + cluster.size() > max_points?
|
||||
int(max_points - cluster.size()) : int(newpts.size());
|
||||
|
||||
cluster.insert(cluster.end(), newpts.begin(), newpts.begin() + c);
|
||||
std::sort(cluster.begin(), cluster.end(), cmp_ptidx_elements);
|
||||
|
||||
if(!newpts.empty() && (!max_points || cluster.size() < max_points))
|
||||
group(newpts, cluster);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<Elems> clusters;
|
||||
for(auto it = sindex.begin(); it != sindex.end();) {
|
||||
Elems cluster = {};
|
||||
Elems pts = {*it};
|
||||
group(pts, cluster);
|
||||
|
||||
for(auto& c : cluster) sindex.remove(c);
|
||||
it = sindex.begin();
|
||||
|
||||
clusters.emplace_back(cluster);
|
||||
}
|
||||
|
||||
ClusteredPoints result;
|
||||
for(auto& cluster : clusters) {
|
||||
result.emplace_back();
|
||||
for(auto c : cluster) result.back().emplace_back(c.second);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<PointIndexEl> distance_queryfn(const Index3D& sindex,
|
||||
const PointIndexEl& p,
|
||||
double dist,
|
||||
unsigned max_points)
|
||||
{
|
||||
std::vector<PointIndexEl> tmp; tmp.reserve(max_points);
|
||||
sindex.query(
|
||||
bgi::nearest(p.first, max_points),
|
||||
std::back_inserter(tmp)
|
||||
);
|
||||
|
||||
for(auto it = tmp.begin(); it < tmp.end(); ++it)
|
||||
if(distance(p.first, it->first) > dist) it = tmp.erase(it);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Clustering a set of points by the given criteria
|
||||
ClusteredPoints cluster(
|
||||
const std::vector<unsigned>& indices,
|
||||
std::function<Vec3d(unsigned)> pointfn,
|
||||
double dist,
|
||||
unsigned max_points)
|
||||
{
|
||||
// A spatial index for querying the nearest points
|
||||
Index3D sindex;
|
||||
|
||||
// Build the index
|
||||
for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx));
|
||||
|
||||
return cluster(sindex, max_points,
|
||||
[dist, max_points](const Index3D& sidx, const PointIndexEl& p)
|
||||
{
|
||||
return distance_queryfn(sidx, p, dist, max_points);
|
||||
});
|
||||
}
|
||||
|
||||
// Clustering a set of points by the given criteria
|
||||
ClusteredPoints cluster(
|
||||
const std::vector<unsigned>& indices,
|
||||
std::function<Vec3d(unsigned)> pointfn,
|
||||
std::function<bool(const PointIndexEl&, const PointIndexEl&)> predicate,
|
||||
unsigned max_points)
|
||||
{
|
||||
// A spatial index for querying the nearest points
|
||||
Index3D sindex;
|
||||
|
||||
// Build the index
|
||||
for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx));
|
||||
|
||||
return cluster(sindex, max_points,
|
||||
[max_points, predicate](const Index3D& sidx, const PointIndexEl& p)
|
||||
{
|
||||
std::vector<PointIndexEl> tmp; tmp.reserve(max_points);
|
||||
sidx.query(bgi::satisfies([p, predicate](const PointIndexEl& e){
|
||||
return predicate(p, e);
|
||||
}), std::back_inserter(tmp));
|
||||
return tmp;
|
||||
});
|
||||
}
|
||||
|
||||
ClusteredPoints cluster(const PointSet& pts, double dist, unsigned max_points)
|
||||
{
|
||||
// A spatial index for querying the nearest points
|
||||
Index3D sindex;
|
||||
|
||||
// Build the index
|
||||
for(Eigen::Index i = 0; i < pts.rows(); i++)
|
||||
sindex.insert(std::make_pair(Vec3d(pts.row(i)), unsigned(i)));
|
||||
|
||||
return cluster(sindex, max_points,
|
||||
[dist, max_points](const Index3D& sidx, const PointIndexEl& p)
|
||||
{
|
||||
return distance_queryfn(sidx, p, dist, max_points);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace sla
|
||||
} // namespace Slic3r
|
||||
}} // namespace Slic3r::sla
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
#ifndef SLA_EIGENMESH3D_H
|
||||
#define SLA_EIGENMESH3D_H
|
||||
#ifndef SLA_INDEXEDMESH_H
|
||||
#define SLA_INDEXEDMESH_H
|
||||
|
||||
#include <libslic3r/SLA/Common.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <libslic3r/Point.hpp>
|
||||
|
||||
// There is an implementation of a hole-aware raycaster that was eventually
|
||||
// not used in production version. It is now hidden under following define
|
||||
|
|
@ -19,10 +21,12 @@ class TriangleMesh;
|
|||
|
||||
namespace sla {
|
||||
|
||||
using PointSet = Eigen::MatrixXd;
|
||||
|
||||
/// An index-triangle structure for libIGL functions. Also serves as an
|
||||
/// alternative (raw) input format for the SLASupportTree.
|
||||
// Implemented in libslic3r/SLA/Common.cpp
|
||||
class EigenMesh3D {
|
||||
class IndexedMesh {
|
||||
class AABBImpl;
|
||||
|
||||
const TriangleMesh* m_tm;
|
||||
|
|
@ -38,15 +42,15 @@ class EigenMesh3D {
|
|||
|
||||
public:
|
||||
|
||||
explicit EigenMesh3D(const TriangleMesh&);
|
||||
explicit IndexedMesh(const TriangleMesh&);
|
||||
|
||||
EigenMesh3D(const EigenMesh3D& other);
|
||||
EigenMesh3D& operator=(const EigenMesh3D&);
|
||||
IndexedMesh(const IndexedMesh& other);
|
||||
IndexedMesh& operator=(const IndexedMesh&);
|
||||
|
||||
EigenMesh3D(EigenMesh3D &&other);
|
||||
EigenMesh3D& operator=(EigenMesh3D &&other);
|
||||
IndexedMesh(IndexedMesh &&other);
|
||||
IndexedMesh& operator=(IndexedMesh &&other);
|
||||
|
||||
~EigenMesh3D();
|
||||
~IndexedMesh();
|
||||
|
||||
inline double ground_level() const { return m_ground_level + m_gnd_offset; }
|
||||
inline void ground_level_offset(double o) { m_gnd_offset = o; }
|
||||
|
|
@ -62,15 +66,15 @@ public:
|
|||
// m_t holds a distance from m_source to the intersection.
|
||||
double m_t = infty();
|
||||
int m_face_id = -1;
|
||||
const EigenMesh3D *m_mesh = nullptr;
|
||||
const IndexedMesh *m_mesh = nullptr;
|
||||
Vec3d m_dir;
|
||||
Vec3d m_source;
|
||||
Vec3d m_normal;
|
||||
friend class EigenMesh3D;
|
||||
friend class IndexedMesh;
|
||||
|
||||
// A valid object of this class can only be obtained from
|
||||
// EigenMesh3D::query_ray_hit method.
|
||||
explicit inline hit_result(const EigenMesh3D& em): m_mesh(&em) {}
|
||||
// IndexedMesh::query_ray_hit method.
|
||||
explicit inline hit_result(const IndexedMesh& em): m_mesh(&em) {}
|
||||
public:
|
||||
// This denotes no hit on the mesh.
|
||||
static inline constexpr double infty() { return std::numeric_limits<double>::infinity(); }
|
||||
|
|
@ -83,7 +87,7 @@ public:
|
|||
inline Vec3d position() const { return m_source + m_dir * m_t; }
|
||||
inline int face() const { return m_face_id; }
|
||||
inline bool is_valid() const { return m_mesh != nullptr; }
|
||||
inline bool is_hit() const { return !std::isinf(m_t); }
|
||||
inline bool is_hit() const { return m_face_id >= 0 && !std::isinf(m_t); }
|
||||
|
||||
inline const Vec3d& normal() const {
|
||||
assert(is_valid());
|
||||
|
|
@ -107,7 +111,7 @@ public:
|
|||
// This function is currently not used anywhere, it was written when the
|
||||
// holes were subtracted on slices, that is, before we started using CGAL
|
||||
// to actually cut the holes into the mesh.
|
||||
hit_result filter_hits(const std::vector<EigenMesh3D::hit_result>& obj_hits) const;
|
||||
hit_result filter_hits(const std::vector<IndexedMesh::hit_result>& obj_hits) const;
|
||||
#endif
|
||||
|
||||
// Casting a ray on the mesh, returns the distance where the hit occures.
|
||||
|
|
@ -125,16 +129,18 @@ public:
|
|||
}
|
||||
|
||||
Vec3d normal_by_face_id(int face_id) const;
|
||||
|
||||
const TriangleMesh * get_triangle_mesh() const { return m_tm; }
|
||||
};
|
||||
|
||||
// Calculate the normals for the selected points (from 'points' set) on the
|
||||
// mesh. This will call squared distance for each point.
|
||||
PointSet normals(const PointSet& points,
|
||||
const EigenMesh3D& convert_mesh,
|
||||
const IndexedMesh& convert_mesh,
|
||||
double eps = 0.05, // min distance from edges
|
||||
std::function<void()> throw_on_cancel = [](){},
|
||||
const std::vector<unsigned>& selected_points = {});
|
||||
|
||||
}} // namespace Slic3r::sla
|
||||
|
||||
#endif // EIGENMESH3D_H
|
||||
#endif // INDEXEDMESH_H
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
#define SLA_JOBCONTROLLER_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace Slic3r { namespace sla {
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue