mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-22 16:21:24 -06:00
Merge branch 'master' into fs_QuadricEdgeCollapse
This commit is contained in:
commit
59d02aea0a
160 changed files with 5982 additions and 5094 deletions
|
@ -155,11 +155,6 @@ void AppConfig::set_defaults()
|
|||
if (get("seq_top_layer_only").empty())
|
||||
set("seq_top_layer_only", "1");
|
||||
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
if (get("seq_top_gcode_indices").empty())
|
||||
set("seq_top_gcode_indices", "1");
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
|
||||
if (get("use_perspective_camera").empty())
|
||||
set("use_perspective_camera", "1");
|
||||
|
||||
|
@ -172,6 +167,12 @@ void AppConfig::set_defaults()
|
|||
if (get("show_splash_screen").empty())
|
||||
set("show_splash_screen", "1");
|
||||
|
||||
if (get("last_hint").empty())
|
||||
set("last_hint", "0");
|
||||
|
||||
if (get("show_hints").empty())
|
||||
set("show_hints", "1");
|
||||
|
||||
#ifdef _WIN32
|
||||
if (get("use_legacy_3DConnexion").empty())
|
||||
set("use_legacy_3DConnexion", "0");
|
||||
|
@ -544,6 +545,8 @@ void AppConfig::update_config_dir(const std::string &dir)
|
|||
|
||||
void AppConfig::update_skein_dir(const std::string &dir)
|
||||
{
|
||||
if (is_shapes_dir(dir))
|
||||
return; // do not save "shapes gallery" directory
|
||||
this->set("recent", "skein_directory", dir);
|
||||
}
|
||||
/*
|
||||
|
@ -576,7 +579,7 @@ std::string AppConfig::get_last_output_dir(const std::string& alt, const bool re
|
|||
if (it2 != it->second.end() && it3 != it->second.end() && !it2->second.empty() && it3->second == "1")
|
||||
return it2->second;
|
||||
}
|
||||
return alt;
|
||||
return is_shapes_dir(alt) ? get_last_dir() : alt;
|
||||
}
|
||||
|
||||
void AppConfig::update_last_output_dir(const std::string& dir, const bool removable)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "LibraryCheck.hpp"
|
||||
#include "BlacklistedLibraryCheck.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
|
@ -12,61 +12,46 @@ namespace Slic3r {
|
|||
#ifdef WIN32
|
||||
|
||||
//only dll name with .dll suffix - currently case sensitive
|
||||
const std::vector<std::wstring> LibraryCheck::blacklist({ L"NahimicOSD.dll" });
|
||||
const std::vector<std::wstring> BlacklistedLibraryCheck::blacklist({ L"NahimicOSD.dll" });
|
||||
|
||||
bool LibraryCheck::get_blacklisted(std::vector<std::wstring>& names)
|
||||
bool BlacklistedLibraryCheck::get_blacklisted(std::vector<std::wstring>& names)
|
||||
{
|
||||
if (m_found.empty())
|
||||
return false;
|
||||
for (const auto& lib : m_found)
|
||||
names.emplace_back(lib);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
std::wstring LibraryCheck::get_blacklisted_string()
|
||||
std::wstring BlacklistedLibraryCheck::get_blacklisted_string()
|
||||
{
|
||||
std::wstring ret;
|
||||
if (m_found.empty())
|
||||
return ret;
|
||||
//ret = L"These libraries has been detected inside of the PrusaSlicer process.\n"
|
||||
// L"We suggest stopping or uninstalling these services if you experience crashes while using PrusaSlicer.\n\n";
|
||||
for (const auto& lib : m_found)
|
||||
{
|
||||
ret += lib;
|
||||
ret += L"\n";
|
||||
}
|
||||
ret += lib + L"\n";
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool LibraryCheck::perform_check()
|
||||
{
|
||||
|
||||
DWORD processID = GetCurrentProcessId();
|
||||
HMODULE hMods[1024];
|
||||
HANDLE hProcess;
|
||||
DWORD cbNeeded;
|
||||
unsigned int i;
|
||||
|
||||
bool BlacklistedLibraryCheck::perform_check()
|
||||
{
|
||||
// Get a handle to the process.
|
||||
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
|
||||
PROCESS_VM_READ,
|
||||
FALSE, processID);
|
||||
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, GetCurrentProcessId());
|
||||
if (NULL == hProcess)
|
||||
return false;
|
||||
|
||||
// Get a list of all the modules in this process.
|
||||
HMODULE hMods[1024];
|
||||
DWORD cbNeeded;
|
||||
if (EnumProcessModulesEx(hProcess, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_ALL))
|
||||
{
|
||||
//printf("Total Dlls: %d\n", cbNeeded / sizeof(HMODULE));
|
||||
for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
|
||||
for (unsigned int i = 0; i < cbNeeded / sizeof(HMODULE); ++ i)
|
||||
{
|
||||
TCHAR szModName[MAX_PATH];
|
||||
wchar_t szModName[MAX_PATH];
|
||||
// Get the full path to the module's file.
|
||||
if (GetModuleFileNameEx(hProcess, hMods[i], szModName,
|
||||
sizeof(szModName) / sizeof(TCHAR)))
|
||||
if (GetModuleFileNameExW(hProcess, hMods[i], szModName, MAX_PATH))
|
||||
{
|
||||
// Add to list if blacklisted
|
||||
if(LibraryCheck::is_blacklisted(szModName)) {
|
||||
if (BlacklistedLibraryCheck::is_blacklisted(szModName)) {
|
||||
//wprintf(L"Contains library: %s\n", szModName);
|
||||
if (std::find(m_found.begin(), m_found.end(), szModName) == m_found.end())
|
||||
m_found.emplace_back(szModName);
|
||||
|
@ -79,23 +64,22 @@ bool LibraryCheck::perform_check()
|
|||
CloseHandle(hProcess);
|
||||
//printf("\n");
|
||||
return !m_found.empty();
|
||||
|
||||
}
|
||||
|
||||
bool LibraryCheck::is_blacklisted(std::wstring dllpath)
|
||||
bool BlacklistedLibraryCheck::is_blacklisted(const std::wstring &dllpath)
|
||||
{
|
||||
std::wstring dllname = boost::filesystem::path(dllpath).filename().wstring();
|
||||
//std::transform(dllname.begin(), dllname.end(), dllname.begin(), std::tolower);
|
||||
if (std::find(LibraryCheck::blacklist.begin(), LibraryCheck::blacklist.end(), dllname) != LibraryCheck::blacklist.end()) {
|
||||
if (std::find(BlacklistedLibraryCheck::blacklist.begin(), BlacklistedLibraryCheck::blacklist.end(), dllname) != BlacklistedLibraryCheck::blacklist.end()) {
|
||||
//std::wprintf(L"%s is blacklisted\n", dllname.c_str());
|
||||
return true;
|
||||
}
|
||||
//std::wprintf(L"%s is NOT blacklisted\n", dllname.c_str());
|
||||
return false;
|
||||
}
|
||||
bool LibraryCheck::is_blacklisted(std::string dllpath)
|
||||
bool BlacklistedLibraryCheck::is_blacklisted(const std::string &dllpath)
|
||||
{
|
||||
return LibraryCheck::is_blacklisted(boost::nowide::widen(dllpath));
|
||||
return BlacklistedLibraryCheck::is_blacklisted(boost::nowide::widen(dllpath));
|
||||
}
|
||||
|
||||
#endif //WIN32
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef slic3r_LibraryCheck_hpp_
|
||||
#define slic3r_LibraryCheck_hpp_
|
||||
#ifndef slic3r_BlacklistedLibraryCheck_hpp_
|
||||
#define slic3r_BlacklistedLibraryCheck_hpp_
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
|
@ -10,30 +10,31 @@
|
|||
namespace Slic3r {
|
||||
|
||||
#ifdef WIN32
|
||||
class LibraryCheck
|
||||
class BlacklistedLibraryCheck
|
||||
{
|
||||
public:
|
||||
static LibraryCheck& get_instance()
|
||||
static BlacklistedLibraryCheck& get_instance()
|
||||
{
|
||||
static LibraryCheck instance;
|
||||
static BlacklistedLibraryCheck instance;
|
||||
|
||||
return instance;
|
||||
}
|
||||
private:
|
||||
LibraryCheck() {}
|
||||
BlacklistedLibraryCheck() = default;
|
||||
|
||||
std::vector<std::wstring> m_found;
|
||||
public:
|
||||
LibraryCheck(LibraryCheck const&) = delete;
|
||||
void operator=(LibraryCheck const&) = delete;
|
||||
BlacklistedLibraryCheck(BlacklistedLibraryCheck const&) = delete;
|
||||
void operator=(BlacklistedLibraryCheck const&) = delete;
|
||||
// returns all found blacklisted dlls
|
||||
bool get_blacklisted(std::vector<std::wstring>& names);
|
||||
std::wstring get_blacklisted_string();
|
||||
// returns true if enumerating found blacklisted dll
|
||||
bool perform_check();
|
||||
|
||||
static bool is_blacklisted(std::string dllpath);
|
||||
static bool is_blacklisted(std::wstring dllpath);
|
||||
// UTF-8 encoded path
|
||||
static bool is_blacklisted(const std::string &dllpath);
|
||||
static bool is_blacklisted(const std::wstring &dllpath);
|
||||
private:
|
||||
static const std::vector<std::wstring> blacklist;
|
||||
};
|
||||
|
@ -42,4 +43,4 @@ private:
|
|||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif //slic3r_LibraryCheck_hpp_
|
||||
#endif //slic3r_BlacklistedLibraryCheck_hpp_
|
|
@ -1,5 +1,5 @@
|
|||
project(libslic3r)
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(libslic3r)
|
||||
|
||||
include(PrecompiledHeader)
|
||||
|
||||
|
@ -125,8 +125,8 @@ add_library(libslic3r STATIC
|
|||
"${CMAKE_CURRENT_BINARY_DIR}/libslic3r_version.h"
|
||||
Line.cpp
|
||||
Line.hpp
|
||||
LibraryCheck.cpp
|
||||
LibraryCheck.hpp
|
||||
BlacklistedLibraryCheck.cpp
|
||||
BlacklistedLibraryCheck.hpp
|
||||
LocalesUtils.cpp
|
||||
LocalesUtils.hpp
|
||||
Model.cpp
|
||||
|
|
|
@ -20,10 +20,13 @@ public:
|
|||
Contour(const Slic3r::Point *data, size_t size, bool open) : Contour(data, data + size, open) {}
|
||||
Contour(const std::vector<Slic3r::Point> &pts, bool open) : Contour(pts.data(), pts.size(), open) {}
|
||||
|
||||
const Slic3r::Point *begin() const { return m_begin; }
|
||||
const Slic3r::Point *end() const { return m_end; }
|
||||
bool open() const { return m_open; }
|
||||
bool closed() const { return ! m_open; }
|
||||
const Slic3r::Point *begin() const { return m_begin; }
|
||||
const Slic3r::Point *end() const { return m_end; }
|
||||
bool open() const { return m_open; }
|
||||
bool closed() const { return !m_open; }
|
||||
|
||||
const Slic3r::Point &front() const { return *m_begin; }
|
||||
const Slic3r::Point &back() const { return *(m_end - 1); }
|
||||
|
||||
// Start point of a segment idx.
|
||||
const Slic3r::Point& segment_start(size_t idx) const {
|
||||
|
@ -61,6 +64,23 @@ public:
|
|||
|
||||
size_t num_segments() const { return this->size() - (m_open ? 1 : 0); }
|
||||
|
||||
Line get_segment(size_t idx) const
|
||||
{
|
||||
assert(idx < this->num_segments());
|
||||
return Line(this->segment_start(idx), this->segment_end(idx));
|
||||
}
|
||||
|
||||
Lines get_segments() const
|
||||
{
|
||||
Lines lines;
|
||||
lines.reserve(this->num_segments());
|
||||
if (this->num_segments() > 2) {
|
||||
for (auto it = this->begin(); it != this->end() - 1; ++it) lines.push_back(Line(*it, *(it + 1)));
|
||||
if (!m_open) lines.push_back(Line(this->back(), this->front()));
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t size() const { return m_end - m_begin; }
|
||||
|
||||
|
|
|
@ -666,7 +666,6 @@ namespace Slic3r {
|
|||
|
||||
close_zip_reader(&archive);
|
||||
|
||||
#if ENABLE_RELOAD_FROM_DISK_FOR_3MF
|
||||
if (m_version == 0) {
|
||||
// if the 3mf was not produced by PrusaSlicer and there is more than one instance,
|
||||
// split the object in as many objects as instances
|
||||
|
@ -711,7 +710,6 @@ namespace Slic3r {
|
|||
++i;
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_RELOAD_FROM_DISK_FOR_3MF
|
||||
|
||||
for (const IdToModelObjectMap::value_type& object : m_objects) {
|
||||
if (object.second >= int(m_model->objects.size())) {
|
||||
|
@ -779,7 +777,6 @@ namespace Slic3r {
|
|||
return false;
|
||||
}
|
||||
|
||||
#if ENABLE_RELOAD_FROM_DISK_FOR_3MF
|
||||
int object_idx = 0;
|
||||
for (ModelObject* o : model.objects) {
|
||||
int volume_idx = 0;
|
||||
|
@ -795,7 +792,6 @@ namespace Slic3r {
|
|||
}
|
||||
++object_idx;
|
||||
}
|
||||
#endif // ENABLE_RELOAD_FROM_DISK_FOR_3MF
|
||||
|
||||
// // fixes the min z of the model if negative
|
||||
// model.adjust_min_z();
|
||||
|
@ -1877,7 +1873,6 @@ namespace Slic3r {
|
|||
stl_get_size(&stl);
|
||||
triangle_mesh.repair();
|
||||
|
||||
#if ENABLE_RELOAD_FROM_DISK_FOR_3MF
|
||||
if (m_version == 0) {
|
||||
// if the 3mf was not produced by PrusaSlicer and there is only one instance,
|
||||
// bake the transformation into the geometry to allow the reload from disk command
|
||||
|
@ -1887,7 +1882,6 @@ namespace Slic3r {
|
|||
object.instances.front()->set_transformation(Slic3r::Geometry::Transformation());
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_RELOAD_FROM_DISK_FOR_3MF
|
||||
|
||||
ModelVolume* volume = object.add_volume(std::move(triangle_mesh));
|
||||
// stores the volume matrix taken from the metadata, if present
|
||||
|
|
|
@ -172,11 +172,7 @@ namespace Slic3r {
|
|||
// subdivide the retraction in segments
|
||||
if (!wipe_path.empty()) {
|
||||
// add tag for processor
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Start) + "\n";
|
||||
#else
|
||||
gcode += ";" + GCodeProcessor::Wipe_Start_Tag + "\n";
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
for (const Line& line : wipe_path.lines()) {
|
||||
double segment_length = line.length();
|
||||
/* Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one
|
||||
|
@ -192,11 +188,7 @@ namespace Slic3r {
|
|||
);
|
||||
}
|
||||
// add tag for processor
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_End) + "\n";
|
||||
#else
|
||||
gcode += ";" + GCodeProcessor::Wipe_End_Tag + "\n";
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
gcodegen.set_last_pos(wipe_path.points.back());
|
||||
}
|
||||
|
||||
|
@ -655,7 +647,6 @@ namespace DoExport {
|
|||
print_statistics.filament_stats = result.print_statistics.volumes_per_extruder;
|
||||
}
|
||||
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
// if any reserved keyword is found, returns a std::vector containing the first MAX_COUNT keywords found
|
||||
// into pairs containing:
|
||||
// first: source
|
||||
|
@ -714,7 +705,6 @@ namespace DoExport {
|
|||
|
||||
return ret;
|
||||
}
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
} // namespace DoExport
|
||||
|
||||
void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||
|
@ -729,7 +719,6 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re
|
|||
|
||||
print->set_started(psGCodeExport);
|
||||
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
// check if any custom gcode contains keywords used by the gcode processor to
|
||||
// produce time estimation and gcode toolpaths
|
||||
std::vector<std::pair<std::string, std::string>> validation_res = DoExport::validate_custom_gcode(*print);
|
||||
|
@ -743,7 +732,6 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re
|
|||
reports +
|
||||
_(L("This may cause problems in g-code visualization and printing time estimation.")));
|
||||
}
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Exporting G-code..." << log_memory_info();
|
||||
|
||||
|
@ -794,16 +782,11 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re
|
|||
m_processor.process_file(path_tmp, true, [print]() { print->throw_if_canceled(); });
|
||||
// DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics);
|
||||
DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics);
|
||||
#if ENABLE_GCODE_WINDOW
|
||||
if (result != nullptr) {
|
||||
*result = std::move(m_processor.extract_result());
|
||||
// set the filename to the correct value
|
||||
result->filename = path;
|
||||
}
|
||||
#else
|
||||
if (result != nullptr)
|
||||
*result = std::move(m_processor.extract_result());
|
||||
#endif // ENABLE_GCODE_WINDOW
|
||||
BOOST_LOG_TRIVIAL(debug) << "Finished processing gcode, " << log_memory_info();
|
||||
|
||||
if (rename_file(path_tmp, path))
|
||||
|
@ -1168,11 +1151,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
|
||||
// adds tags for time estimators
|
||||
if (print.config().remaining_times.value)
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
_write_format(file, ";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::First_Line_M73_Placeholder).c_str());
|
||||
#else
|
||||
_writeln(file, GCodeProcessor::First_Line_M73_Placeholder_Tag);
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
|
||||
// Prepare the helper object for replacing placeholders in custom G-code and output filename.
|
||||
m_placeholder_parser = print.placeholder_parser();
|
||||
|
@ -1279,11 +1258,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, false);
|
||||
|
||||
// adds tag for processor
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
_write_format(file, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str());
|
||||
#else
|
||||
_write_format(file, ";%s%s\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), ExtrusionEntity::role_to_string(erCustom).c_str());
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
|
||||
// Write the custom start G-code
|
||||
_writeln(file, start_gcode);
|
||||
|
@ -1444,11 +1419,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
_write(file, m_writer.set_fan(false));
|
||||
|
||||
// adds tag for processor
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
_write_format(file, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str());
|
||||
#else
|
||||
_write_format(file, ";%s%s\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), ExtrusionEntity::role_to_string(erCustom).c_str());
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
|
||||
// Process filament-specific gcode in extruder order.
|
||||
{
|
||||
|
@ -1475,11 +1446,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
|
||||
// adds tags for time estimators
|
||||
if (print.config().remaining_times.value)
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
_write_format(file, ";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Last_Line_M73_Placeholder).c_str());
|
||||
#else
|
||||
_writeln(file, GCodeProcessor::Last_Line_M73_Placeholder_Tag);
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
|
||||
print.throw_if_canceled();
|
||||
|
||||
|
@ -1495,11 +1462,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
_write_format(file, "; total filament cost = %.2lf\n", print.m_print_statistics.total_cost);
|
||||
if (print.m_print_statistics.total_toolchanges > 0)
|
||||
_write_format(file, "; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges);
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
_write_format(file, ";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str());
|
||||
#else
|
||||
_writeln(file, GCodeProcessor::Estimated_Printing_Time_Placeholder_Tag);
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
|
||||
// Append full config.
|
||||
_write(file, "\n");
|
||||
|
@ -1799,11 +1762,11 @@ namespace ProcessLayer
|
|||
assert(m600_extruder_before_layer >= 0);
|
||||
// Color Change or Tool Change as Color Change.
|
||||
// add tag for processor
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Color_Change) + ",T" + std::to_string(m600_extruder_before_layer) + "\n";
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Color_Change) + ",T" + std::to_string(m600_extruder_before_layer) + "," + custom_gcode->color + "\n";
|
||||
#else
|
||||
gcode += ";" + GCodeProcessor::Color_Change_Tag + ",T" + std::to_string(m600_extruder_before_layer) + "\n";
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Color_Change) + ",T" + std::to_string(m600_extruder_before_layer) + "\n";
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
|
||||
if (!single_extruder_printer && m600_extruder_before_layer >= 0 && first_extruder_id != (unsigned)m600_extruder_before_layer
|
||||
// && !MMU1
|
||||
|
@ -1828,11 +1791,7 @@ namespace ProcessLayer
|
|||
if (gcode_type == CustomGCode::PausePrint) // Pause print
|
||||
{
|
||||
// add tag for processor
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Pause_Print) + "\n";
|
||||
#else
|
||||
gcode += ";" + GCodeProcessor::Pause_Print_Tag + "\n";
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
//! FIXME_in_fw show message during print pause
|
||||
if (!pause_print_msg.empty())
|
||||
gcode += "M117 " + pause_print_msg + "\n";
|
||||
|
@ -1840,11 +1799,7 @@ namespace ProcessLayer
|
|||
}
|
||||
else {
|
||||
// add tag for processor
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Custom_Code) + "\n";
|
||||
#else
|
||||
gcode += ";" + GCodeProcessor::Custom_Code_Tag + "\n";
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
if (gcode_type == CustomGCode::Template) // Template Custom Gcode
|
||||
gcode += gcodegen.placeholder_parser_process("template_custom_gcode", config.template_custom_gcode, current_extruder_id);
|
||||
else // custom Gcode
|
||||
|
@ -1991,22 +1946,14 @@ void GCode::process_layer(
|
|||
assert(is_decimal_separator_point()); // for the sprintfs
|
||||
|
||||
// add tag for processor
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Layer_Change) + "\n";
|
||||
#else
|
||||
gcode += ";" + GCodeProcessor::Layer_Change_Tag + "\n";
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
// export layer z
|
||||
char buf[64];
|
||||
sprintf(buf, ";Z:%g\n", print_z);
|
||||
gcode += buf;
|
||||
// export layer height
|
||||
float height = first_layer ? static_cast<float>(print_z) : static_cast<float>(print_z) - m_last_layer_z;
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
sprintf(buf, ";%s%g\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height).c_str(), height);
|
||||
#else
|
||||
sprintf(buf, ";%s%g\n", GCodeProcessor::Height_Tag.c_str(), height);
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
gcode += buf;
|
||||
// update caches
|
||||
m_last_layer_z = static_cast<float>(print_z);
|
||||
|
@ -2835,21 +2782,13 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
|
||||
if (path.role() != m_last_processor_extrusion_role) {
|
||||
m_last_processor_extrusion_role = path.role();
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
sprintf(buf, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(m_last_processor_extrusion_role).c_str());
|
||||
#else
|
||||
sprintf(buf, ";%s%s\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), ExtrusionEntity::role_to_string(m_last_processor_extrusion_role).c_str());
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
gcode += buf;
|
||||
}
|
||||
|
||||
if (last_was_wipe_tower || m_last_width != path.width) {
|
||||
m_last_width = path.width;
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
sprintf(buf, ";%s%g\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Width).c_str(), m_last_width);
|
||||
#else
|
||||
sprintf(buf, ";%s%g\n", GCodeProcessor::Width_Tag.c_str(), m_last_width);
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
gcode += buf;
|
||||
}
|
||||
|
||||
|
@ -2863,11 +2802,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
|
||||
if (last_was_wipe_tower || std::abs(m_last_height - path.height) > EPSILON) {
|
||||
m_last_height = path.height;
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
sprintf(buf, ";%s%g\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height).c_str(), m_last_height);
|
||||
#else
|
||||
sprintf(buf, ";%s%g\n", GCodeProcessor::Height_Tag.c_str(), m_last_height);
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
gcode += buf;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,15 +5,11 @@
|
|||
#include "GCodeProcessor.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#if ENABLE_GCODE_WINDOW
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#endif // ENABLE_GCODE_WINDOW
|
||||
|
||||
#include <float.h>
|
||||
#include <assert.h>
|
||||
|
@ -37,7 +33,6 @@ static const Slic3r::Vec3f DEFAULT_EXTRUDER_OFFSET = Slic3r::Vec3f::Zero();
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
const std::vector<std::string> GCodeProcessor::Reserved_Tags = {
|
||||
"TYPE:",
|
||||
"WIPE_START",
|
||||
|
@ -52,21 +47,6 @@ const std::vector<std::string> GCodeProcessor::Reserved_Tags = {
|
|||
"_GP_LAST_LINE_M73_PLACEHOLDER",
|
||||
"_GP_ESTIMATED_PRINTING_TIME_PLACEHOLDER"
|
||||
};
|
||||
#else
|
||||
const std::string GCodeProcessor::Extrusion_Role_Tag = "TYPE:";
|
||||
const std::string GCodeProcessor::Wipe_Start_Tag = "WIPE_START";
|
||||
const std::string GCodeProcessor::Wipe_End_Tag = "WIPE_END";
|
||||
const std::string GCodeProcessor::Height_Tag = "HEIGHT:";
|
||||
const std::string GCodeProcessor::Width_Tag = "WIDTH:";
|
||||
const std::string GCodeProcessor::Layer_Change_Tag = "LAYER_CHANGE";
|
||||
const std::string GCodeProcessor::Color_Change_Tag = "COLOR_CHANGE";
|
||||
const std::string GCodeProcessor::Pause_Print_Tag = "PAUSE_PRINT";
|
||||
const std::string GCodeProcessor::Custom_Code_Tag = "CUSTOM_GCODE";
|
||||
|
||||
const std::string GCodeProcessor::First_Line_M73_Placeholder_Tag = "; _GP_FIRST_LINE_M73_PLACEHOLDER";
|
||||
const std::string GCodeProcessor::Last_Line_M73_Placeholder_Tag = "; _GP_LAST_LINE_M73_PLACEHOLDER";
|
||||
const std::string GCodeProcessor::Estimated_Printing_Time_Placeholder_Tag = "; _GP_ESTIMATED_PRINTING_TIME_PLACEHOLDER";
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
|
||||
const float GCodeProcessor::Wipe_Width = 0.05f;
|
||||
const float GCodeProcessor::Wipe_Height = 0.05f;
|
||||
|
@ -202,9 +182,7 @@ void GCodeProcessor::TimeMachine::reset()
|
|||
max_travel_acceleration = 0.0f;
|
||||
extrude_factor_override_percentage = 1.0f;
|
||||
time = 0.0f;
|
||||
#if ENABLE_EXTENDED_M73_LINES
|
||||
stop_times = std::vector<StopTime>();
|
||||
#endif // ENABLE_EXTENDED_M73_LINES
|
||||
curr.reset();
|
||||
prev.reset();
|
||||
gcode_time.reset();
|
||||
|
@ -332,13 +310,11 @@ void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks)
|
|||
layers_time[block.layer_id - 1] += block_time;
|
||||
}
|
||||
g1_times_cache.push_back({ block.g1_line_id, time });
|
||||
#if ENABLE_EXTENDED_M73_LINES
|
||||
// update times for remaining time to printer stop placeholders
|
||||
auto it_stop_time = std::lower_bound(stop_times.begin(), stop_times.end(), block.g1_line_id,
|
||||
[](const StopTime& t, unsigned int value) { return t.g1_line_id < value; });
|
||||
if (it_stop_time != stop_times.end() && it_stop_time->g1_line_id == block.g1_line_id)
|
||||
it_stop_time->elapsed_time = time;
|
||||
#endif // ENABLE_EXTENDED_M73_LINES
|
||||
}
|
||||
|
||||
if (keep_last_n_blocks)
|
||||
|
@ -361,11 +337,7 @@ void GCodeProcessor::TimeProcessor::reset()
|
|||
machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true;
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<MoveVertex>& moves)
|
||||
#else
|
||||
void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
{
|
||||
boost::nowide::ifstream in(filename);
|
||||
if (!in.good())
|
||||
|
@ -381,16 +353,12 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
return int(::roundf(time_in_seconds / 60.0f));
|
||||
};
|
||||
|
||||
#if ENABLE_EXTENDED_M73_LINES
|
||||
auto time_in_last_minute = [](float time_in_seconds) {
|
||||
assert(time_in_seconds <= 60.0f);
|
||||
return time_in_seconds / 60.0f;
|
||||
};
|
||||
|
||||
auto format_line_M73_main = [](const std::string& mask, int percent, int time) {
|
||||
#else
|
||||
auto format_line_M73 = [](const std::string& mask, int percent, int time) {
|
||||
#endif // ENABLE_EXTENDED_M73_LINES
|
||||
char line_M73[64];
|
||||
sprintf(line_M73, mask.c_str(),
|
||||
std::to_string(percent).c_str(),
|
||||
|
@ -398,7 +366,6 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
return std::string(line_M73);
|
||||
};
|
||||
|
||||
#if ENABLE_EXTENDED_M73_LINES
|
||||
auto format_line_M73_stop_int = [](const std::string& mask, int time) {
|
||||
char line_M73[64];
|
||||
sprintf(line_M73, mask.c_str(), std::to_string(time).c_str());
|
||||
|
@ -414,13 +381,11 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
sprintf(line_M73, mask.c_str(), format_time_float(time).c_str());
|
||||
return std::string(line_M73);
|
||||
};
|
||||
#endif // ENABLE_EXTENDED_M73_LINES
|
||||
|
||||
GCodeReader parser;
|
||||
std::string gcode_line;
|
||||
size_t g1_lines_counter = 0;
|
||||
// keeps track of last exported pair <percent, remaining time>
|
||||
#if ENABLE_EXTENDED_M73_LINES
|
||||
std::array<std::pair<int, int>, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> last_exported_main;
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
last_exported_main[i] = { 0, time_in_minutes(machines[i].time) };
|
||||
|
@ -431,27 +396,18 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
last_exported_stop[i] = time_in_minutes(machines[i].time);
|
||||
}
|
||||
#else
|
||||
std::array<std::pair<int, int>, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> last_exported;
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
last_exported[i] = { 0, time_in_minutes(machines[i].time) };
|
||||
}
|
||||
#endif // ENABLE_EXTENDED_M73_LINES
|
||||
|
||||
// buffer line to export only when greater than 64K to reduce writing calls
|
||||
std::string export_line;
|
||||
|
||||
// replace placeholder lines with the proper final value
|
||||
auto process_placeholders = [&](const std::string& gcode_line) {
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
unsigned int extra_lines_count = 0;
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
|
||||
// remove trailing '\n'
|
||||
std::string line = gcode_line.substr(0, gcode_line.length() - 1);
|
||||
|
||||
std::string ret;
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
if (line.length() > 1) {
|
||||
line = line.substr(1);
|
||||
if (export_remaining_time_enabled &&
|
||||
|
@ -459,49 +415,23 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
const TimeMachine& machine = machines[i];
|
||||
if (machine.enabled) {
|
||||
#if ENABLE_EXTENDED_M73_LINES
|
||||
// export pair <percent, remaining time>
|
||||
ret += format_line_M73_main(machine.line_m73_main_mask.c_str(),
|
||||
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100,
|
||||
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0);
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
++extra_lines_count;
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
|
||||
// export remaining time to next printer stop
|
||||
if (line == reserved_tag(ETags::First_Line_M73_Placeholder) && !machine.stop_times.empty()) {
|
||||
int to_export_stop = time_in_minutes(machine.stop_times.front().elapsed_time);
|
||||
ret += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
|
||||
last_exported_stop[i] = to_export_stop;
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
++extra_lines_count;
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
}
|
||||
#else
|
||||
ret += format_line_M73(machine.line_m73_mask.c_str(),
|
||||
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100,
|
||||
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0);
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
++extra_lines_count;
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
#endif // ENABLE_EXTENDED_M73_LINES
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (line == reserved_tag(ETags::Estimated_Printing_Time_Placeholder)) {
|
||||
#else
|
||||
if (export_remaining_time_enabled && (line == First_Line_M73_Placeholder_Tag || line == Last_Line_M73_Placeholder_Tag)) {
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
const TimeMachine& machine = machines[i];
|
||||
if (machine.enabled) {
|
||||
ret += format_line_M73(machine.line_m73_mask.c_str(),
|
||||
(line == First_Line_M73_Placeholder_Tag) ? 0 : 100,
|
||||
(line == First_Line_M73_Placeholder_Tag) ? time_in_minutes(machines[i].time) : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (line == Estimated_Printing_Time_Placeholder_Tag) {
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
const TimeMachine& machine = machines[i];
|
||||
PrintEstimatedStatistics::ETimeMode mode = static_cast<PrintEstimatedStatistics::ETimeMode>(i);
|
||||
|
@ -514,15 +444,9 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
}
|
||||
}
|
||||
}
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
}
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
return std::tuple(!ret.empty(), ret.empty() ? gcode_line : ret, (extra_lines_count == 0) ? extra_lines_count : extra_lines_count - 1);
|
||||
#else
|
||||
return std::make_pair(!ret.empty(), ret.empty() ? gcode_line : ret);
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
};
|
||||
|
||||
// check for temporary lines
|
||||
|
@ -546,9 +470,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
|
||||
// add lines M73 to exported gcode
|
||||
auto process_line_G1 = [&]() {
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
unsigned int exported_lines_count = 0;
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
if (export_remaining_time_enabled) {
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
const TimeMachine& machine = machines[i];
|
||||
|
@ -559,30 +481,14 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
while (it != machine.g1_times_cache.end() && it->id < g1_lines_counter)
|
||||
++it;
|
||||
if (it != machine.g1_times_cache.end() && it->id == g1_lines_counter) {
|
||||
#if ENABLE_EXTENDED_M73_LINES
|
||||
std::pair<int, int> to_export_main = { int(100.0f * it->elapsed_time / machine.time),
|
||||
time_in_minutes(machine.time - it->elapsed_time) };
|
||||
if (last_exported_main[i] != to_export_main) {
|
||||
export_line += format_line_M73_main(machine.line_m73_main_mask.c_str(),
|
||||
to_export_main.first, to_export_main.second);
|
||||
last_exported_main[i] = to_export_main;
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
++exported_lines_count;
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
#else
|
||||
float elapsed_time = it->elapsed_time;
|
||||
std::pair<int, int> to_export = { int(100.0f * elapsed_time / machine.time),
|
||||
time_in_minutes(machine.time - elapsed_time) };
|
||||
if (last_exported[i] != to_export) {
|
||||
export_line += format_line_M73(machine.line_m73_mask.c_str(),
|
||||
to_export.first, to_export.second);
|
||||
last_exported[i] = to_export;
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
++exported_lines_count;
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
#endif // ENABLE_EXTENDED_M73_LINES
|
||||
}
|
||||
#if ENABLE_EXTENDED_M73_LINES
|
||||
// export remaining time to next printer stop
|
||||
auto it_stop = std::upper_bound(machine.stop_times.begin(), machine.stop_times.end(), it->elapsed_time,
|
||||
[](float value, const TimeMachine::StopTime& t) { return value < t.elapsed_time; });
|
||||
|
@ -593,9 +499,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
if (last_exported_stop[i] != to_export_stop) {
|
||||
export_line += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
|
||||
last_exported_stop[i] = to_export_stop;
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
++exported_lines_count;
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -620,21 +524,16 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
export_line += format_line_M73_stop_float(machine.line_m73_stop_mask.c_str(), time_in_last_minute(it_stop->elapsed_time - it->elapsed_time));
|
||||
|
||||
last_exported_stop[i] = to_export_stop;
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
++exported_lines_count;
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_EXTENDED_M73_LINES
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
return exported_lines_count;
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
};
|
||||
|
||||
// helper function to write to disk
|
||||
|
@ -649,10 +548,8 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
export_line.clear();
|
||||
};
|
||||
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
unsigned int line_id = 0;
|
||||
std::vector<std::pair<unsigned int, unsigned int>> offsets;
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
|
||||
while (std::getline(in, gcode_line)) {
|
||||
if (!in.good()) {
|
||||
|
@ -660,19 +557,13 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nError while reading from file.\n"));
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
++line_id;
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
|
||||
gcode_line += "\n";
|
||||
// replace placeholder lines
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
auto [processed, result, lines_added_count] = process_placeholders(gcode_line);
|
||||
if (processed && lines_added_count > 0)
|
||||
offsets.push_back({ line_id, lines_added_count });
|
||||
#else
|
||||
auto [processed, result] = process_placeholders(gcode_line);
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
gcode_line = result;
|
||||
if (!processed) {
|
||||
// remove temporary lines
|
||||
|
@ -683,15 +574,10 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
parser.parse_line(gcode_line,
|
||||
[&](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
|
||||
if (line.cmd_is("G1")) {
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
unsigned int extra_lines_count = process_line_G1();
|
||||
++g1_lines_counter;
|
||||
if (extra_lines_count > 0)
|
||||
offsets.push_back({ line_id, extra_lines_count });
|
||||
#else
|
||||
process_line_G1();
|
||||
++g1_lines_counter;
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -707,7 +593,6 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
fclose(out);
|
||||
in.close();
|
||||
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
// updates moves' gcode ids which have been modified by the insertion of the M73 lines
|
||||
unsigned int curr_offset_id = 0;
|
||||
unsigned int total_offset = 0;
|
||||
|
@ -718,7 +603,6 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
}
|
||||
move.gcode_id += total_offset;
|
||||
}
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
|
||||
if (rename_file(out_path, filename))
|
||||
throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + out_path + " to " + filename + '\n' +
|
||||
|
@ -800,6 +684,9 @@ void GCodeProcessor::Result::reset() {
|
|||
extruder_colors = std::vector<std::string>();
|
||||
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
|
||||
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
time = 0;
|
||||
}
|
||||
#else
|
||||
|
@ -811,6 +698,9 @@ void GCodeProcessor::Result::reset() {
|
|||
extruder_colors = std::vector<std::string>();
|
||||
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
|
||||
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
||||
|
@ -827,7 +717,6 @@ const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> GCodeProces
|
|||
|
||||
unsigned int GCodeProcessor::s_result_id = 0;
|
||||
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
bool GCodeProcessor::contains_reserved_tag(const std::string& gcode, std::string& found_tag)
|
||||
{
|
||||
bool ret = false;
|
||||
|
@ -879,20 +768,14 @@ bool GCodeProcessor::contains_reserved_tags(const std::string& gcode, unsigned i
|
|||
|
||||
return ret;
|
||||
}
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
|
||||
GCodeProcessor::GCodeProcessor()
|
||||
{
|
||||
reset();
|
||||
#if ENABLE_EXTENDED_M73_LINES
|
||||
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].line_m73_main_mask = "M73 P%s R%s\n";
|
||||
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].line_m73_stop_mask = "M73 C%s\n";
|
||||
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].line_m73_main_mask = "M73 Q%s S%s\n";
|
||||
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].line_m73_stop_mask = "M73 D%s\n";
|
||||
#else
|
||||
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].line_m73_mask = "M73 P%s R%s\n";
|
||||
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].line_m73_mask = "M73 Q%s S%s\n";
|
||||
#endif // ENABLE_EXTENDED_M73_LINES
|
||||
}
|
||||
|
||||
void GCodeProcessor::apply_config(const PrintConfig& config)
|
||||
|
@ -959,11 +842,9 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
|||
m_time_processor.export_remaining_time_enabled = config.remaining_times.value;
|
||||
m_use_volumetric_e = config.use_volumetric_e;
|
||||
|
||||
#if ENABLE_START_GCODE_VISUALIZATION
|
||||
const ConfigOptionFloatOrPercent* first_layer_height = config.option<ConfigOptionFloatOrPercent>("first_layer_height");
|
||||
if (first_layer_height != nullptr)
|
||||
m_first_layer_height = std::abs(first_layer_height->value);
|
||||
#endif // ENABLE_START_GCODE_VISUALIZATION
|
||||
}
|
||||
|
||||
void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
||||
|
@ -1188,11 +1069,9 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
|||
if (use_volumetric_e != nullptr)
|
||||
m_use_volumetric_e = use_volumetric_e->value;
|
||||
|
||||
#if ENABLE_START_GCODE_VISUALIZATION
|
||||
const ConfigOptionFloatOrPercent* first_layer_height = config.option<ConfigOptionFloatOrPercent>("first_layer_height");
|
||||
if (first_layer_height != nullptr)
|
||||
m_first_layer_height = std::abs(first_layer_height->value);
|
||||
#endif // ENABLE_START_GCODE_VISUALIZATION
|
||||
}
|
||||
|
||||
void GCodeProcessor::enable_stealth_time_estimator(bool enabled)
|
||||
|
@ -1214,12 +1093,8 @@ void GCodeProcessor::reset()
|
|||
m_cached_position.reset();
|
||||
m_wiping = false;
|
||||
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
m_line_id = 0;
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
m_last_line_id = 0;
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
m_feedrate = 0.0f;
|
||||
m_width = 0.0f;
|
||||
m_height = 0.0f;
|
||||
|
@ -1240,10 +1115,8 @@ void GCodeProcessor::reset()
|
|||
}
|
||||
|
||||
m_extruded_last_z = 0.0f;
|
||||
#if ENABLE_START_GCODE_VISUALIZATION
|
||||
m_first_layer_height = 0.0f;
|
||||
m_processing_start_custom_gcode = false;
|
||||
#endif // ENABLE_START_GCODE_VISUALIZATION
|
||||
m_g1_line_id = 0;
|
||||
m_layer_id = 0;
|
||||
m_cp_color.reset();
|
||||
|
@ -1258,6 +1131,9 @@ void GCodeProcessor::reset()
|
|||
m_result.id = ++s_result_id;
|
||||
|
||||
m_use_volumetric_e = false;
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
m_last_default_color_id = 0;
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_mm3_per_mm_compare.reset();
|
||||
|
@ -1284,11 +1160,7 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
|
|||
if (cmd.length() == 0) {
|
||||
const std::string_view comment = line.comment();
|
||||
if (comment.length() > 1 && detect_producer(comment))
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
m_parser.quit_parsing();
|
||||
#else
|
||||
m_parser.quit_parsing_file();
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1308,9 +1180,7 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
|
|||
}
|
||||
|
||||
// process gcode
|
||||
#if ENABLE_GCODE_WINDOW
|
||||
m_result.filename = filename;
|
||||
#endif // ENABLE_GCODE_WINDOW
|
||||
m_result.id = ++s_result_id;
|
||||
// 1st move must be a dummy move
|
||||
m_result.moves.emplace_back(MoveVertex());
|
||||
|
@ -1349,11 +1219,7 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
|
|||
|
||||
// post-process to add M73 lines into the gcode
|
||||
if (apply_postprocess)
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
m_time_processor.post_process(filename, m_result.moves);
|
||||
#else
|
||||
m_time_processor.post_process(filename);
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
std::cout << "\n";
|
||||
|
@ -1500,9 +1366,7 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line)
|
|||
{
|
||||
/* std::cout << line.raw() << std::endl; */
|
||||
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
++m_line_id;
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
|
||||
// update start position
|
||||
m_start_position = m_end_position;
|
||||
|
@ -1627,17 +1491,12 @@ void GCodeProcessor::process_tags(const std::string_view comment)
|
|||
if (m_producers_enabled && process_producers_tags(comment))
|
||||
return;
|
||||
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
// extrusion role tag
|
||||
if (boost::starts_with(comment, reserved_tag(ETags::Role))) {
|
||||
set_extrusion_role(ExtrusionEntity::string_to_role(comment.substr(reserved_tag(ETags::Role).length())));
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
if (m_extrusion_role == erExternalPerimeter)
|
||||
m_seams_detector.activate(true);
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
#if ENABLE_START_GCODE_VISUALIZATION
|
||||
m_processing_start_custom_gcode = (m_extrusion_role == erCustom && m_g1_line_id == 0);
|
||||
#endif // ENABLE_START_GCODE_VISUALIZATION
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1652,28 +1511,8 @@ void GCodeProcessor::process_tags(const std::string_view comment)
|
|||
m_wiping = false;
|
||||
return;
|
||||
}
|
||||
#else
|
||||
// extrusion role tag
|
||||
if (boost::starts_with(comment, Extrusion_Role_Tag)) {
|
||||
set_extrusion_role(ExtrusionEntity::string_to_role(comment.substr(Extrusion_Role_Tag.length())));
|
||||
return;
|
||||
}
|
||||
|
||||
// wipe start tag
|
||||
if (boost::starts_with(comment, Wipe_Start_Tag)) {
|
||||
m_wiping = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// wipe end tag
|
||||
if (boost::starts_with(comment, Wipe_End_Tag)) {
|
||||
m_wiping = false;
|
||||
return;
|
||||
}
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
|
||||
if (!m_producers_enabled || m_producer == EProducer::PrusaSlicer) {
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
// height tag
|
||||
if (boost::starts_with(comment, reserved_tag(ETags::Height))) {
|
||||
if (!parse_number(comment.substr(reserved_tag(ETags::Height).size()), m_forced_height))
|
||||
|
@ -1686,26 +1525,61 @@ void GCodeProcessor::process_tags(const std::string_view comment)
|
|||
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ").";
|
||||
return;
|
||||
}
|
||||
#else
|
||||
// height tag
|
||||
if (boost::starts_with(comment, Height_Tag)) {
|
||||
if (!parse_number(comment.substr(Height_Tag.size()), m_forced_height))
|
||||
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ").";
|
||||
return;
|
||||
}
|
||||
// width tag
|
||||
if (boost::starts_with(comment, Width_Tag)) {
|
||||
if (!parse_number(comment.substr(Width_Tag.size()), m_forced_width))
|
||||
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ").";
|
||||
return;
|
||||
}
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
}
|
||||
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
// color change tag
|
||||
if (boost::starts_with(comment, reserved_tag(ETags::Color_Change))) {
|
||||
unsigned char extruder_id = 0;
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
static std::vector<std::string> Default_Colors = {
|
||||
"#0B2C7A", // { 0.043f, 0.173f, 0.478f }, // bluish
|
||||
"#1C8891", // { 0.110f, 0.533f, 0.569f },
|
||||
"#AAF200", // { 0.667f, 0.949f, 0.000f },
|
||||
"#F5CE0A", // { 0.961f, 0.808f, 0.039f },
|
||||
"#D16830", // { 0.820f, 0.408f, 0.188f },
|
||||
"#942616", // { 0.581f, 0.149f, 0.087f } // reddish
|
||||
};
|
||||
|
||||
std::string color = Default_Colors[0];
|
||||
auto is_valid_color = [](const std::string& color) {
|
||||
auto is_hex_digit = [](char c) {
|
||||
return ((c >= '0' && c <= '9') ||
|
||||
(c >= 'A' && c <= 'F') ||
|
||||
(c >= 'a' && c <= 'f'));
|
||||
};
|
||||
|
||||
if (color[0] != '#' || color.length() != 7)
|
||||
return false;
|
||||
for (int i = 1; i <= 6; ++i) {
|
||||
if (!is_hex_digit(color[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
boost::split(tokens, comment, boost::is_any_of(","), boost::token_compress_on);
|
||||
if (tokens.size() > 1) {
|
||||
if (tokens[1][0] == 'T') {
|
||||
int eid;
|
||||
if (!parse_number(tokens[1].substr(1), eid) || eid < 0 || eid > 255) {
|
||||
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Color_Change (" << comment << ").";
|
||||
return;
|
||||
}
|
||||
extruder_id = static_cast<unsigned char>(eid);
|
||||
}
|
||||
}
|
||||
if (tokens.size() > 2) {
|
||||
if (is_valid_color(tokens[2]))
|
||||
color = tokens[2];
|
||||
}
|
||||
else {
|
||||
color = Default_Colors[m_last_default_color_id];
|
||||
++m_last_default_color_id;
|
||||
if (m_last_default_color_id == Default_Colors.size())
|
||||
m_last_default_color_id = 0;
|
||||
}
|
||||
#else
|
||||
if (boost::starts_with(comment.substr(reserved_tag(ETags::Color_Change).size()), ",T")) {
|
||||
int eid;
|
||||
if (!parse_number(comment.substr(reserved_tag(ETags::Color_Change).size() + 2), eid) || eid < 0 || eid > 255) {
|
||||
|
@ -1714,6 +1588,7 @@ void GCodeProcessor::process_tags(const std::string_view comment)
|
|||
}
|
||||
extruder_id = static_cast<unsigned char>(eid);
|
||||
}
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
|
||||
if (extruder_id < m_extruder_colors.size())
|
||||
m_extruder_colors[extruder_id] = static_cast<unsigned char>(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview
|
||||
|
@ -1724,10 +1599,18 @@ void GCodeProcessor::process_tags(const std::string_view comment)
|
|||
if (m_extruder_id == extruder_id) {
|
||||
m_cp_color.current = m_extruder_colors[extruder_id];
|
||||
store_move_vertex(EMoveType::Color_change);
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::ColorChange, extruder_id + 1, color, "" };
|
||||
m_result.custom_gcode_per_print_z.emplace_back(item);
|
||||
process_custom_gcode_time(CustomGCode::ColorChange);
|
||||
process_filaments(CustomGCode::ColorChange);
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
}
|
||||
|
||||
#if !ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
process_custom_gcode_time(CustomGCode::ColorChange);
|
||||
process_filaments(CustomGCode::ColorChange);
|
||||
#endif // !ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1735,6 +1618,10 @@ void GCodeProcessor::process_tags(const std::string_view comment)
|
|||
// pause print tag
|
||||
if (comment == reserved_tag(ETags::Pause_Print)) {
|
||||
store_move_vertex(EMoveType::Pause_Print);
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::PausePrint, m_extruder_id + 1, "", "" };
|
||||
m_result.custom_gcode_per_print_z.emplace_back(item);
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
process_custom_gcode_time(CustomGCode::PausePrint);
|
||||
return;
|
||||
}
|
||||
|
@ -1742,6 +1629,10 @@ void GCodeProcessor::process_tags(const std::string_view comment)
|
|||
// custom code tag
|
||||
if (comment == reserved_tag(ETags::Custom_Code)) {
|
||||
store_move_vertex(EMoveType::Custom_GCode);
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::Custom, m_extruder_id + 1, "", "" };
|
||||
m_result.custom_gcode_per_print_z.emplace_back(item);
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1750,53 +1641,6 @@ void GCodeProcessor::process_tags(const std::string_view comment)
|
|||
++m_layer_id;
|
||||
return;
|
||||
}
|
||||
#else
|
||||
// color change tag
|
||||
if (boost::starts_with(comment, Color_Change_Tag)) {
|
||||
unsigned char extruder_id = 0;
|
||||
if (boost::starts_with(comment.substr(Color_Change_Tag.size()), ",T")) {
|
||||
int eid;
|
||||
if (! parse_number(comment.substr(Color_Change_Tag.size() + 2), eid) || eid < 0 || eid > 255) {
|
||||
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Color_Change (" << comment << ").";
|
||||
return;
|
||||
}
|
||||
extruder_id = static_cast<unsigned char>(eid);
|
||||
}
|
||||
|
||||
m_extruder_colors[extruder_id] = static_cast<unsigned char>(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview
|
||||
++m_cp_color.counter;
|
||||
if (m_cp_color.counter == UCHAR_MAX)
|
||||
m_cp_color.counter = 0;
|
||||
|
||||
if (m_extruder_id == extruder_id) {
|
||||
m_cp_color.current = m_extruder_colors[extruder_id];
|
||||
store_move_vertex(EMoveType::Color_change);
|
||||
}
|
||||
|
||||
process_custom_gcode_time(CustomGCode::ColorChange);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// pause print tag
|
||||
if (comment == Pause_Print_Tag) {
|
||||
store_move_vertex(EMoveType::Pause_Print);
|
||||
process_custom_gcode_time(CustomGCode::PausePrint);
|
||||
return;
|
||||
}
|
||||
|
||||
// custom code tag
|
||||
if (comment == Custom_Code_Tag) {
|
||||
store_move_vertex(EMoveType::Custom_GCode);
|
||||
return;
|
||||
}
|
||||
|
||||
// layer change tag
|
||||
if (comment == Layer_Change_Tag) {
|
||||
++m_layer_id;
|
||||
return;
|
||||
}
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
// mm3_per_mm print tag
|
||||
|
@ -1857,10 +1701,9 @@ bool GCodeProcessor::process_cura_tags(const std::string_view comment)
|
|||
BOOST_LOG_TRIVIAL(warning) << "GCodeProcessor found unknown extrusion role: " << type;
|
||||
}
|
||||
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
if (m_extrusion_role == erExternalPerimeter)
|
||||
m_seams_detector.activate(true);
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1925,9 +1768,7 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string_view comment)
|
|||
pos = cmt.find(" outer perimeter");
|
||||
if (pos == 0) {
|
||||
set_extrusion_role(erExternalPerimeter);
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
m_seams_detector.activate(true);
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2082,10 +1923,8 @@ bool GCodeProcessor::process_craftware_tags(const std::string_view comment)
|
|||
BOOST_LOG_TRIVIAL(warning) << "GCodeProcessor found unknown extrusion role: " << type;
|
||||
}
|
||||
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
if (m_extrusion_role == erExternalPerimeter)
|
||||
m_seams_detector.activate(true);
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -2126,10 +1965,9 @@ bool GCodeProcessor::process_ideamaker_tags(const std::string_view comment)
|
|||
BOOST_LOG_TRIVIAL(warning) << "GCodeProcessor found unknown extrusion role: " << type;
|
||||
}
|
||||
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
if (m_extrusion_role == erExternalPerimeter)
|
||||
m_seams_detector.activate(true);
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2198,9 +2036,7 @@ bool GCodeProcessor::process_kissslicer_tags(const std::string_view comment)
|
|||
pos = comment.find(" 'Perimeter Path'");
|
||||
if (pos == 0) {
|
||||
set_extrusion_role(erExternalPerimeter);
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
m_seams_detector.activate(true);
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2407,11 +2243,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
}
|
||||
|
||||
#if ENABLE_START_GCODE_VISUALIZATION
|
||||
if (type == EMoveType::Extrude && (m_width == 0.0f || m_height == 0.0f))
|
||||
#else
|
||||
if (type == EMoveType::Extrude && (m_extrusion_role == erCustom || m_width == 0.0f || m_height == 0.0f))
|
||||
#endif // ENABLE_START_GCODE_VISUALIZATION
|
||||
type = EMoveType::Travel;
|
||||
|
||||
// time estimate section
|
||||
|
@ -2578,7 +2410,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
machine.calculate_time(TimeProcessor::Planner::queue_size);
|
||||
}
|
||||
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
if (m_seams_detector.is_active()) {
|
||||
// check for seam starting vertex
|
||||
if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter && !m_seams_detector.has_first_vertex())
|
||||
|
@ -2602,7 +2433,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
m_seams_detector.activate(false);
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
|
||||
// store move
|
||||
store_move_vertex(type);
|
||||
|
@ -3057,29 +2887,17 @@ void GCodeProcessor::process_T(const std::string_view command)
|
|||
|
||||
void GCodeProcessor::store_move_vertex(EMoveType type)
|
||||
{
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
m_last_line_id = (type == EMoveType::Color_change || type == EMoveType::Pause_Print || type == EMoveType::Custom_GCode) ?
|
||||
m_line_id + 1 :
|
||||
((type == EMoveType::Seam) ? m_last_line_id : m_line_id);
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
|
||||
MoveVertex vertex = {
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
m_last_line_id,
|
||||
#else
|
||||
(type == EMoveType::Color_change || type == EMoveType::Pause_Print || type == EMoveType::Custom_GCode) ? m_line_id + 1 : m_line_id,
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
type,
|
||||
m_extrusion_role,
|
||||
m_extruder_id,
|
||||
m_cp_color.current,
|
||||
#if ENABLE_START_GCODE_VISUALIZATION
|
||||
Vec3f(m_end_position[X], m_end_position[Y], m_processing_start_custom_gcode ? m_first_layer_height : m_end_position[Z]) + m_extruder_offsets[m_extruder_id],
|
||||
#else
|
||||
Vec3f(m_end_position[X], m_end_position[Y], m_end_position[Z]) + m_extruder_offsets[m_extruder_id],
|
||||
#endif // ENABLE_START_GCODE_VISUALIZATION
|
||||
m_end_position[E] - m_start_position[E],
|
||||
m_feedrate,
|
||||
m_width,
|
||||
|
@ -3091,7 +2909,6 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
|
|||
};
|
||||
m_result.moves.emplace_back(vertex);
|
||||
|
||||
#if ENABLE_EXTENDED_M73_LINES
|
||||
// stores stop time placeholders for later use
|
||||
if (type == EMoveType::Color_change || type == EMoveType::Pause_Print) {
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
|
@ -3102,7 +2919,6 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
|
|||
machine.stop_times.push_back({ m_g1_line_id, 0.0f });
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_EXTENDED_M73_LINES
|
||||
}
|
||||
|
||||
void GCodeProcessor::set_extrusion_role(ExtrusionRole role)
|
||||
|
|
|
@ -12,9 +12,7 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
#include <optional>
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -23,9 +21,7 @@ namespace Slic3r {
|
|||
Noop,
|
||||
Retract,
|
||||
Unretract,
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
Seam,
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
Tool_change,
|
||||
Color_change,
|
||||
Pause_Print,
|
||||
|
@ -82,11 +78,9 @@ namespace Slic3r {
|
|||
|
||||
class GCodeProcessor
|
||||
{
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
static const std::vector<std::string> Reserved_Tags;
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
|
||||
public:
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
enum class ETags : unsigned char
|
||||
{
|
||||
Role,
|
||||
|
@ -109,20 +103,6 @@ namespace Slic3r {
|
|||
// checks the given gcode for reserved tags and returns true when finding any
|
||||
// (the first max_count found tags are returned into found_tag)
|
||||
static bool contains_reserved_tags(const std::string& gcode, unsigned int max_count, std::vector<std::string>& found_tag);
|
||||
#else
|
||||
static const std::string Extrusion_Role_Tag;
|
||||
static const std::string Wipe_Start_Tag;
|
||||
static const std::string Wipe_End_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;
|
||||
static const std::string Width_Tag;
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
|
||||
static const float Wipe_Width;
|
||||
static const float Wipe_Height;
|
||||
|
@ -210,7 +190,6 @@ namespace Slic3r {
|
|||
float time() const;
|
||||
};
|
||||
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
struct MoveVertex
|
||||
{
|
||||
unsigned int gcode_id{ 0 };
|
||||
|
@ -230,7 +209,6 @@ namespace Slic3r {
|
|||
|
||||
float volumetric_rate() const { return feedrate * mm3_per_mm; }
|
||||
};
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
|
||||
private:
|
||||
struct TimeMachine
|
||||
|
@ -269,7 +247,6 @@ namespace Slic3r {
|
|||
float max_travel_acceleration; // mm/s^2
|
||||
float extrude_factor_override_percentage;
|
||||
float time; // s
|
||||
#if ENABLE_EXTENDED_M73_LINES
|
||||
struct StopTime
|
||||
{
|
||||
unsigned int g1_line_id;
|
||||
|
@ -278,9 +255,6 @@ namespace Slic3r {
|
|||
std::vector<StopTime> stop_times;
|
||||
std::string line_m73_main_mask;
|
||||
std::string line_m73_stop_mask;
|
||||
#else
|
||||
std::string line_m73_mask;
|
||||
#endif // ENABLE_EXTENDED_M73_LINES
|
||||
State curr;
|
||||
State prev;
|
||||
CustomGCodeTime gcode_time;
|
||||
|
@ -326,12 +300,8 @@ namespace Slic3r {
|
|||
void reset();
|
||||
|
||||
// post process the file with the given filename to add remaining time lines M73
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
// and updates moves' gcode ids accordingly
|
||||
void post_process(const std::string& filename, std::vector<MoveVertex>& moves);
|
||||
#else
|
||||
void post_process(const std::string& filename);
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
};
|
||||
|
||||
struct UsedFilaments // filaments per ColorChange
|
||||
|
@ -358,27 +328,6 @@ namespace Slic3r {
|
|||
};
|
||||
|
||||
public:
|
||||
#if !ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
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 temperature{ 0.0f }; // Celsius degrees
|
||||
float time{ 0.0f }; // s
|
||||
|
||||
float volumetric_rate() const { return feedrate * mm3_per_mm; }
|
||||
};
|
||||
#endif // !ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
|
||||
struct Result
|
||||
{
|
||||
struct SettingsIds
|
||||
|
@ -393,9 +342,7 @@ namespace Slic3r {
|
|||
printer = "";
|
||||
}
|
||||
};
|
||||
#if ENABLE_GCODE_WINDOW
|
||||
std::string filename;
|
||||
#endif // ENABLE_GCODE_WINDOW
|
||||
unsigned int id;
|
||||
std::vector<MoveVertex> moves;
|
||||
Pointfs bed_shape;
|
||||
|
@ -405,6 +352,9 @@ namespace Slic3r {
|
|||
std::vector<float> filament_diameters;
|
||||
std::vector<float> filament_densities;
|
||||
PrintEstimatedStatistics print_statistics;
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
std::vector<CustomGCode::Item> custom_gcode_per_print_z;
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
int64_t time{ 0 };
|
||||
|
@ -412,7 +362,6 @@ namespace Slic3r {
|
|||
void reset();
|
||||
};
|
||||
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
class SeamsDetector
|
||||
{
|
||||
bool m_active{ false };
|
||||
|
@ -433,7 +382,6 @@ namespace Slic3r {
|
|||
bool is_active() const { return m_active; }
|
||||
bool has_first_vertex() const { return m_first_vertex.has_value(); }
|
||||
};
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
struct DataChecker
|
||||
|
@ -518,12 +466,8 @@ namespace Slic3r {
|
|||
CachedPosition m_cached_position;
|
||||
bool m_wiping;
|
||||
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
unsigned int m_line_id;
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
unsigned int m_last_line_id;
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
float m_feedrate; // mm/s
|
||||
float m_width; // mm
|
||||
float m_height; // mm
|
||||
|
@ -536,17 +480,16 @@ namespace Slic3r {
|
|||
ExtruderColors m_extruder_colors;
|
||||
ExtruderTemps m_extruder_temps;
|
||||
float m_extruded_last_z;
|
||||
#if ENABLE_START_GCODE_VISUALIZATION
|
||||
float m_first_layer_height; // mm
|
||||
bool m_processing_start_custom_gcode;
|
||||
#endif // ENABLE_START_GCODE_VISUALIZATION
|
||||
unsigned int m_g1_line_id;
|
||||
unsigned int m_layer_id;
|
||||
CpColor m_cp_color;
|
||||
bool m_use_volumetric_e;
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
SeamsDetector m_seams_detector;
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
size_t m_last_default_color_id;
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
|
||||
enum class EProducer
|
||||
{
|
||||
|
|
|
@ -34,13 +34,8 @@ public:
|
|||
{
|
||||
// adds tag for analyzer:
|
||||
std::ostringstream str;
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
str << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) << m_layer_height << "\n"; // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming
|
||||
str << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << ExtrusionEntity::role_to_string(erWipeTower) << "\n";
|
||||
#else
|
||||
str << ";" << GCodeProcessor::Height_Tag << m_layer_height << "\n"; // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming
|
||||
str << ";" << GCodeProcessor::Extrusion_Role_Tag << ExtrusionEntity::role_to_string(erWipeTower) << "\n";
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
m_gcode += str.str();
|
||||
change_analyzer_line_width(line_width);
|
||||
}
|
||||
|
@ -48,11 +43,7 @@ public:
|
|||
WipeTowerWriter& change_analyzer_line_width(float line_width) {
|
||||
// adds tag for analyzer:
|
||||
std::stringstream str;
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
str << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Width) << line_width << "\n";
|
||||
#else
|
||||
str << ";" << GCodeProcessor::Width_Tag << line_width << "\n";
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
m_gcode += str.str();
|
||||
return *this;
|
||||
}
|
||||
|
|
|
@ -120,13 +120,8 @@ void GCodeReader::parse_file(const std::string &file, callback_t callback)
|
|||
{
|
||||
boost::nowide::ifstream f(file);
|
||||
std::string line;
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
m_parsing = true;
|
||||
while (m_parsing && std::getline(f, line))
|
||||
#else
|
||||
m_parsing_file = true;
|
||||
while (m_parsing_file && std::getline(f, line))
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
this->parse_line(line, callback);
|
||||
}
|
||||
|
||||
|
|
|
@ -84,12 +84,8 @@ public:
|
|||
{
|
||||
const char *ptr = buffer.c_str();
|
||||
GCodeLine gline;
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
m_parsing = true;
|
||||
while (m_parsing && *ptr != 0) {
|
||||
#else
|
||||
while (*ptr != 0) {
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
gline.reset();
|
||||
ptr = this->parse_line(ptr, gline, callback);
|
||||
}
|
||||
|
@ -113,11 +109,7 @@ public:
|
|||
{ GCodeLine gline; this->parse_line(line.c_str(), gline, callback); }
|
||||
|
||||
void parse_file(const std::string &file, callback_t callback);
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
void quit_parsing() { m_parsing = false; }
|
||||
#else
|
||||
void quit_parsing_file() { m_parsing_file = false; }
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
|
||||
float& x() { return m_position[X]; }
|
||||
float x() const { return m_position[X]; }
|
||||
|
@ -156,11 +148,7 @@ private:
|
|||
char m_extrusion_axis;
|
||||
float m_position[NUM_AXES];
|
||||
bool m_verbose;
|
||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
bool m_parsing{ false };
|
||||
#else
|
||||
bool m_parsing_file{ false };
|
||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||
};
|
||||
|
||||
} /* namespace Slic3r */
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "Fill/Fill.hpp"
|
||||
#include "ShortestPath.hpp"
|
||||
#include "SVG.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
|
@ -258,4 +259,26 @@ void Layer::export_region_fill_surfaces_to_svg_debug(const char *name) const
|
|||
this->export_region_fill_surfaces_to_svg(debug_out_path("Layer-fill_surfaces-%s-%d.svg", name, idx ++).c_str());
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const LayerRegion &layer_region)
|
||||
{
|
||||
BoundingBox bbox;
|
||||
if (!layer_region.slices.surfaces.empty()) {
|
||||
bbox = get_extents(layer_region.slices.surfaces.front());
|
||||
for (auto it = layer_region.slices.surfaces.cbegin() + 1; it != layer_region.slices.surfaces.cend(); ++it)
|
||||
bbox.merge(get_extents(*it));
|
||||
}
|
||||
return bbox;
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const LayerRegionPtrs &layer_regions)
|
||||
{
|
||||
BoundingBox bbox;
|
||||
if (!layer_regions.empty()) {
|
||||
bbox = get_extents(*layer_regions.front());
|
||||
for (auto it = layer_regions.begin() + 1; it != layer_regions.end(); ++it)
|
||||
bbox.merge(get_extents(**it));
|
||||
}
|
||||
return bbox;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -211,6 +211,9 @@ inline std::vector<float> zs_from_layers(const LayerContainer &layers)
|
|||
return zs;
|
||||
}
|
||||
|
||||
extern BoundingBox get_extents(const LayerRegion &layer_region);
|
||||
extern BoundingBox get_extents(const LayerRegionPtrs &layer_regions);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -918,7 +918,6 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
|
|||
// Calculate 2D convex hull of of a projection of the transformed printable volumes into the XY plane.
|
||||
// This method is cheap in that it does not make any unnecessary copy of the volume meshes.
|
||||
// This method is used by the auto arrange function.
|
||||
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||
Polygon ModelObject::convex_hull_2d(const Transform3d& trafo_instance) const
|
||||
{
|
||||
Points pts;
|
||||
|
@ -928,33 +927,6 @@ Polygon ModelObject::convex_hull_2d(const Transform3d& trafo_instance) const
|
|||
}
|
||||
return Geometry::convex_hull(std::move(pts));
|
||||
}
|
||||
#else
|
||||
Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) const
|
||||
{
|
||||
Points pts;
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
if (v->is_model_part()) {
|
||||
Transform3d trafo = trafo_instance * v->get_matrix();
|
||||
const indexed_triangle_set &its = v->mesh().its;
|
||||
if (its.vertices.empty()) {
|
||||
// Using the STL faces.
|
||||
const stl_file& stl = v->mesh().stl;
|
||||
for (const stl_facet &facet : stl.facet_start)
|
||||
for (size_t j = 0; j < 3; ++ j) {
|
||||
Vec3d p = trafo * facet.vertex[j].cast<double>();
|
||||
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
|
||||
}
|
||||
} else {
|
||||
// Using the shared vertices should be a bit quicker than using the STL faces.
|
||||
for (size_t i = 0; i < its.vertices.size(); ++ i) {
|
||||
Vec3d p = trafo * its.vertices[i].cast<double>();
|
||||
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
|
||||
}
|
||||
}
|
||||
}
|
||||
return Geometry::convex_hull(std::move(pts));
|
||||
}
|
||||
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||
|
||||
void ModelObject::center_around_origin(bool include_modifiers)
|
||||
{
|
||||
|
@ -969,19 +941,12 @@ void ModelObject::center_around_origin(bool include_modifiers)
|
|||
this->origin_translation += shift;
|
||||
}
|
||||
|
||||
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||
void ModelObject::ensure_on_bed(bool allow_negative_z)
|
||||
{
|
||||
const double min_z = get_min_z();
|
||||
if (!allow_negative_z || min_z > SINKING_Z_THRESHOLD)
|
||||
translate_instances({ 0.0, 0.0, -min_z });
|
||||
}
|
||||
#else
|
||||
void ModelObject::ensure_on_bed()
|
||||
{
|
||||
translate_instances({ 0.0, 0.0, -get_min_z() });
|
||||
}
|
||||
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||
|
||||
void ModelObject::translate_instances(const Vec3d& vector)
|
||||
{
|
||||
|
@ -1734,6 +1699,10 @@ size_t ModelVolume::split(unsigned int max_extruders)
|
|||
|
||||
for (TriangleMesh *mesh : meshptrs) {
|
||||
mesh->repair();
|
||||
if (mesh->empty())
|
||||
// Repair may have removed unconnected triangles, thus emptying the mesh.
|
||||
continue;
|
||||
|
||||
if (idx == 0)
|
||||
{
|
||||
this->set_mesh(std::move(*mesh));
|
||||
|
@ -1927,20 +1896,10 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
|
|||
Vec3d rotation = get_rotation();
|
||||
rotation.z() = 0.;
|
||||
Transform3d trafo_instance =
|
||||
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||
Geometry::assemble_transform(get_offset().z() * Vec3d::UnitZ(), rotation,
|
||||
get_scaling_factor(), get_mirror());
|
||||
#else
|
||||
Geometry::assemble_transform(Vec3d::Zero(), rotation,
|
||||
get_scaling_factor(), get_mirror());
|
||||
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||
Geometry::assemble_transform(get_offset().z() * Vec3d::UnitZ(), rotation, get_scaling_factor(), get_mirror());
|
||||
|
||||
Polygon p = get_object()->convex_hull_2d(trafo_instance);
|
||||
|
||||
#if !ENABLE_ALLOW_NEGATIVE_Z
|
||||
assert(!p.points.empty());
|
||||
#endif // !ENABLE_ALLOW_NEGATIVE_Z
|
||||
|
||||
// if (!p.points.empty()) {
|
||||
// Polygons pp{p};
|
||||
// pp = p.simplify(scaled<double>(SIMPLIFY_TOLERANCE_MM));
|
||||
|
@ -1958,14 +1917,16 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
|
|||
indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const
|
||||
{
|
||||
TriangleSelector selector(mv.mesh());
|
||||
selector.deserialize(m_data);
|
||||
// Reset of TriangleSelector is done inside TriangleSelector's constructor, so we don't need it to perform it again in deserialize().
|
||||
selector.deserialize(m_data, false);
|
||||
return selector.get_facets(type);
|
||||
}
|
||||
|
||||
indexed_triangle_set FacetsAnnotation::get_facets_strict(const ModelVolume& mv, EnforcerBlockerType type) const
|
||||
{
|
||||
TriangleSelector selector(mv.mesh());
|
||||
selector.deserialize(m_data);
|
||||
// Reset of TriangleSelector is done inside TriangleSelector's constructor, so we don't need it to perform it again in deserialize().
|
||||
selector.deserialize(m_data, false);
|
||||
return selector.get_facets_strict(type);
|
||||
}
|
||||
|
||||
|
|
|
@ -322,12 +322,8 @@ public:
|
|||
Polygon convex_hull_2d(const Transform3d &trafo_instance) const;
|
||||
|
||||
void center_around_origin(bool include_modifiers = true);
|
||||
|
||||
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||
void ensure_on_bed(bool allow_negative_z = false);
|
||||
#else
|
||||
void ensure_on_bed();
|
||||
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||
|
||||
void translate_instances(const Vec3d& vector);
|
||||
void translate_instance(size_t instance_idx, const Vec3d& vector);
|
||||
void translate(const Vec3d &vector) { this->translate(vector(0), vector(1), vector(2)); }
|
||||
|
@ -1176,9 +1172,7 @@ void check_model_ids_validity(const Model &model);
|
|||
void check_model_ids_equal(const Model &model1, const Model &model2);
|
||||
#endif /* NDEBUG */
|
||||
|
||||
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||
static const float SINKING_Z_THRESHOLD = -0.001f;
|
||||
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <mutex>
|
||||
#include <boost/thread/lock_guard.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
struct ColoredLine {
|
||||
|
@ -38,6 +40,10 @@ struct segment_traits<Slic3r::ColoredLine> {
|
|||
};
|
||||
}
|
||||
|
||||
//#define MMU_SEGMENTATION_DEBUG_GRAPH
|
||||
//#define MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
//#define MMU_SEGMENTATION_DEBUG_INPUT
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Assumes that is at most same projected_l length or below than projection_l
|
||||
|
@ -74,7 +80,7 @@ struct PaintedLine
|
|||
|
||||
struct PaintedLineVisitor
|
||||
{
|
||||
PaintedLineVisitor(const EdgeGrid::Grid &grid, std::vector<PaintedLine> &painted_lines, size_t reserve) : grid(grid), painted_lines(painted_lines)
|
||||
PaintedLineVisitor(const EdgeGrid::Grid &grid, std::vector<PaintedLine> &painted_lines, std::mutex &painted_lines_mutex, size_t reserve) : grid(grid), painted_lines(painted_lines), painted_lines_mutex(painted_lines_mutex)
|
||||
{
|
||||
painted_lines_set.reserve(reserve);
|
||||
}
|
||||
|
@ -115,8 +121,11 @@ struct PaintedLineVisitor
|
|||
if ((line_to_test_projected.a - grid_line.a).cast<double>().squaredNorm() > (line_to_test_projected.b - grid_line.a).cast<double>().squaredNorm())
|
||||
line_to_test_projected.reverse();
|
||||
|
||||
painted_lines.push_back({it_contour_and_segment->first, it_contour_and_segment->second, line_to_test_projected, this->color});
|
||||
painted_lines_set.insert(*it_contour_and_segment);
|
||||
{
|
||||
boost::lock_guard<std::mutex> lock(painted_lines_mutex);
|
||||
painted_lines.push_back({it_contour_and_segment->first, it_contour_and_segment->second, line_to_test_projected, this->color});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,6 +136,7 @@ struct PaintedLineVisitor
|
|||
|
||||
const EdgeGrid::Grid &grid;
|
||||
std::vector<PaintedLine> &painted_lines;
|
||||
std::mutex &painted_lines_mutex;
|
||||
Line line_to_test;
|
||||
std::unordered_set<std::pair<size_t, size_t>, boost::hash<std::pair<size_t, size_t>>> painted_lines_set;
|
||||
int color = -1;
|
||||
|
@ -136,14 +146,14 @@ struct PaintedLineVisitor
|
|||
static inline const double append_threshold2 = Slic3r::sqr(append_threshold);
|
||||
};
|
||||
|
||||
static std::vector<ColoredLine> to_colored_lines(const Polygon &polygon, int color)
|
||||
static std::vector<ColoredLine> to_colored_lines(const EdgeGrid::Contour &contour, int color)
|
||||
{
|
||||
std::vector<ColoredLine> lines;
|
||||
if (polygon.points.size() > 2) {
|
||||
lines.reserve(polygon.points.size());
|
||||
for (auto it = polygon.points.begin(); it != polygon.points.end() - 1; ++it)
|
||||
if (contour.num_segments() > 2) {
|
||||
lines.reserve(contour.num_segments());
|
||||
for (auto it = contour.begin(); it != contour.end() - 1; ++it)
|
||||
lines.push_back({Line(*it, *(it + 1)), color});
|
||||
lines.push_back({Line(polygon.points.back(), polygon.points.front()), color});
|
||||
lines.push_back({Line(contour.back(), contour.front()), color});
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
@ -238,7 +248,9 @@ static std::vector<ColoredLine> colorize_line(const Line & line_to_
|
|||
std::vector<PaintedLine> &painted_lines)
|
||||
{
|
||||
std::vector<PaintedLine> internal_painted;
|
||||
for (size_t line_idx = start_idx; line_idx <= end_idx; ++line_idx) { internal_painted.emplace_back(painted_lines[line_idx]); }
|
||||
for (size_t line_idx = start_idx; line_idx <= end_idx; ++line_idx)
|
||||
internal_painted.emplace_back(painted_lines[line_idx]);
|
||||
|
||||
const int filter_eps_value = scale_(0.1f);
|
||||
std::vector<PaintedLine> filtered_lines;
|
||||
filtered_lines.emplace_back(internal_painted.front());
|
||||
|
@ -324,18 +336,18 @@ static std::vector<ColoredLine> colorize_line(const Line & line_to_
|
|||
if (line_1.line.length() <= scale_(0.2)) line_1.color = line_0.color;
|
||||
}
|
||||
|
||||
std::vector<ColoredLine> colored_lines_simpl;
|
||||
colored_lines_simpl.emplace_back(final_lines.front());
|
||||
std::vector<ColoredLine> colored_lines_simple;
|
||||
colored_lines_simple.emplace_back(final_lines.front());
|
||||
for (size_t line_idx = 1; line_idx < final_lines.size(); ++line_idx) {
|
||||
const ColoredLine &line_0 = final_lines[line_idx];
|
||||
|
||||
if (colored_lines_simpl.back().color == line_0.color)
|
||||
colored_lines_simpl.back().line.b = line_0.line.b;
|
||||
if (colored_lines_simple.back().color == line_0.color)
|
||||
colored_lines_simple.back().line.b = line_0.line.b;
|
||||
else
|
||||
colored_lines_simpl.emplace_back(line_0);
|
||||
colored_lines_simple.emplace_back(line_0);
|
||||
}
|
||||
|
||||
final_lines = colored_lines_simpl;
|
||||
final_lines = colored_lines_simple;
|
||||
|
||||
if (final_lines.size() > 1) {
|
||||
if (final_lines.front().color != final_lines[1].color && final_lines.front().line.length() <= scale_(0.2)) {
|
||||
|
@ -354,13 +366,12 @@ static std::vector<ColoredLine> colorize_line(const Line & line_to_
|
|||
return final_lines;
|
||||
}
|
||||
|
||||
static std::vector<ColoredLine> colorize_polygon(const Polygon &poly, const size_t start_idx, const size_t end_idx, std::vector<PaintedLine> &painted_lines)
|
||||
static std::vector<ColoredLine> colorize_polygon(const EdgeGrid::Contour &contour, const size_t start_idx, const size_t end_idx, std::vector<PaintedLine> &painted_lines)
|
||||
{
|
||||
std::vector<ColoredLine> new_lines;
|
||||
Lines lines = poly.lines();
|
||||
|
||||
new_lines.reserve(end_idx - start_idx + 1);
|
||||
for (size_t idx = 0; idx < painted_lines[start_idx].line_idx; ++idx)
|
||||
new_lines.emplace_back(ColoredLine{lines[idx], 0});
|
||||
new_lines.emplace_back(ColoredLine{contour.get_segment(idx), 0});
|
||||
|
||||
for (size_t first_idx = start_idx; first_idx <= end_idx; ++first_idx) {
|
||||
size_t second_idx = first_idx;
|
||||
|
@ -368,18 +379,18 @@ static std::vector<ColoredLine> colorize_polygon(const Polygon &poly, const size
|
|||
--second_idx;
|
||||
|
||||
assert(painted_lines[first_idx].line_idx == painted_lines[second_idx].line_idx);
|
||||
std::vector<ColoredLine> lines_c_line = colorize_line(lines[painted_lines[first_idx].line_idx], first_idx, second_idx, painted_lines);
|
||||
std::vector<ColoredLine> lines_c_line = colorize_line(contour.get_segment(painted_lines[first_idx].line_idx), first_idx, second_idx, painted_lines);
|
||||
new_lines.insert(new_lines.end(), lines_c_line.begin(), lines_c_line.end());
|
||||
|
||||
if (second_idx + 1 <= end_idx)
|
||||
for (size_t idx = painted_lines[second_idx].line_idx + 1; idx < painted_lines[second_idx + 1].line_idx; ++idx)
|
||||
new_lines.emplace_back(ColoredLine{lines[idx], 0});
|
||||
new_lines.emplace_back(ColoredLine{contour.get_segment(idx), 0});
|
||||
|
||||
first_idx = second_idx;
|
||||
}
|
||||
|
||||
for (size_t idx = painted_lines[end_idx].line_idx + 1; idx < poly.size(); ++idx)
|
||||
new_lines.emplace_back(ColoredLine{lines[idx], 0});
|
||||
for (size_t idx = painted_lines[end_idx].line_idx + 1; idx < contour.num_segments(); ++idx)
|
||||
new_lines.emplace_back(ColoredLine{contour.get_segment(idx), 0});
|
||||
|
||||
for (size_t line_idx = 2; line_idx < new_lines.size(); ++line_idx) {
|
||||
const ColoredLine &line_0 = new_lines[line_idx - 2];
|
||||
|
@ -456,15 +467,16 @@ static std::vector<ColoredLine> colorize_polygon(const Polygon &poly, const size
|
|||
return new_lines;
|
||||
}
|
||||
|
||||
static std::vector<std::vector<ColoredLine>> colorize_polygons(const Polygons &polygons, std::vector<PaintedLine> &painted_lines)
|
||||
static std::vector<std::vector<ColoredLine>> colorize_polygons(const std::vector<EdgeGrid::Contour> &contours, std::vector<PaintedLine> &painted_lines)
|
||||
{
|
||||
const size_t start_idx = 0;
|
||||
const size_t end_idx = painted_lines.size() - 1;
|
||||
|
||||
std::vector<std::vector<ColoredLine>> new_polygons;
|
||||
new_polygons.reserve(contours.size());
|
||||
|
||||
for (size_t idx = 0; idx < painted_lines[start_idx].contour_idx; ++idx)
|
||||
new_polygons.emplace_back(to_colored_lines(polygons[idx], 0));
|
||||
new_polygons.emplace_back(to_colored_lines(contours[idx], 0));
|
||||
|
||||
for (size_t first_idx = start_idx; first_idx <= end_idx; ++first_idx) {
|
||||
size_t second_idx = first_idx;
|
||||
|
@ -473,18 +485,17 @@ static std::vector<std::vector<ColoredLine>> colorize_polygons(const Polygons &p
|
|||
--second_idx;
|
||||
|
||||
assert(painted_lines[first_idx].contour_idx == painted_lines[second_idx].contour_idx);
|
||||
std::vector<ColoredLine> polygon_c = colorize_polygon(polygons[painted_lines[first_idx].contour_idx], first_idx, second_idx, painted_lines);
|
||||
new_polygons.emplace_back(polygon_c);
|
||||
new_polygons.emplace_back(colorize_polygon(contours[painted_lines[first_idx].contour_idx], first_idx, second_idx, painted_lines));
|
||||
|
||||
if (second_idx + 1 <= end_idx)
|
||||
for (size_t idx = painted_lines[second_idx].contour_idx + 1; idx < painted_lines[second_idx + 1].contour_idx; ++idx)
|
||||
new_polygons.emplace_back(to_colored_lines(polygons[idx], 0));
|
||||
new_polygons.emplace_back(to_colored_lines(contours[idx], 0));
|
||||
|
||||
first_idx = second_idx;
|
||||
}
|
||||
|
||||
for (size_t idx = painted_lines[end_idx].contour_idx + 1; idx < polygons.size(); ++idx)
|
||||
new_polygons.emplace_back(to_colored_lines(polygons[idx], 0));
|
||||
for (size_t idx = painted_lines[end_idx].contour_idx + 1; idx < contours.size(); ++idx)
|
||||
new_polygons.emplace_back(to_colored_lines(contours[idx], 0));
|
||||
|
||||
return new_polygons;
|
||||
}
|
||||
|
@ -507,7 +518,6 @@ struct MMU_Graph
|
|||
size_t to_idx;
|
||||
int color;
|
||||
ARC_TYPE type;
|
||||
bool used{false};
|
||||
|
||||
bool operator==(const Arc &rhs) const { return (from_idx == rhs.from_idx) && (to_idx == rhs.to_idx) && (color == rhs.color) && (type == rhs.type); }
|
||||
bool operator!=(const Arc &rhs) const { return !operator==(rhs); }
|
||||
|
@ -515,15 +525,16 @@ struct MMU_Graph
|
|||
|
||||
struct Node
|
||||
{
|
||||
Point point;
|
||||
std::list<MMU_Graph::Arc> neighbours;
|
||||
Point point;
|
||||
std::list<size_t> arc_idxs;
|
||||
|
||||
void remove_edge(const size_t to_idx)
|
||||
void remove_edge(const size_t to_idx, MMU_Graph &graph)
|
||||
{
|
||||
for (auto arc_it = this->neighbours.begin(); arc_it != this->neighbours.end(); ++arc_it) {
|
||||
if (arc_it->to_idx == to_idx) {
|
||||
assert(arc_it->type != ARC_TYPE::BORDER);
|
||||
this->neighbours.erase(arc_it);
|
||||
for (auto arc_it = this->arc_idxs.begin(); arc_it != this->arc_idxs.end(); ++arc_it) {
|
||||
MMU_Graph::Arc &arc = graph.arcs[*arc_it];
|
||||
if (arc.to_idx == to_idx) {
|
||||
assert(arc.type != ARC_TYPE::BORDER);
|
||||
this->arc_idxs.erase(arc_it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -539,8 +550,8 @@ struct MMU_Graph
|
|||
|
||||
void remove_edge(const size_t from_idx, const size_t to_idx)
|
||||
{
|
||||
nodes[from_idx].remove_edge(to_idx);
|
||||
nodes[to_idx].remove_edge(from_idx);
|
||||
nodes[from_idx].remove_edge(to_idx, *this);
|
||||
nodes[to_idx].remove_edge(from_idx, *this);
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t get_global_index(const size_t poly_idx, const size_t point_idx) const { return polygon_idx_offset[poly_idx] + point_idx; }
|
||||
|
@ -548,42 +559,55 @@ struct MMU_Graph
|
|||
void append_edge(const size_t &from_idx, const size_t &to_idx, int color = -1, ARC_TYPE type = ARC_TYPE::NON_BORDER)
|
||||
{
|
||||
// Don't append duplicate edges between the same nodes.
|
||||
for (const MMU_Graph::Arc &arc : this->nodes[from_idx].neighbours)
|
||||
if (arc.to_idx == to_idx)
|
||||
for (const size_t &arc_idx : this->nodes[from_idx].arc_idxs)
|
||||
if (arcs[arc_idx].to_idx == to_idx)
|
||||
return;
|
||||
for (const MMU_Graph::Arc &arc : this->nodes[to_idx].neighbours)
|
||||
if (arc.to_idx == to_idx)
|
||||
for (const size_t &arc_idx : this->nodes[to_idx].arc_idxs)
|
||||
if (arcs[arc_idx].to_idx == to_idx)
|
||||
return;
|
||||
|
||||
this->nodes[from_idx].neighbours.push_back({from_idx, to_idx, color, type});
|
||||
this->nodes[to_idx].neighbours.push_back({to_idx, from_idx, color, type});
|
||||
this->nodes[from_idx].arc_idxs.push_back(this->arcs.size());
|
||||
this->arcs.push_back({from_idx, to_idx, color, type});
|
||||
this->arcs.push_back({to_idx, from_idx, color, type});
|
||||
|
||||
// Always insert only one directed arc for the input polygons.
|
||||
// Two directed arcs in both directions are inserted if arcs aren't between points of the input polygons.
|
||||
if (type == ARC_TYPE::NON_BORDER) {
|
||||
this->nodes[to_idx].arc_idxs.push_back(this->arcs.size());
|
||||
this->arcs.push_back({to_idx, from_idx, color, type});
|
||||
}
|
||||
}
|
||||
|
||||
// Ignoring arcs in the opposite direction
|
||||
MMU_Graph::Arc get_arc(size_t idx) { return this->arcs[idx * 2]; }
|
||||
// It assumes that between points of the input polygons is always only one directed arc,
|
||||
// with the same direction as lines of the input polygon.
|
||||
[[nodiscard]] MMU_Graph::Arc get_border_arc(size_t idx) const {
|
||||
assert(idx < this->all_border_points);
|
||||
return this->arcs[idx];
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t nodes_count() const { return this->nodes.size(); }
|
||||
|
||||
void remove_nodes_with_one_arc()
|
||||
{
|
||||
std::queue<size_t> update_queue;
|
||||
for (const MMU_Graph::Node &node : this->nodes)
|
||||
if (node.neighbours.size() == 1) update_queue.emplace(&node - &this->nodes.front());
|
||||
for (const MMU_Graph::Node &node : this->nodes) {
|
||||
size_t node_idx = &node - &this->nodes.front();
|
||||
// Skip nodes that represent points of input polygons.
|
||||
if (node.arc_idxs.size() == 1 && node_idx >= this->all_border_points)
|
||||
update_queue.emplace(&node - &this->nodes.front());
|
||||
}
|
||||
|
||||
while (!update_queue.empty()) {
|
||||
size_t node_from_idx = update_queue.front();
|
||||
MMU_Graph::Node &node_from = this->nodes[update_queue.front()];
|
||||
update_queue.pop();
|
||||
if (node_from.neighbours.empty())
|
||||
if (node_from.arc_idxs.empty())
|
||||
continue;
|
||||
|
||||
assert(node_from.neighbours.size() == 1);
|
||||
size_t node_to_idx = node_from.neighbours.front().to_idx;
|
||||
assert(node_from.arc_idxs.size() == 1);
|
||||
size_t node_to_idx = arcs[node_from.arc_idxs.front()].to_idx;
|
||||
MMU_Graph::Node &node_to = this->nodes[node_to_idx];
|
||||
this->remove_edge(node_from_idx, node_to_idx);
|
||||
if (node_to.neighbours.size() == 1)
|
||||
if (node_to.arc_idxs.size() == 1 && node_to_idx >= this->all_border_points)
|
||||
update_queue.emplace(node_to_idx);
|
||||
}
|
||||
}
|
||||
|
@ -660,17 +684,17 @@ struct MMU_Graph
|
|||
vertex.color(-1);
|
||||
Point vertex_point = mk_point(vertex);
|
||||
|
||||
const Point &first_point = this->nodes[this->get_arc(vertex.incident_edge()->cell()->source_index()).from_idx].point;
|
||||
const Point &second_point = this->nodes[this->get_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx].point;
|
||||
const Point &first_point = this->nodes[this->get_border_arc(vertex.incident_edge()->cell()->source_index()).from_idx].point;
|
||||
const Point &second_point = this->nodes[this->get_border_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx].point;
|
||||
|
||||
if (vertex_equal_to_point(&vertex, first_point)) {
|
||||
assert(vertex.color() != vertex.incident_edge()->cell()->source_index());
|
||||
assert(vertex.color() != vertex.incident_edge()->twin()->cell()->source_index());
|
||||
vertex.color(this->get_arc(vertex.incident_edge()->cell()->source_index()).from_idx);
|
||||
vertex.color(this->get_border_arc(vertex.incident_edge()->cell()->source_index()).from_idx);
|
||||
} else if (vertex_equal_to_point(&vertex, second_point)) {
|
||||
assert(vertex.color() != vertex.incident_edge()->cell()->source_index());
|
||||
assert(vertex.color() != vertex.incident_edge()->twin()->cell()->source_index());
|
||||
vertex.color(this->get_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx);
|
||||
vertex.color(this->get_border_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx);
|
||||
} else if (bbox.contains(vertex_point)) {
|
||||
if (auto [contour_pt, c_dist_sqr] = closest_contour_point.find(vertex_point); contour_pt != nullptr && c_dist_sqr < 3 * SCALED_EPSILON) {
|
||||
vertex.color(this->get_global_index(contour_pt->m_contour_idx, contour_pt->m_point_idx));
|
||||
|
@ -684,6 +708,35 @@ struct MMU_Graph
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void garbage_collect()
|
||||
{
|
||||
std::vector<int> nodes_map(this->nodes.size(), -1);
|
||||
int nodes_count = 0;
|
||||
size_t arcs_count = 0;
|
||||
for (const MMU_Graph::Node &node : this->nodes)
|
||||
if (size_t node_idx = &node - &this->nodes.front(); !node.arc_idxs.empty()) {
|
||||
nodes_map[node_idx] = nodes_count++;
|
||||
arcs_count += node.arc_idxs.size();
|
||||
}
|
||||
|
||||
std::vector<MMU_Graph::Node> new_nodes;
|
||||
std::vector<MMU_Graph::Arc> new_arcs;
|
||||
new_nodes.reserve(nodes_count);
|
||||
new_arcs.reserve(arcs_count);
|
||||
for (const MMU_Graph::Node &node : this->nodes)
|
||||
if (size_t node_idx = &node - &this->nodes.front(); nodes_map[node_idx] >= 0) {
|
||||
new_nodes.push_back({node.point});
|
||||
for (const size_t &arc_idx : node.arc_idxs) {
|
||||
const Arc &arc = this->arcs[arc_idx];
|
||||
new_nodes.back().arc_idxs.emplace_back(new_arcs.size());
|
||||
new_arcs.push_back({size_t(nodes_map[arc.from_idx]), size_t(nodes_map[arc.to_idx]), arc.color, arc.type});
|
||||
}
|
||||
}
|
||||
|
||||
this->nodes = std::move(new_nodes);
|
||||
this->arcs = std::move(new_arcs);
|
||||
}
|
||||
};
|
||||
|
||||
static inline void mark_processed(const voronoi_diagram<double>::const_edge_iterator &edge_iterator)
|
||||
|
@ -825,7 +878,7 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vector<std::vector<Col
|
|||
Point contour_intersection;
|
||||
|
||||
if (line_intersection_with_epsilon(contour_line.line, edge_line, &contour_intersection)) {
|
||||
const MMU_Graph::Arc &graph_arc = graph.get_arc(edge_it->cell()->source_index());
|
||||
const MMU_Graph::Arc &graph_arc = graph.get_border_arc(edge_it->cell()->source_index());
|
||||
const size_t from_idx = (edge_it->vertex1() != nullptr) ? edge_it->vertex1()->color() : edge_it->vertex0()->color();
|
||||
size_t to_idx = ((contour_line.line.a - contour_intersection).cast<double>().squaredNorm() <
|
||||
(contour_line.line.b - contour_intersection).cast<double>().squaredNorm()) ?
|
||||
|
@ -859,12 +912,12 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vector<std::vector<Col
|
|||
if (edge_it->vertex1()->color() < graph.nodes_count() && !graph.is_vertex_on_contour(edge_it->vertex1())) {
|
||||
Line contour_line_twin = lines_colored[edge_it->twin()->cell()->source_index()].line;
|
||||
if (line_intersection_with_epsilon(contour_line_twin, edge_line, &intersection)) {
|
||||
const MMU_Graph::Arc &graph_arc = graph.get_arc(edge_it->twin()->cell()->source_index());
|
||||
const MMU_Graph::Arc &graph_arc = graph.get_border_arc(edge_it->twin()->cell()->source_index());
|
||||
const size_t to_idx_l = is_point_closer_to_beginning_of_line(contour_line_twin, intersection) ? graph_arc.from_idx :
|
||||
graph_arc.to_idx;
|
||||
graph.append_edge(edge_it->vertex1()->color(), to_idx_l);
|
||||
} else if (line_intersection_with_epsilon(contour_line, edge_line, &intersection)) {
|
||||
const MMU_Graph::Arc &graph_arc = graph.get_arc(edge_it->cell()->source_index());
|
||||
const MMU_Graph::Arc &graph_arc = graph.get_border_arc(edge_it->cell()->source_index());
|
||||
const size_t to_idx_l = is_point_closer_to_beginning_of_line(contour_line, intersection) ? graph_arc.from_idx : graph_arc.to_idx;
|
||||
graph.append_edge(edge_it->vertex1()->color(), to_idx_l);
|
||||
}
|
||||
|
@ -912,27 +965,25 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vector<std::vector<Col
|
|||
Line second_part(intersection, real_v1);
|
||||
|
||||
if (!has_same_color(contour_line_prev, colored_line)) {
|
||||
if (points_inside(contour_line_prev.line, contour_line, first_part.b)) {
|
||||
graph.append_edge(edge_it->vertex0()->color(), graph.get_arc(edge_it->cell()->source_index()).from_idx);
|
||||
}
|
||||
if (points_inside(contour_line_prev.line, contour_line, second_part.b)) {
|
||||
graph.append_edge(edge_it->vertex1()->color(), graph.get_arc(edge_it->cell()->source_index()).from_idx);
|
||||
}
|
||||
if (points_inside(contour_line_prev.line, contour_line, first_part.b))
|
||||
graph.append_edge(edge_it->vertex0()->color(), graph.get_border_arc(edge_it->cell()->source_index()).from_idx);
|
||||
|
||||
if (points_inside(contour_line_prev.line, contour_line, second_part.b))
|
||||
graph.append_edge(edge_it->vertex1()->color(), graph.get_border_arc(edge_it->cell()->source_index()).from_idx);
|
||||
}
|
||||
} else {
|
||||
const size_t int_point_idx = graph.get_arc(edge_it->cell()->source_index()).to_idx;
|
||||
const size_t int_point_idx = graph.get_border_arc(edge_it->cell()->source_index()).to_idx;
|
||||
const Point int_point = graph.nodes[int_point_idx].point;
|
||||
|
||||
const Line first_part(int_point, real_v0);
|
||||
const Line second_part(int_point, real_v1);
|
||||
|
||||
if (!has_same_color(contour_line_next, colored_line)) {
|
||||
if (points_inside(contour_line, contour_line_next.line, first_part.b)) {
|
||||
if (points_inside(contour_line, contour_line_next.line, first_part.b))
|
||||
graph.append_edge(edge_it->vertex0()->color(), int_point_idx);
|
||||
}
|
||||
if (points_inside(contour_line, contour_line_next.line, second_part.b)) {
|
||||
|
||||
if (points_inside(contour_line, contour_line_next.line, second_part.b))
|
||||
graph.append_edge(edge_it->vertex1()->color(), int_point_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -974,13 +1025,15 @@ static inline Polygon to_polygon(const Lines &lines)
|
|||
// It iterates through all nodes on the border between two different colors, and from this point,
|
||||
// start selection always left most edges for every node to construct CCW polygons.
|
||||
// Assumes that graph is planar (without self-intersection edges)
|
||||
static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(MMU_Graph &graph)
|
||||
static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(const MMU_Graph &graph)
|
||||
{
|
||||
std::vector<bool> used_arcs(graph.arcs.size(), false);
|
||||
// When there is no next arc, then is returned original_arc or edge with is marked as used
|
||||
auto get_next = [&graph](const Line &process_line, MMU_Graph::Arc &original_arc) -> MMU_Graph::Arc & {
|
||||
std::vector<std::pair<MMU_Graph::Arc *, double>> sorted_arcs;
|
||||
for (MMU_Graph::Arc &arc : graph.nodes[original_arc.to_idx].neighbours) {
|
||||
if (graph.nodes[arc.to_idx].point == process_line.a || arc.used)
|
||||
auto get_next = [&graph, &used_arcs](const Line &process_line, const MMU_Graph::Arc &original_arc) -> const MMU_Graph::Arc & {
|
||||
std::vector<std::pair<const MMU_Graph::Arc *, double>> sorted_arcs;
|
||||
for (const size_t &arc_idx : graph.nodes[original_arc.to_idx].arc_idxs) {
|
||||
const MMU_Graph::Arc &arc = graph.arcs[arc_idx];
|
||||
if (graph.nodes[arc.to_idx].point == process_line.a || used_arcs[arc_idx])
|
||||
continue;
|
||||
|
||||
assert(original_arc.to_idx == arc.from_idx);
|
||||
|
@ -995,11 +1048,11 @@ static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(MMU_Grap
|
|||
}
|
||||
|
||||
std::sort(sorted_arcs.begin(), sorted_arcs.end(),
|
||||
[](std::pair<MMU_Graph::Arc *, double> &l, std::pair<MMU_Graph::Arc *, double> &r) -> bool { return l.second < r.second; });
|
||||
[](std::pair<const MMU_Graph::Arc *, double> &l, std::pair<const MMU_Graph::Arc *, double> &r) -> bool { return l.second < r.second; });
|
||||
|
||||
// Try to return left most edge witch is unused
|
||||
for (auto &sorted_arc : sorted_arcs)
|
||||
if (!sorted_arc.first->used)
|
||||
if (size_t arc_idx = sorted_arc.first - &graph.arcs.front(); !used_arcs[arc_idx])
|
||||
return *sorted_arc.first;
|
||||
|
||||
if (sorted_arcs.empty())
|
||||
|
@ -1008,35 +1061,39 @@ static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(MMU_Grap
|
|||
return *(sorted_arcs.front().first);
|
||||
};
|
||||
|
||||
auto all_arc_used = [&used_arcs](const MMU_Graph::Node &node) -> bool {
|
||||
return std::all_of(node.arc_idxs.cbegin(), node.arc_idxs.cend(), [&used_arcs](const size_t &arc_idx) -> bool { return used_arcs[arc_idx]; });
|
||||
};
|
||||
|
||||
std::vector<std::pair<Polygon, size_t>> polygons_segments;
|
||||
for (MMU_Graph::Node &node : graph.nodes)
|
||||
for (MMU_Graph::Arc &arc : node.neighbours)
|
||||
arc.used = false;
|
||||
|
||||
for (size_t node_idx = 0; node_idx < graph.all_border_points; ++node_idx) {
|
||||
MMU_Graph::Node &node = graph.nodes[node_idx];
|
||||
const MMU_Graph::Node &node = graph.nodes[node_idx];
|
||||
|
||||
for (const size_t &arc_idx : node.arc_idxs) {
|
||||
const MMU_Graph::Arc &arc = graph.arcs[arc_idx];
|
||||
if (arc.type == MMU_Graph::ARC_TYPE::NON_BORDER || used_arcs[arc_idx])continue;
|
||||
|
||||
for (MMU_Graph::Arc &arc : node.neighbours) {
|
||||
if (arc.type == MMU_Graph::ARC_TYPE::NON_BORDER || arc.used) continue;
|
||||
|
||||
Line process_line(node.point, graph.nodes[arc.to_idx].point);
|
||||
arc.used = true;
|
||||
used_arcs[arc_idx] = true;
|
||||
|
||||
Lines face_lines;
|
||||
face_lines.emplace_back(process_line);
|
||||
Point start_p = process_line.a;
|
||||
|
||||
Line p_vec = process_line;
|
||||
MMU_Graph::Arc *p_arc = &arc;
|
||||
Line p_vec = process_line;
|
||||
const MMU_Graph::Arc *p_arc = &arc;
|
||||
do {
|
||||
MMU_Graph::Arc &next = get_next(p_vec, *p_arc);
|
||||
face_lines.emplace_back(Line(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point));
|
||||
if (next.used) break;
|
||||
const MMU_Graph::Arc &next = get_next(p_vec, *p_arc);
|
||||
size_t next_arc_idx = &next - &graph.arcs.front();
|
||||
face_lines.emplace_back(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point);
|
||||
if (used_arcs[next_arc_idx])
|
||||
break;
|
||||
|
||||
next.used = true;
|
||||
p_vec = Line(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point);
|
||||
p_arc = &next;
|
||||
} while (graph.nodes[p_arc->to_idx].point != start_p);
|
||||
used_arcs[next_arc_idx] = true;
|
||||
p_vec = Line(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point);
|
||||
p_arc = &next;
|
||||
} while (graph.nodes[p_arc->to_idx].point != start_p || !all_arc_used(graph.nodes[p_arc->to_idx]));
|
||||
|
||||
Polygon poly = to_polygon(face_lines);
|
||||
if (poly.is_counter_clockwise() && poly.is_valid())
|
||||
|
@ -1049,20 +1106,19 @@ static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(MMU_Grap
|
|||
// Used in remove_multiple_edges_in_vertices()
|
||||
// Returns length of edge with is connected to contour. To this length is include other edges with follows it if they are almost straight (with the
|
||||
// tolerance of 15) And also if node between two subsequent edges is connected only to these two edges.
|
||||
static inline double compute_edge_length(MMU_Graph &graph, size_t start_idx, MMU_Graph::Arc &start_edge)
|
||||
static inline double compute_edge_length(const MMU_Graph &graph, const size_t start_idx, const size_t &start_arc_idx)
|
||||
{
|
||||
for (MMU_Graph::Node &node : graph.nodes)
|
||||
for (MMU_Graph::Arc &arc : node.neighbours)
|
||||
arc.used = false;
|
||||
assert(start_arc_idx < graph.arcs.size());
|
||||
std::vector<bool> used_arcs(graph.arcs.size(), false);
|
||||
|
||||
start_edge.used = true;
|
||||
MMU_Graph::Arc *arc = &start_edge;
|
||||
size_t idx = start_idx;
|
||||
double line_total_length = Line(graph.nodes[idx].point, graph.nodes[arc->to_idx].point).length();
|
||||
while (graph.nodes[arc->to_idx].neighbours.size() == 2) {
|
||||
used_arcs[start_arc_idx] = true;
|
||||
const MMU_Graph::Arc *arc = &graph.arcs[start_arc_idx];
|
||||
size_t idx = start_idx;
|
||||
double line_total_length = (graph.nodes[arc->to_idx].point - graph.nodes[idx].point).cast<double>().norm();;
|
||||
while (graph.nodes[arc->to_idx].arc_idxs.size() == 2) {
|
||||
bool found = false;
|
||||
for (MMU_Graph::Arc &arc_n : graph.nodes[arc->to_idx].neighbours) {
|
||||
if (arc_n.type == MMU_Graph::ARC_TYPE::NON_BORDER && !arc_n.used && arc_n.to_idx != idx) {
|
||||
for (const size_t &arc_idx : graph.nodes[arc->to_idx].arc_idxs) {
|
||||
if (const MMU_Graph::Arc &arc_n = graph.arcs[arc_idx]; arc_n.type == MMU_Graph::ARC_TYPE::NON_BORDER && !used_arcs[arc_idx] && arc_n.to_idx != idx) {
|
||||
Line first_line(graph.nodes[idx].point, graph.nodes[arc->to_idx].point);
|
||||
Line second_line(graph.nodes[arc->to_idx].point, graph.nodes[arc_n.to_idx].point);
|
||||
|
||||
|
@ -1080,8 +1136,8 @@ static inline double compute_edge_length(MMU_Graph &graph, size_t start_idx, MMU
|
|||
idx = arc->to_idx;
|
||||
arc = &arc_n;
|
||||
|
||||
line_total_length += Line(graph.nodes[idx].point, graph.nodes[arc->to_idx].point).length();
|
||||
arc_n.used = true;
|
||||
line_total_length += (graph.nodes[arc->to_idx].point - graph.nodes[idx].point).cast<double>().norm();
|
||||
used_arcs[arc_idx] = true;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
@ -1104,11 +1160,12 @@ static void remove_multiple_edges_in_vertices(MMU_Graph &graph, const std::vecto
|
|||
size_t second_idx = graph.get_global_index(poly_idx, (colored_segment.second + 1) % graph.polygon_sizes[poly_idx]);
|
||||
Line seg_line(graph.nodes[first_idx].point, graph.nodes[second_idx].point);
|
||||
|
||||
if (graph.nodes[first_idx].neighbours.size() >= 3) {
|
||||
if (graph.nodes[first_idx].arc_idxs.size() >= 3) {
|
||||
std::vector<std::pair<MMU_Graph::Arc *, double>> arc_to_check;
|
||||
for (MMU_Graph::Arc &n_arc : graph.nodes[first_idx].neighbours) {
|
||||
for (const size_t &arc_idx : graph.nodes[first_idx].arc_idxs) {
|
||||
MMU_Graph::Arc &n_arc = graph.arcs[arc_idx];
|
||||
if (n_arc.type == MMU_Graph::ARC_TYPE::NON_BORDER) {
|
||||
double total_len = compute_edge_length(graph, first_idx, n_arc);
|
||||
double total_len = compute_edge_length(graph, first_idx, arc_idx);
|
||||
arc_to_check.emplace_back(&n_arc, total_len);
|
||||
}
|
||||
}
|
||||
|
@ -1478,18 +1535,18 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
|
|||
LayerColorStat out;
|
||||
const Layer &layer = *layers[layer_idx];
|
||||
for (const LayerRegion *region : layer.regions())
|
||||
if (const PrintRegionConfig &config = region->region().config();
|
||||
if (const PrintRegionConfig &config = region->region().config();
|
||||
// color_idx == 0 means "don't know" extruder aka the underlying extruder.
|
||||
// As this region may split existing regions, we collect statistics over all regions for color_idx == 0.
|
||||
color_idx == 0 || config.perimeter_extruder == int(color_idx)) {
|
||||
out.extrusion_width = std::max<float>(out.extrusion_width, config.perimeter_extrusion_width);
|
||||
out.top_solid_layers = std::max<float>(out.top_solid_layers, config.top_solid_layers);
|
||||
out.bottom_solid_layers = std::max<float>(out.bottom_solid_layers, config.bottom_solid_layers);
|
||||
out.small_region_threshold = config.gap_fill_enabled.value && config.gap_fill_speed.value > 0 ?
|
||||
// Gap fill enabled. Enable a single line of 1/2 extrusion width.
|
||||
0.5 * config.perimeter_extrusion_width :
|
||||
// Gap fill disabled. Enable two lines slightly overlapping.
|
||||
config.perimeter_extrusion_width + 0.7f * Flow::rounded_rectangle_extrusion_spacing(config.perimeter_extrusion_width, layer.height);
|
||||
out.extrusion_width = std::max<float>(out.extrusion_width, float(config.perimeter_extrusion_width));
|
||||
out.top_solid_layers = std::max<int>(out.top_solid_layers, config.top_solid_layers);
|
||||
out.bottom_solid_layers = std::max<int>(out.bottom_solid_layers, config.bottom_solid_layers);
|
||||
out.small_region_threshold = config.gap_fill_enabled.value && config.gap_fill_speed.value > 0 ?
|
||||
// Gap fill enabled. Enable a single line of 1/2 extrusion width.
|
||||
0.5f * float(config.perimeter_extrusion_width) :
|
||||
// Gap fill disabled. Enable two lines slightly overlapping.
|
||||
float(config.perimeter_extrusion_width) + 0.7f * Flow::rounded_rectangle_extrusion_spacing(float(config.perimeter_extrusion_width), float(layer.height));
|
||||
out.small_region_threshold = scaled<float>(out.small_region_threshold * 0.5f);
|
||||
++ out.num_regions;
|
||||
}
|
||||
|
@ -1603,14 +1660,70 @@ static std::vector<std::vector<std::pair<ExPolygon, size_t>>> merge_segmented_la
|
|||
return segmented_regions_merged;
|
||||
}
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
static void export_regions_to_svg(const std::string &path, const std::vector<std::pair<ExPolygon, size_t>> ®ions, const ExPolygons &lslices)
|
||||
{
|
||||
const std::vector<std::string> colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "yellow"};
|
||||
coordf_t stroke_width = scale_(0.05);
|
||||
BoundingBox bbox = get_extents(lslices);
|
||||
bbox.offset(scale_(1.));
|
||||
::Slic3r::SVG svg(path.c_str(), bbox);
|
||||
|
||||
svg.draw_outline(lslices, "green", "lime", stroke_width);
|
||||
for (const std::pair<ExPolygon, size_t> ®ion : regions) {
|
||||
int region_color = region.second;
|
||||
if (region_color >= 0 && region_color < int(colors.size()))
|
||||
svg.draw(region.first, colors[region_color]);
|
||||
else
|
||||
svg.draw(region.first, "black");
|
||||
}
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_GRAPH
|
||||
static void export_graph_to_svg(const std::string &path, const MMU_Graph &graph, const ExPolygons &lslices)
|
||||
{
|
||||
const std::vector<std::string> colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "green", "yellow"};
|
||||
coordf_t stroke_width = scale_(0.05);
|
||||
BoundingBox bbox = get_extents(lslices);
|
||||
bbox.offset(scale_(1.));
|
||||
::Slic3r::SVG svg(path.c_str(), bbox);
|
||||
for (const MMU_Graph::Node &node : graph.nodes)
|
||||
for (const size_t &arc_idx : node.arc_idxs) {
|
||||
const MMU_Graph::Arc &arc = graph.arcs[arc_idx];
|
||||
Line arc_line(node.point, graph.nodes[arc.to_idx].point);
|
||||
if (arc.type == MMU_Graph::ARC_TYPE::BORDER && arc.color >= 0 && arc.color < int(colors.size()))
|
||||
svg.draw(arc_line, colors[arc.color], stroke_width);
|
||||
else
|
||||
svg.draw(arc_line, "black", stroke_width);
|
||||
}
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_GRAPH
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_INPUT
|
||||
void export_processed_input_expolygons_to_svg(const std::string &path, const LayerRegionPtrs ®ions, const ExPolygons &processed_input_expolygons)
|
||||
{
|
||||
coordf_t stroke_width = scale_(0.05);
|
||||
BoundingBox bbox = get_extents(regions);
|
||||
bbox.merge(get_extents(processed_input_expolygons));
|
||||
bbox.offset(scale_(1.));
|
||||
::Slic3r::SVG svg(path.c_str(), bbox);
|
||||
|
||||
for (LayerRegion *region : regions)
|
||||
svg.draw_outline(region->slices.surfaces, "blue", "cyan", stroke_width);
|
||||
|
||||
svg.draw_outline(processed_input_expolygons, "red", "pink", stroke_width);
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_INPUT
|
||||
|
||||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback)
|
||||
{
|
||||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> segmented_regions(print_object.layers().size());
|
||||
std::vector<std::vector<PaintedLine>> painted_lines(print_object.layers().size());
|
||||
std::array<std::mutex, 64> painted_lines_mutex;
|
||||
std::vector<EdgeGrid::Grid> edge_grids(print_object.layers().size());
|
||||
const ConstLayerPtrsAdaptor layers = print_object.layers();
|
||||
std::vector<ExPolygons> input_expolygons(layers.size());
|
||||
std::vector<Polygons> input_polygons(layers.size());
|
||||
|
||||
throw_on_cancel_callback();
|
||||
|
||||
|
@ -1636,86 +1749,99 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
|||
// This consequently leads to issues with the extraction of colored segments by function extract_colored_segments.
|
||||
// Calling expolygons_simplify fixed these issues.
|
||||
input_expolygons[layer_idx] = smooth_outward(expolygons_simplify(offset_ex(ex_polygons, -10.f * float(SCALED_EPSILON)), 5 * SCALED_EPSILON), 10 * coord_t(SCALED_EPSILON));
|
||||
input_polygons[layer_idx] = to_polygons(input_expolygons[layer_idx]);
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_INPUT
|
||||
{
|
||||
static int iRun = 0;
|
||||
export_processed_input_expolygons_to_svg(debug_out_path("mm-input-%d-%d.svg", layer_idx, iRun++), layers[layer_idx]->regions(), input_expolygons[layer_idx]);
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_INPUT
|
||||
}
|
||||
}); // end of parallel_for
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - slices preparation in parallel - end";
|
||||
|
||||
for (size_t layer_idx = 0; layer_idx < layers.size(); ++layer_idx) {
|
||||
throw_on_cancel_callback();
|
||||
BoundingBox bbox(get_extents(input_polygons[layer_idx]));
|
||||
BoundingBox bbox(get_extents(layers[layer_idx]->regions()));
|
||||
bbox.merge(get_extents(input_expolygons[layer_idx]));
|
||||
// Projected triangles may slightly exceed the input polygons.
|
||||
bbox.offset(20 * SCALED_EPSILON);
|
||||
edge_grids[layer_idx].set_bbox(bbox);
|
||||
edge_grids[layer_idx].create(input_polygons[layer_idx], coord_t(scale_(10.)));
|
||||
edge_grids[layer_idx].create(input_expolygons[layer_idx], coord_t(scale_(10.)));
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - projection of painted triangles - begin";
|
||||
for (const ModelVolume *mv : print_object.model_object()->volumes) {
|
||||
const size_t num_extruders = print_object.print()->config().nozzle_diameter.size() + 1;
|
||||
for (size_t extruder_idx = 1; extruder_idx < num_extruders; ++extruder_idx) {
|
||||
throw_on_cancel_callback();
|
||||
const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, EnforcerBlockerType(extruder_idx));
|
||||
if (!mv->is_model_part() || custom_facets.indices.empty())
|
||||
continue;
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(1, num_extruders), [&mv, &print_object, &edge_grids, &painted_lines, &painted_lines_mutex, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t extruder_idx = range.begin(); extruder_idx < range.end(); ++extruder_idx) {
|
||||
throw_on_cancel_callback();
|
||||
const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, EnforcerBlockerType(extruder_idx));
|
||||
if (!mv->is_model_part() || custom_facets.indices.empty())
|
||||
continue;
|
||||
|
||||
const Transform3f tr = print_object.trafo().cast<float>() * mv->get_matrix().cast<float>();
|
||||
for (size_t facet_idx = 0; facet_idx < custom_facets.indices.size(); ++facet_idx) {
|
||||
float min_z = std::numeric_limits<float>::max();
|
||||
float max_z = std::numeric_limits<float>::lowest();
|
||||
const Transform3f tr = print_object.trafo().cast<float>() * mv->get_matrix().cast<float>();
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, custom_facets.indices.size()), [&tr, &custom_facets, &print_object, &edge_grids, &input_expolygons, &painted_lines, &painted_lines_mutex, &extruder_idx](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t facet_idx = range.begin(); facet_idx < range.end(); ++facet_idx) {
|
||||
float min_z = std::numeric_limits<float>::max();
|
||||
float max_z = std::numeric_limits<float>::lowest();
|
||||
|
||||
std::array<Vec3f, 3> facet;
|
||||
for (int p_idx = 0; p_idx < 3; ++p_idx) {
|
||||
facet[p_idx] = tr * custom_facets.vertices[custom_facets.indices[facet_idx](p_idx)];
|
||||
max_z = std::max(max_z, facet[p_idx].z());
|
||||
min_z = std::min(min_z, facet[p_idx].z());
|
||||
}
|
||||
std::array<Vec3f, 3> facet;
|
||||
for (int p_idx = 0; p_idx < 3; ++p_idx) {
|
||||
facet[p_idx] = tr * custom_facets.vertices[custom_facets.indices[facet_idx](p_idx)];
|
||||
max_z = std::max(max_z, facet[p_idx].z());
|
||||
min_z = std::min(min_z, facet[p_idx].z());
|
||||
}
|
||||
|
||||
// Sort the vertices by z-axis for simplification of projected_facet on slices
|
||||
std::sort(facet.begin(), facet.end(), [](const Vec3f &p1, const Vec3f &p2) { return p1.z() < p2.z(); });
|
||||
// Sort the vertices by z-axis for simplification of projected_facet on slices
|
||||
std::sort(facet.begin(), facet.end(), [](const Vec3f &p1, const Vec3f &p2) { return p1.z() < p2.z(); });
|
||||
|
||||
// Find lowest slice not below the triangle.
|
||||
auto first_layer = std::upper_bound(print_object.layers().begin(), print_object.layers().end(), float(min_z - EPSILON),
|
||||
[](float z, const Layer *l1) { return z < l1->slice_z; });
|
||||
auto last_layer = std::upper_bound(print_object.layers().begin(), print_object.layers().end(), float(max_z + EPSILON),
|
||||
[](float z, const Layer *l1) { return z < l1->slice_z; });
|
||||
--last_layer;
|
||||
// Find lowest slice not below the triangle.
|
||||
auto first_layer = std::upper_bound(print_object.layers().begin(), print_object.layers().end(), float(min_z - EPSILON),
|
||||
[](float z, const Layer *l1) { return z < l1->slice_z; });
|
||||
auto last_layer = std::upper_bound(print_object.layers().begin(), print_object.layers().end(), float(max_z + EPSILON),
|
||||
[](float z, const Layer *l1) { return z < l1->slice_z; });
|
||||
--last_layer;
|
||||
|
||||
for (auto layer_it = first_layer; layer_it != (last_layer + 1); ++layer_it) {
|
||||
const Layer *layer = *layer_it;
|
||||
size_t layer_idx = layer_it - print_object.layers().begin();
|
||||
if (facet[0].z() > layer->slice_z || layer->slice_z > facet[2].z())
|
||||
continue;
|
||||
for (auto layer_it = first_layer; layer_it != (last_layer + 1); ++layer_it) {
|
||||
const Layer *layer = *layer_it;
|
||||
size_t layer_idx = layer_it - print_object.layers().begin();
|
||||
if (input_expolygons[layer_idx].empty() || facet[0].z() > layer->slice_z || layer->slice_z > facet[2].z())
|
||||
continue;
|
||||
|
||||
// https://kandepet.com/3d-printing-slicing-3d-objects/
|
||||
float t = (float(layer->slice_z) - facet[0].z()) / (facet[2].z() - facet[0].z());
|
||||
Vec3f line_start_f = facet[0] + t * (facet[2] - facet[0]);
|
||||
Vec3f line_end_f;
|
||||
// https://kandepet.com/3d-printing-slicing-3d-objects/
|
||||
float t = (float(layer->slice_z) - facet[0].z()) / (facet[2].z() - facet[0].z());
|
||||
Vec3f line_start_f = facet[0] + t * (facet[2] - facet[0]);
|
||||
Vec3f line_end_f;
|
||||
|
||||
if (facet[1].z() > layer->slice_z) {
|
||||
// [P0, P2] a [P0, P1]
|
||||
float t1 = (float(layer->slice_z) - facet[0].z()) / (facet[1].z() - facet[0].z());
|
||||
line_end_f = facet[0] + t1 * (facet[1] - facet[0]);
|
||||
} else {
|
||||
// [P0, P2] a [P1, P2]
|
||||
float t2 = (float(layer->slice_z) - facet[1].z()) / (facet[2].z() - facet[1].z());
|
||||
line_end_f = facet[1] + t2 * (facet[2] - facet[1]);
|
||||
if (facet[1].z() > layer->slice_z) {
|
||||
// [P0, P2] and [P0, P1]
|
||||
float t1 = (float(layer->slice_z) - facet[0].z()) / (facet[1].z() - facet[0].z());
|
||||
line_end_f = facet[0] + t1 * (facet[1] - facet[0]);
|
||||
} else {
|
||||
// [P0, P2] and [P1, P2]
|
||||
float t2 = (float(layer->slice_z) - facet[1].z()) / (facet[2].z() - facet[1].z());
|
||||
line_end_f = facet[1] + t2 * (facet[2] - facet[1]);
|
||||
}
|
||||
|
||||
Point line_start(scale_(line_start_f.x()), scale_(line_start_f.y()));
|
||||
Point line_end(scale_(line_end_f.x()), scale_(line_end_f.y()));
|
||||
line_start -= print_object.center_offset();
|
||||
line_end -= print_object.center_offset();
|
||||
|
||||
size_t mutex_idx = layer_idx & 0x3F;
|
||||
assert(mutex_idx < painted_lines_mutex.size());
|
||||
|
||||
PaintedLineVisitor visitor(edge_grids[layer_idx], painted_lines[layer_idx], painted_lines_mutex[mutex_idx], 16);
|
||||
visitor.line_to_test.a = line_start;
|
||||
visitor.line_to_test.b = line_end;
|
||||
visitor.color = int(extruder_idx);
|
||||
edge_grids[layer_idx].visit_cells_intersecting_line(line_start, line_end, visitor);
|
||||
}
|
||||
}
|
||||
|
||||
Point line_start(scale_(line_start_f.x()), scale_(line_start_f.y()));
|
||||
Point line_end(scale_(line_end_f.x()), scale_(line_end_f.y()));
|
||||
line_start -= print_object.center_offset();
|
||||
line_end -= print_object.center_offset();
|
||||
|
||||
PaintedLineVisitor visitor(edge_grids[layer_idx], painted_lines[layer_idx], 16);
|
||||
visitor.reset();
|
||||
visitor.line_to_test.a = line_start;
|
||||
visitor.line_to_test.b = line_end;
|
||||
visitor.color = int(extruder_idx);
|
||||
edge_grids[layer_idx].visit_cells_intersecting_line(line_start, line_end, visitor);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - projection of painted triangles - end";
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - painted layers count: "
|
||||
|
@ -1725,8 +1851,8 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
|||
tbb::parallel_for(tbb::blocked_range<size_t>(0, print_object.layers().size()), [&](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||
throw_on_cancel_callback();
|
||||
auto comp = [&input_polygons, layer_idx](const PaintedLine &first, const PaintedLine &second) {
|
||||
Point first_start_p = input_polygons[layer_idx][first.contour_idx][first.line_idx];
|
||||
auto comp = [&edge_grids, layer_idx](const PaintedLine &first, const PaintedLine &second) {
|
||||
Point first_start_p = edge_grids[layer_idx].contours()[first.contour_idx].segment_start(first.line_idx);
|
||||
return first.contour_idx < second.contour_idx ||
|
||||
(first.contour_idx == second.contour_idx &&
|
||||
(first.line_idx < second.line_idx ||
|
||||
|
@ -1740,13 +1866,28 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
|||
std::vector<PaintedLine> &painted_lines_single = painted_lines[layer_idx];
|
||||
|
||||
if (!painted_lines_single.empty()) {
|
||||
std::vector<std::vector<ColoredLine>> color_poly = colorize_polygons(input_polygons[layer_idx], painted_lines_single);
|
||||
std::vector<std::vector<ColoredLine>> color_poly = colorize_polygons(edge_grids[layer_idx].contours(), painted_lines_single);
|
||||
MMU_Graph graph = build_graph(layer_idx, color_poly);
|
||||
remove_multiple_edges_in_vertices(graph, color_poly);
|
||||
graph.remove_nodes_with_one_arc();
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_GRAPH
|
||||
{
|
||||
static int iRun = 0;
|
||||
export_graph_to_svg(debug_out_path("mm-graph-final-%d-%d.svg", layer_idx, iRun++), graph, input_expolygons[layer_idx]);
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_GRAPH
|
||||
|
||||
std::vector<std::pair<Polygon, size_t>> segmentation = extract_colored_segments(graph);
|
||||
for (std::pair<Polygon, size_t> ®ion : segmentation)
|
||||
segmented_regions[layer_idx].emplace_back(std::move(region));
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
{
|
||||
static int iRun = 0;
|
||||
export_regions_to_svg(debug_out_path("mm-regions-sides-%d-%d.svg", layer_idx, iRun++), segmented_regions[layer_idx], input_expolygons[layer_idx]);
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
}
|
||||
}
|
||||
}); // end of parallel_for
|
||||
|
@ -1765,6 +1906,14 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
|||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> segmented_regions_merged = merge_segmented_layers(segmented_regions, std::move(top_and_bottom_layers), throw_on_cancel_callback);
|
||||
throw_on_cancel_callback();
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
{
|
||||
static int iRun = 0;
|
||||
for (size_t layer_idx = 0; layer_idx < print_object.layers().size(); ++layer_idx)
|
||||
export_regions_to_svg(debug_out_path("mm-regions-merged-%d-%d.svg", layer_idx, iRun++), segmented_regions_merged[layer_idx], input_expolygons[layer_idx]);
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
|
||||
return segmented_regions_merged;
|
||||
}
|
||||
|
||||
|
|
|
@ -625,17 +625,13 @@ 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),
|
||||
#if ENABLE_PROJECT_DIRTY_STATE
|
||||
m_saved_preset(type, "", false),
|
||||
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||
m_idx_selected(0)
|
||||
{
|
||||
// Insert just the default preset.
|
||||
this->add_default_preset(keys, defaults, default_name);
|
||||
m_edited_preset.config.apply(m_presets.front().config);
|
||||
#if ENABLE_PROJECT_DIRTY_STATE
|
||||
update_saved_preset_from_current_preset();
|
||||
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||
}
|
||||
|
||||
void PresetCollection::reset(bool delete_files)
|
||||
|
@ -816,10 +812,8 @@ std::pair<Preset*, bool> PresetCollection::load_external_preset(
|
|||
// The source config may contain keys from many possible preset types. Just copy those that relate to this preset.
|
||||
this->get_edited_preset().config.apply_only(combined_config, keys, true);
|
||||
this->update_dirty();
|
||||
#if ENABLE_PROJECT_DIRTY_STATE
|
||||
update_saved_preset_from_current_preset();
|
||||
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||
assert(this->get_edited_preset().is_dirty);
|
||||
assert(this->get_edited_preset().is_dirty);
|
||||
return std::make_pair(&(*it), this->get_edited_preset().is_dirty);
|
||||
}
|
||||
if (inherits.empty()) {
|
||||
|
@ -1229,9 +1223,7 @@ Preset& PresetCollection::select_preset(size_t idx)
|
|||
idx = first_visible_idx();
|
||||
m_idx_selected = idx;
|
||||
m_edited_preset = m_presets[idx];
|
||||
#if ENABLE_PROJECT_DIRTY_STATE
|
||||
update_saved_preset_from_current_preset();
|
||||
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||
bool default_visible = ! m_default_suppressed || m_idx_selected < m_num_default_presets;
|
||||
for (size_t i = 0; i < m_num_default_presets; ++i)
|
||||
m_presets[i].is_visible = default_visible;
|
||||
|
|
|
@ -370,10 +370,8 @@ public:
|
|||
Preset& get_edited_preset() { return m_edited_preset; }
|
||||
const Preset& get_edited_preset() const { return m_edited_preset; }
|
||||
|
||||
#if ENABLE_PROJECT_DIRTY_STATE
|
||||
// Return the last saved preset.
|
||||
const Preset& get_saved_preset() const { return m_saved_preset; }
|
||||
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||
|
||||
// Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist.
|
||||
PresetWithVendorProfile get_preset_with_vendor_profile(const Preset &preset) const;
|
||||
|
@ -394,15 +392,11 @@ public:
|
|||
// Return a preset by an index. If the preset is active, a temporary copy is returned.
|
||||
Preset& preset(size_t idx) { return (idx == m_idx_selected) ? m_edited_preset : m_presets[idx]; }
|
||||
const Preset& preset(size_t idx) const { return const_cast<PresetCollection*>(this)->preset(idx); }
|
||||
#if ENABLE_PROJECT_DIRTY_STATE
|
||||
void discard_current_changes() {
|
||||
m_presets[m_idx_selected].reset_dirty();
|
||||
m_edited_preset = m_presets[m_idx_selected];
|
||||
update_saved_preset_from_current_preset();
|
||||
}
|
||||
#else
|
||||
void discard_current_changes() { m_presets[m_idx_selected].reset_dirty(); m_edited_preset = m_presets[m_idx_selected]; }
|
||||
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||
|
||||
// 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.
|
||||
|
@ -477,7 +471,6 @@ public:
|
|||
std::vector<std::string> current_different_from_parent_options(const bool deep_compare = false) const
|
||||
{ return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); }
|
||||
|
||||
#if ENABLE_PROJECT_DIRTY_STATE
|
||||
// Compare the content of get_saved_preset() with get_edited_preset() configs, return true if they differ.
|
||||
bool saved_is_dirty() const { return !this->saved_dirty_options().empty(); }
|
||||
// Compare the content of get_saved_preset() with get_edited_preset() configs, return the list of keys where they differ.
|
||||
|
@ -485,7 +478,6 @@ public:
|
|||
{ return dirty_options(&this->get_edited_preset(), &this->get_saved_preset(), deep_compare); }
|
||||
// Copy edited preset into saved preset.
|
||||
void update_saved_preset_from_current_preset() { m_saved_preset = m_edited_preset; }
|
||||
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||
|
||||
// Return a sorted list of system preset names.
|
||||
// Used for validating the "inherits" flag when importing user's config bundles.
|
||||
|
@ -574,10 +566,8 @@ private:
|
|||
std::map<std::string, std::string> m_map_system_profile_renamed;
|
||||
// Initially this preset contains a copy of the selected preset. Later on, this copy may be modified by the user.
|
||||
Preset m_edited_preset;
|
||||
#if ENABLE_PROJECT_DIRTY_STATE
|
||||
// Contains a copy of the last saved selected preset.
|
||||
Preset m_saved_preset;
|
||||
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||
|
||||
// Selected preset.
|
||||
size_t m_idx_selected;
|
||||
|
|
|
@ -373,7 +373,6 @@ bool Print::sequential_print_horizontal_clearance_valid(const Print& print, Poly
|
|||
// FIXME: Arrangement has different parameters for offsetting (jtMiter, limit 2)
|
||||
// which causes that the warning will be showed after arrangement with the
|
||||
// appropriate object distance. Even if I set this to jtMiter the warning still shows up.
|
||||
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||
it_convex_hull = map_model_object_to_convex_hull.emplace_hint(it_convex_hull, model_object_id,
|
||||
offset(print_object->model_object()->convex_hull_2d(
|
||||
Geometry::assemble_transform({ 0.0, 0.0, model_instance0->get_offset().z() }, model_instance0->get_rotation(), model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
|
||||
|
@ -381,15 +380,6 @@ bool Print::sequential_print_horizontal_clearance_valid(const Print& print, Poly
|
|||
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
|
||||
float(scale_(0.5 * print.config().extruder_clearance_radius.value - EPSILON)),
|
||||
jtRound, scale_(0.1)).front());
|
||||
#else
|
||||
it_convex_hull = map_model_object_to_convex_hull.emplace_hint(it_convex_hull, model_object_id,
|
||||
offset(print_object->model_object()->convex_hull_2d(
|
||||
Geometry::assemble_transform(Vec3d::Zero(), model_instance0->get_rotation(), model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
|
||||
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
|
||||
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
|
||||
float(scale_(0.5 * print.config().extruder_clearance_radius.value - EPSILON)),
|
||||
jtRound, float(scale_(0.1))).front());
|
||||
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||
}
|
||||
// Make a copy, so it may be rotated for instances.
|
||||
Polygon convex_hull0 = it_convex_hull->second;
|
||||
|
|
|
@ -1279,7 +1279,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_labels.push_back("Teacup");
|
||||
def->enum_labels.push_back("MakerWare (MakerBot)");
|
||||
def->enum_labels.push_back("Marlin (legacy)");
|
||||
def->enum_labels.push_back("Marlin Firmware");
|
||||
def->enum_labels.push_back("Marlin 2");
|
||||
def->enum_labels.push_back("Sailfish (MakerBot)");
|
||||
def->enum_labels.push_back("Mach3/LinuxCNC");
|
||||
def->enum_labels.push_back("Machinekit");
|
||||
|
@ -1457,6 +1457,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->tooltip = L("Maximum width of a segmented region. Zero disables this feature.");
|
||||
def->sidetext = L("mm (zero to disable)");
|
||||
def->min = 0;
|
||||
def->category = L("Advanced");
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(0.f));
|
||||
|
||||
|
|
|
@ -1600,15 +1600,9 @@ PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &defau
|
|||
|
||||
void PrintObject::update_slicing_parameters()
|
||||
{
|
||||
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||
if (!m_slicing_params.valid)
|
||||
m_slicing_params = SlicingParameters::create_from_config(
|
||||
this->print()->config(), m_config, this->model_object()->bounding_box().max.z(), this->object_extruders());
|
||||
#else
|
||||
if (! m_slicing_params.valid)
|
||||
m_slicing_params = SlicingParameters::create_from_config(
|
||||
this->print()->config(), m_config, unscale<double>(this->height()), this->object_extruders());
|
||||
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||
}
|
||||
|
||||
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z)
|
||||
|
@ -1670,7 +1664,6 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
|
|||
updated = true;
|
||||
}
|
||||
|
||||
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||
// Verify the layer_height_profile.
|
||||
if (!layer_height_profile.empty() &&
|
||||
// Must not be of even length.
|
||||
|
@ -1678,15 +1671,6 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
|
|||
// Last entry must be at the top of the object.
|
||||
std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_max) > 1e-3))
|
||||
layer_height_profile.clear();
|
||||
#else
|
||||
// Verify the layer_height_profile.
|
||||
if (! layer_height_profile.empty() &&
|
||||
// Must not be of even length.
|
||||
((layer_height_profile.size() & 1) != 0 ||
|
||||
// Last entry must be at the top of the object.
|
||||
std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_height()) > 1e-3))
|
||||
layer_height_profile.clear();
|
||||
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||
|
||||
if (layer_height_profile.empty()) {
|
||||
//layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes);
|
||||
|
|
|
@ -112,7 +112,7 @@ InteriorPtr generate_interior(const TriangleMesh & mesh,
|
|||
const HollowingConfig &hc,
|
||||
const JobController & ctl)
|
||||
{
|
||||
static const double MIN_OVERSAMPL = 3.;
|
||||
static const double MIN_OVERSAMPL = 3.5;
|
||||
static const double MAX_OVERSAMPL = 8.;
|
||||
|
||||
// I can't figure out how to increase the grid resolution through openvdb
|
||||
|
|
|
@ -323,6 +323,7 @@ private:
|
|||
{
|
||||
support_tree_ptr = sla::SupportTree::create(*this, ctl);
|
||||
tree_mesh = TriangleMesh{support_tree_ptr->retrieve_mesh(sla::MeshType::Support)};
|
||||
tree_mesh.require_shared_vertices();
|
||||
return support_tree_ptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
#define ENABLE_SELECTION_DEBUG_OUTPUT 0
|
||||
// Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active
|
||||
#define ENABLE_RENDER_SELECTION_CENTER 0
|
||||
// Shows an imgui dialog with render related data
|
||||
#define ENABLE_RENDER_STATISTICS 0
|
||||
// Shows an imgui dialog with camera related data
|
||||
#define ENABLE_CAMERA_STATISTICS 0
|
||||
// Render the picking pass instead of the main scene (use [T] key to toggle between regular rendering and picking pass only rendering)
|
||||
|
@ -26,6 +24,8 @@
|
|||
#define ENABLE_GCODE_VIEWER_STATISTICS 0
|
||||
// Enable G-Code viewer comparison between toolpaths height and width detected from gcode and calculated at gcode generation
|
||||
#define ENABLE_GCODE_VIEWER_DATA_CHECKING 0
|
||||
// Enable project dirty state manager debug window
|
||||
#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW 0
|
||||
|
||||
|
||||
// Enable rendering of objects using environment map
|
||||
|
@ -41,33 +41,12 @@
|
|||
//====================
|
||||
#define ENABLE_2_4_0_ALPHA0 1
|
||||
|
||||
// Enable reload from disk command for 3mf files
|
||||
#define ENABLE_RELOAD_FROM_DISK_FOR_3MF (1 && ENABLE_2_4_0_ALPHA0)
|
||||
// Enable showing gcode line numbers in preview horizontal slider
|
||||
#define ENABLE_GCODE_LINES_ID_IN_H_SLIDER (1 && ENABLE_2_4_0_ALPHA0)
|
||||
// Enable validation of custom gcode against gcode processor reserved keywords
|
||||
#define ENABLE_VALIDATE_CUSTOM_GCODE (1 && ENABLE_2_4_0_ALPHA0)
|
||||
// Enable showing a imgui window containing gcode in preview
|
||||
#define ENABLE_GCODE_WINDOW (1 && ENABLE_2_4_0_ALPHA0)
|
||||
// Enable exporting lines M73 for remaining time to next printer stop to gcode
|
||||
#define ENABLE_EXTENDED_M73_LINES (1 && ENABLE_VALIDATE_CUSTOM_GCODE)
|
||||
// Enable a modified version of automatic downscale on load of objects too big
|
||||
#define ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG (1 && ENABLE_2_4_0_ALPHA0)
|
||||
// Enable scrollable legend in preview
|
||||
#define ENABLE_SCROLLABLE_LEGEND (1 && ENABLE_2_4_0_ALPHA0)
|
||||
// Enable visualization of start gcode as regular toolpaths
|
||||
#define ENABLE_START_GCODE_VISUALIZATION (1 && ENABLE_2_4_0_ALPHA0)
|
||||
// Enable visualization of seams in preview
|
||||
#define ENABLE_SEAMS_VISUALIZATION (1 && ENABLE_2_4_0_ALPHA0)
|
||||
// Enable project dirty state manager
|
||||
#define ENABLE_PROJECT_DIRTY_STATE (1 && ENABLE_2_4_0_ALPHA0)
|
||||
// Enable project dirty state manager debug window
|
||||
#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW (0 && ENABLE_PROJECT_DIRTY_STATE)
|
||||
// Enable to push object instances under the bed
|
||||
#define ENABLE_ALLOW_NEGATIVE_Z (1 && ENABLE_2_4_0_ALPHA0)
|
||||
#define DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA (1 && ENABLE_ALLOW_NEGATIVE_Z)
|
||||
// Enable delayed rendering of transparent volumes
|
||||
#define ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING (1 && ENABLE_2_4_0_ALPHA0)
|
||||
// Enable the fix of importing color print view from gcode files into GCodeViewer
|
||||
#define ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER (1 && ENABLE_2_4_0_ALPHA0)
|
||||
// Enable drawing contours, at cut level, for sinking volumes
|
||||
#define ENABLE_SINKING_CONTOURS (1 && ENABLE_2_4_0_ALPHA0)
|
||||
|
||||
|
||||
#endif // _prusaslicer_technologies_h_
|
||||
|
|
|
@ -698,27 +698,29 @@ struct EdgeToFace {
|
|||
bool operator<(const EdgeToFace &other) const { return vertex_low < other.vertex_low || (vertex_low == other.vertex_low && vertex_high < other.vertex_high); }
|
||||
};
|
||||
|
||||
template<typename ThrowOnCancelCallback>
|
||||
template<typename FaceFilter, typename ThrowOnCancelCallback>
|
||||
static std::vector<EdgeToFace> create_edge_map(
|
||||
const indexed_triangle_set &its, ThrowOnCancelCallback throw_on_cancel)
|
||||
const indexed_triangle_set &its, FaceFilter face_filter, ThrowOnCancelCallback throw_on_cancel)
|
||||
{
|
||||
std::vector<EdgeToFace> edges_map;
|
||||
edges_map.assign(its.indices.size() * 3, EdgeToFace());
|
||||
edges_map.reserve(its.indices.size() * 3);
|
||||
for (uint32_t facet_idx = 0; facet_idx < its.indices.size(); ++ facet_idx)
|
||||
for (int i = 0; i < 3; ++ i) {
|
||||
EdgeToFace &e2f = edges_map[facet_idx * 3 + i];
|
||||
e2f.vertex_low = its.indices[facet_idx][i];
|
||||
e2f.vertex_high = its.indices[facet_idx][(i + 1) % 3];
|
||||
e2f.face = facet_idx;
|
||||
// 1 based indexing, to be always strictly positive.
|
||||
e2f.face_edge = i + 1;
|
||||
if (e2f.vertex_low > e2f.vertex_high) {
|
||||
// Sort the vertices
|
||||
std::swap(e2f.vertex_low, e2f.vertex_high);
|
||||
// and make the face_edge negative to indicate a flipped edge.
|
||||
e2f.face_edge = - e2f.face_edge;
|
||||
if (face_filter(facet_idx))
|
||||
for (int i = 0; i < 3; ++ i) {
|
||||
edges_map.push_back({});
|
||||
EdgeToFace &e2f = edges_map.back();
|
||||
e2f.vertex_low = its.indices[facet_idx][i];
|
||||
e2f.vertex_high = its.indices[facet_idx][(i + 1) % 3];
|
||||
e2f.face = facet_idx;
|
||||
// 1 based indexing, to be always strictly positive.
|
||||
e2f.face_edge = i + 1;
|
||||
if (e2f.vertex_low > e2f.vertex_high) {
|
||||
// Sort the vertices
|
||||
std::swap(e2f.vertex_low, e2f.vertex_high);
|
||||
// and make the face_edge negative to indicate a flipped edge.
|
||||
e2f.face_edge = - e2f.face_edge;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw_on_cancel();
|
||||
std::sort(edges_map.begin(), edges_map.end());
|
||||
|
||||
|
@ -727,12 +729,12 @@ static std::vector<EdgeToFace> create_edge_map(
|
|||
|
||||
// Map from a face edge to a unique edge identifier or -1 if no neighbor exists.
|
||||
// Two neighbor faces share a unique edge identifier even if they are flipped.
|
||||
template<typename ThrowOnCancelCallback>
|
||||
static inline std::vector<Vec3i> its_face_edge_ids_impl(const indexed_triangle_set &its, ThrowOnCancelCallback throw_on_cancel)
|
||||
template<typename FaceFilter, typename ThrowOnCancelCallback>
|
||||
static inline std::vector<Vec3i> its_face_edge_ids_impl(const indexed_triangle_set &its, FaceFilter face_filter, ThrowOnCancelCallback throw_on_cancel)
|
||||
{
|
||||
std::vector<Vec3i> out(its.indices.size(), Vec3i(-1, -1, -1));
|
||||
|
||||
std::vector<EdgeToFace> edges_map = create_edge_map(its, throw_on_cancel);
|
||||
std::vector<EdgeToFace> edges_map = create_edge_map(its, face_filter, throw_on_cancel);
|
||||
|
||||
// Assign a unique common edge id to touching triangle edges.
|
||||
int num_edges = 0;
|
||||
|
@ -780,12 +782,17 @@ static inline std::vector<Vec3i> its_face_edge_ids_impl(const indexed_triangle_s
|
|||
|
||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its)
|
||||
{
|
||||
return its_face_edge_ids_impl(its, [](){});
|
||||
return its_face_edge_ids_impl(its, [](const uint32_t){ return true; }, [](){});
|
||||
}
|
||||
|
||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback)
|
||||
{
|
||||
return its_face_edge_ids_impl(its, throw_on_cancel_callback);
|
||||
return its_face_edge_ids_impl(its, [](const uint32_t){ return true; }, throw_on_cancel_callback);
|
||||
}
|
||||
|
||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, const std::vector<bool> &face_mask)
|
||||
{
|
||||
return its_face_edge_ids_impl(its, [&face_mask](const uint32_t idx){ return face_mask[idx]; }, [](){});
|
||||
}
|
||||
|
||||
// Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices.
|
||||
|
|
|
@ -118,6 +118,7 @@ private:
|
|||
// Used for chaining slice lines into polygons.
|
||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its);
|
||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback);
|
||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, const std::vector<bool> &face_mask);
|
||||
// Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices.
|
||||
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors, bool assign_unbound_edges = false, int *num_edges = nullptr);
|
||||
|
||||
|
|
|
@ -362,6 +362,35 @@ static inline std::vector<IntersectionLines> slice_make_lines(
|
|||
return lines;
|
||||
}
|
||||
|
||||
template<typename TransformVertex, typename FaceFilter>
|
||||
static inline IntersectionLines slice_make_lines(
|
||||
const std::vector<stl_vertex> &mesh_vertices,
|
||||
const TransformVertex &transform_vertex_fn,
|
||||
const std::vector<stl_triangle_vertex_indices> &mesh_faces,
|
||||
const std::vector<Vec3i> &face_edge_ids,
|
||||
const float plane_z,
|
||||
FaceFilter face_filter)
|
||||
{
|
||||
IntersectionLines lines;
|
||||
for (int face_idx = 0; face_idx < mesh_faces.size(); ++ face_idx)
|
||||
if (face_filter(face_idx)) {
|
||||
const Vec3i &indices = mesh_faces[face_idx];
|
||||
stl_vertex vertices[3] { transform_vertex_fn(mesh_vertices[indices(0)]), transform_vertex_fn(mesh_vertices[indices(1)]), transform_vertex_fn(mesh_vertices[indices(2)]) };
|
||||
// find facet extents
|
||||
const float min_z = fminf(vertices[0].z(), fminf(vertices[1].z(), vertices[2].z()));
|
||||
const float max_z = fmaxf(vertices[0].z(), fmaxf(vertices[1].z(), vertices[2].z()));
|
||||
assert(min_z <= plane_z && max_z >= plane_z);
|
||||
int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
|
||||
IntersectionLine il;
|
||||
// Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume.
|
||||
if (min_z != max_z && slice_facet(plane_z, vertices, indices, face_edge_ids[face_idx], idx_vertex_lowest, false, il) == FacetSliceType::Slicing) {
|
||||
assert(il.edge_type != IntersectionLine::FacetEdgeType::Horizontal);
|
||||
lines.emplace_back(il);
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
// For projecting triangle sets onto slice slabs.
|
||||
struct SlabLines {
|
||||
// Intersection lines of a slice with a triangle set, CCW oriented.
|
||||
|
@ -1720,6 +1749,69 @@ std::vector<Polygons> slice_mesh(
|
|||
return layers;
|
||||
}
|
||||
|
||||
// Specialized version for a single slicing plane only, running on a single thread.
|
||||
Polygons slice_mesh(
|
||||
const indexed_triangle_set &mesh,
|
||||
// Unscaled Zs
|
||||
const float plane_z,
|
||||
const MeshSlicingParams ¶ms)
|
||||
{
|
||||
std::vector<IntersectionLines> lines;
|
||||
|
||||
{
|
||||
bool trafo_identity = is_identity(params.trafo);
|
||||
Transform3f tf;
|
||||
std::vector<bool> face_mask(mesh.indices.size(), false);
|
||||
|
||||
{
|
||||
// 1) Mark vertices as below or above the slicing plane.
|
||||
std::vector<char> vertex_side(mesh.vertices.size(), 0);
|
||||
if (trafo_identity) {
|
||||
for (size_t i = 0; i < mesh.vertices.size(); ++ i) {
|
||||
float z = mesh.vertices[i].z();
|
||||
char s = z < plane_z ? -1 : z == plane_z ? 0 : 1;
|
||||
vertex_side[i] = s;
|
||||
}
|
||||
} else {
|
||||
tf = make_trafo_for_slicing(params.trafo);
|
||||
for (size_t i = 0; i < mesh.vertices.size(); ++ i) {
|
||||
//FIXME don't need to transform x & y, just Z.
|
||||
float z = (tf * mesh.vertices[i]).z();
|
||||
char s = z < plane_z ? -1 : z == plane_z ? 0 : 1;
|
||||
vertex_side[i] = s;
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Mark faces crossing the plane.
|
||||
for (size_t i = 0; i < mesh.indices.size(); ++ i) {
|
||||
const Vec3i &face = mesh.indices[i];
|
||||
int sides[3] = { vertex_side[face(0)], vertex_side[face(1)], vertex_side[face(2)] };
|
||||
face_mask[i] = sides[0] * sides[1] <= 0 || sides[1] * sides[2] <= 0 || sides[0] * sides[2] <= 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Calculate face neighbors for just the faces in face_mask.
|
||||
std::vector<Vec3i> face_edge_ids = its_face_edge_ids(mesh, face_mask);
|
||||
|
||||
// 4) Slice "face_mask" triangles, collect line segments.
|
||||
// It likely is not worthwile to copy the vertices. Apply the transformation in place.
|
||||
if (trafo_identity) {
|
||||
lines.emplace_back(slice_make_lines(
|
||||
mesh.vertices, [](const Vec3f &p) { return Vec3f(scaled<float>(p.x()), scaled<float>(p.y()), p.z()); },
|
||||
mesh.indices, face_edge_ids, plane_z, [&face_mask](int face_idx) { return face_mask[face_idx]; }));
|
||||
} else {
|
||||
// Transform the vertices, scale up in XY, not in Z.
|
||||
lines.emplace_back(slice_make_lines(mesh.vertices, [tf](const Vec3f& p) { return tf * p; }, mesh.indices, face_edge_ids, plane_z,
|
||||
[&face_mask](int face_idx) { return face_mask[face_idx]; }));
|
||||
}
|
||||
}
|
||||
|
||||
// 5) Chain the line segments.
|
||||
std::vector<Polygons> layers = make_loops(lines, params, [](){});
|
||||
assert(layers.size() == 1);
|
||||
return layers.front();
|
||||
}
|
||||
|
||||
std::vector<ExPolygons> slice_mesh_ex(
|
||||
const indexed_triangle_set &mesh,
|
||||
const std::vector<float> &zs,
|
||||
|
|
|
@ -52,6 +52,12 @@ std::vector<Polygons> slice_mesh(
|
|||
const MeshSlicingParams ¶ms,
|
||||
std::function<void()> throw_on_cancel = []{});
|
||||
|
||||
// Specialized version for a single slicing plane only, running on a single thread.
|
||||
Polygons slice_mesh(
|
||||
const indexed_triangle_set &mesh,
|
||||
const float plane_z,
|
||||
const MeshSlicingParams ¶ms);
|
||||
|
||||
std::vector<ExPolygons> slice_mesh_ex(
|
||||
const indexed_triangle_set &mesh,
|
||||
const std::vector<float> &zs,
|
||||
|
|
|
@ -228,7 +228,7 @@ void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_st
|
|||
}
|
||||
}
|
||||
|
||||
void TriangleSelector::precompute_all_level_neighbors_recursive(const int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector<Vec3i> &neighbors_out) const
|
||||
void TriangleSelector::precompute_all_neighbors_recursive(const int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector<Vec3i> &neighbors_out, std::vector<Vec3i> &neighbors_propagated_out) const
|
||||
{
|
||||
assert(facet_idx < int(m_triangles.size()));
|
||||
|
||||
|
@ -236,7 +236,8 @@ void TriangleSelector::precompute_all_level_neighbors_recursive(const int facet_
|
|||
if (!tr->valid())
|
||||
return;
|
||||
|
||||
neighbors_out[facet_idx] = neighbors_propagated;
|
||||
neighbors_out[facet_idx] = neighbors;
|
||||
neighbors_propagated_out[facet_idx] = neighbors_propagated;
|
||||
if (tr->is_split()) {
|
||||
assert(this->verify_triangle_neighbors(*tr, neighbors));
|
||||
|
||||
|
@ -247,67 +248,51 @@ void TriangleSelector::precompute_all_level_neighbors_recursive(const int facet_
|
|||
assert(tr->children[i] < int(m_triangles.size()));
|
||||
// Recursion, deep first search over the children of this triangle.
|
||||
// All children of this triangle were created by splitting a single source triangle of the original mesh.
|
||||
this->precompute_all_level_neighbors_recursive(tr->children[i], this->child_neighbors(*tr, neighbors, i), this->child_neighbors_propagated(*tr, neighbors_propagated, i), neighbors_out);
|
||||
this->precompute_all_neighbors_recursive(tr->children[i], this->child_neighbors(*tr, neighbors, i),
|
||||
this->child_neighbors_propagated(*tr, neighbors_propagated, i), neighbors_out,
|
||||
neighbors_propagated_out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Vec3i> TriangleSelector::precompute_all_level_neighbors() const
|
||||
std::pair<std::vector<Vec3i>, std::vector<Vec3i>> TriangleSelector::precompute_all_neighbors() const
|
||||
{
|
||||
std::vector<Vec3i> neighbors(m_triangles.size(), Vec3i(-1, -1, -1));
|
||||
std::vector<Vec3i> neighbors_propagated(m_triangles.size(), Vec3i(-1, -1, -1));
|
||||
for (int facet_idx = 0; facet_idx < this->m_orig_size_indices; ++facet_idx) {
|
||||
neighbors[facet_idx] = root_neighbors(*m_mesh, facet_idx);
|
||||
neighbors[facet_idx] = root_neighbors(*m_mesh, facet_idx);
|
||||
neighbors_propagated[facet_idx] = neighbors[facet_idx];
|
||||
assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors[facet_idx]));
|
||||
if (m_triangles[facet_idx].is_split())
|
||||
this->precompute_all_level_neighbors_recursive(facet_idx, neighbors[facet_idx], neighbors[facet_idx], neighbors);
|
||||
this->precompute_all_neighbors_recursive(facet_idx, neighbors[facet_idx], neighbors_propagated[facet_idx], neighbors, neighbors_propagated);
|
||||
}
|
||||
return neighbors;
|
||||
return std::make_pair(std::move(neighbors), std::move(neighbors_propagated));
|
||||
}
|
||||
|
||||
bool TriangleSelector::are_triangles_touching(const int first_facet_idx, const int second_facet_idx) const
|
||||
// It appends all triangles that are touching the edge (vertexi, vertexj) of the triangle.
|
||||
// It doesn't append the triangles that are touching the triangle only by part of the edge that means the triangles are from lower depth.
|
||||
void TriangleSelector::append_touching_subtriangles(int itriangle, int vertexi, int vertexj, std::vector<int> &touching_subtriangles_out) const
|
||||
{
|
||||
std::array<Linef3, 3> sides_facet = {Linef3(m_vertices[m_triangles[first_facet_idx].verts_idxs[0]].v.cast<double>(), m_vertices[m_triangles[first_facet_idx].verts_idxs[1]].v.cast<double>()),
|
||||
Linef3(m_vertices[m_triangles[first_facet_idx].verts_idxs[1]].v.cast<double>(), m_vertices[m_triangles[first_facet_idx].verts_idxs[2]].v.cast<double>()),
|
||||
Linef3(m_vertices[m_triangles[first_facet_idx].verts_idxs[2]].v.cast<double>(), m_vertices[m_triangles[first_facet_idx].verts_idxs[0]].v.cast<double>())};
|
||||
if (itriangle == -1)
|
||||
return;
|
||||
|
||||
const Vec3d p0 = m_vertices[m_triangles[second_facet_idx].verts_idxs[0]].v.cast<double>();
|
||||
const Vec3d p1 = m_vertices[m_triangles[second_facet_idx].verts_idxs[1]].v.cast<double>();
|
||||
const Vec3d p2 = m_vertices[m_triangles[second_facet_idx].verts_idxs[2]].v.cast<double>();
|
||||
auto process_subtriangle = [this, &itriangle, &vertexi, &vertexj, &touching_subtriangles_out](const int subtriangle_idx) -> void {
|
||||
assert(subtriangle_idx == -1);
|
||||
if (!m_triangles[subtriangle_idx].is_split())
|
||||
touching_subtriangles_out.emplace_back(subtriangle_idx);
|
||||
else if (int midpoint = this->triangle_midpoint(itriangle, vertexi, vertexj); midpoint != -1)
|
||||
append_touching_subtriangles(subtriangle_idx, vertexi, midpoint, touching_subtriangles_out);
|
||||
else
|
||||
append_touching_subtriangles(subtriangle_idx, vertexi, vertexj, touching_subtriangles_out);
|
||||
};
|
||||
|
||||
for (size_t idx = 0; idx < 3; ++idx)
|
||||
if (line_alg::distance_to_squared(sides_facet[idx], p0) <= EPSILON && (line_alg::distance_to_squared(sides_facet[idx], p1) <= EPSILON || line_alg::distance_to_squared(sides_facet[idx], p2) <= EPSILON))
|
||||
return true;
|
||||
else if (line_alg::distance_to_squared(sides_facet[idx], p1) <= EPSILON && line_alg::distance_to_squared(sides_facet[idx], p2) <= EPSILON)
|
||||
return true;
|
||||
std::pair<int, int> touching = this->triangle_subtriangles(itriangle, vertexi, vertexj);
|
||||
if (touching.first != -1)
|
||||
process_subtriangle(touching.first);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<int> TriangleSelector::neighboring_triangles(const int first_facet_idx, const int second_facet_idx, EnforcerBlockerType second_facet_state) const
|
||||
{
|
||||
assert(first_facet_idx < int(m_triangles.size()));
|
||||
|
||||
const Triangle *tr = &m_triangles[first_facet_idx];
|
||||
if (!tr->valid())
|
||||
return {};
|
||||
|
||||
if (!tr->is_split() && tr->get_state() == second_facet_state && (are_triangles_touching(second_facet_idx, first_facet_idx) || are_triangles_touching(first_facet_idx, second_facet_idx)))
|
||||
return {first_facet_idx};
|
||||
|
||||
std::vector<int> neighbor_facets_out;
|
||||
int num_of_children = tr->number_of_split_sides() + 1;
|
||||
if (num_of_children != 1) {
|
||||
for (int i = 0; i < num_of_children; ++i) {
|
||||
assert(i < int(tr->children.size()));
|
||||
assert(tr->children[i] < int(m_triangles.size()));
|
||||
|
||||
if (std::vector<int> neighbor_facets = neighboring_triangles(tr->children[i], second_facet_idx, second_facet_state); !neighbor_facets.empty())
|
||||
Slic3r::append(neighbor_facets_out, std::move(neighbor_facets));
|
||||
}
|
||||
}
|
||||
|
||||
return neighbor_facets_out;
|
||||
if (touching.second != -1)
|
||||
process_subtriangle(touching.second);
|
||||
}
|
||||
|
||||
void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_start, bool propagate)
|
||||
|
@ -326,7 +311,23 @@ void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_
|
|||
return;
|
||||
}
|
||||
|
||||
std::vector<Vec3i> all_level_neighbors = this->precompute_all_level_neighbors();
|
||||
auto get_all_touching_triangles = [this](int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated) -> std::vector<int> {
|
||||
assert(facet_idx != -1 && facet_idx < m_triangles.size());
|
||||
assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
|
||||
std::vector<int> touching_triangles;
|
||||
Vec3i vertices = {m_triangles[facet_idx].verts_idxs[0], m_triangles[facet_idx].verts_idxs[1], m_triangles[facet_idx].verts_idxs[2]};
|
||||
append_touching_subtriangles(neighbors(0), vertices(1), vertices(0), touching_triangles);
|
||||
append_touching_subtriangles(neighbors(1), vertices(2), vertices(1), touching_triangles);
|
||||
append_touching_subtriangles(neighbors(2), vertices(0), vertices(2), touching_triangles);
|
||||
|
||||
for (int neighbor_idx : neighbors_propagated)
|
||||
if (neighbor_idx != -1 && !m_triangles[neighbor_idx].is_split())
|
||||
touching_triangles.emplace_back(neighbor_idx);
|
||||
|
||||
return touching_triangles;
|
||||
};
|
||||
|
||||
auto [neighbors, neighbors_propagated] = this->precompute_all_neighbors();
|
||||
std::vector<bool> visited(m_triangles.size(), false);
|
||||
std::queue<int> facet_queue;
|
||||
|
||||
|
@ -338,17 +339,14 @@ void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_
|
|||
|
||||
if (!visited[current_facet]) {
|
||||
m_triangles[current_facet].select_by_seed_fill();
|
||||
for (int neighbor_idx : all_level_neighbors[current_facet]) {
|
||||
if (neighbor_idx < 0 || visited[neighbor_idx])
|
||||
|
||||
std::vector<int> touching_triangles = get_all_touching_triangles(current_facet, neighbors[current_facet], neighbors_propagated[current_facet]);
|
||||
for(const int tr_idx : touching_triangles) {
|
||||
if (tr_idx < 0 || visited[tr_idx] || m_triangles[tr_idx].get_state() != start_facet_state)
|
||||
continue;
|
||||
|
||||
if (!m_triangles[neighbor_idx].is_split()) {
|
||||
if (m_triangles[neighbor_idx].get_state() == start_facet_state)
|
||||
facet_queue.push(neighbor_idx);
|
||||
} else {
|
||||
for (int neighbor_facet_idx : neighboring_triangles(neighbor_idx, current_facet, start_facet_state))
|
||||
facet_queue.push(neighbor_facet_idx);
|
||||
}
|
||||
assert(!m_triangles[tr_idx].is_split());
|
||||
facet_queue.push(tr_idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -437,6 +435,40 @@ int TriangleSelector::neighbor_child(int itriangle, int vertexi, int vertexj, Pa
|
|||
return itriangle == -1 ? -1 : this->neighbor_child(m_triangles[itriangle], vertexi, vertexj, partition);
|
||||
}
|
||||
|
||||
std::pair<int, int> TriangleSelector::triangle_subtriangles(int itriangle, int vertexi, int vertexj) const
|
||||
{
|
||||
return itriangle == -1 ? std::make_pair(-1, -1) : this->triangle_subtriangles(m_triangles[itriangle], vertexi, vertexj);
|
||||
}
|
||||
|
||||
std::pair<int, int> TriangleSelector::triangle_subtriangles(const Triangle &tr, int vertexi, int vertexj)
|
||||
{
|
||||
if (tr.number_of_split_sides() == 0)
|
||||
// If this triangle is not split, then there is no subtriangles touching the edge.
|
||||
return std::make_pair(-1, -1);
|
||||
|
||||
// Find the triangle edge.
|
||||
int edge = tr.verts_idxs[0] == vertexi ? 0 : tr.verts_idxs[1] == vertexi ? 1 : 2;
|
||||
assert(tr.verts_idxs[edge] == vertexi);
|
||||
assert(tr.verts_idxs[next_idx_modulo(edge, 3)] == vertexj);
|
||||
|
||||
if (tr.number_of_split_sides() == 1) {
|
||||
return edge == next_idx_modulo(tr.special_side(), 3) ? std::make_pair(tr.children[0], tr.children[1]) :
|
||||
std::make_pair(tr.children[edge == tr.special_side() ? 0 : 1], -1);
|
||||
} else if (tr.number_of_split_sides() == 2) {
|
||||
return edge == next_idx_modulo(tr.special_side(), 3) ? std::make_pair(tr.children[2], -1) :
|
||||
edge == tr.special_side() ? std::make_pair(tr.children[0], tr.children[1]) :
|
||||
std::make_pair(tr.children[2], tr.children[0]);
|
||||
} else {
|
||||
assert(tr.number_of_split_sides() == 3);
|
||||
assert(tr.special_side() == 0);
|
||||
return edge == 0 ? std::make_pair(tr.children[0], tr.children[1]) :
|
||||
edge == 1 ? std::make_pair(tr.children[1], tr.children[2]) :
|
||||
std::make_pair(tr.children[2], tr.children[0]);
|
||||
}
|
||||
|
||||
return std::make_pair(-1, -1);
|
||||
}
|
||||
|
||||
// Return existing midpoint of CCW oriented side (vertexi, vertexj).
|
||||
// If itriangle == -1 or if the side sharing (vertexi, vertexj) is not split, return -1.
|
||||
int TriangleSelector::triangle_midpoint(const Triangle &tr, int vertexi, int vertexj) const
|
||||
|
@ -524,12 +556,8 @@ Vec3i TriangleSelector::child_neighbors(const Triangle &tr, const Vec3i &neighbo
|
|||
|
||||
assert(child_idx >= 0 && child_idx <= tr.number_of_split_sides());
|
||||
int i = tr.special_side();
|
||||
int j = i + 1;
|
||||
if (j >= 3)
|
||||
j = 0;
|
||||
int k = j + 1;
|
||||
if (k >= 3)
|
||||
k = 0;
|
||||
int j = next_idx_modulo(i, 3);
|
||||
int k = next_idx_modulo(j, 3);
|
||||
|
||||
Vec3i out;
|
||||
switch (tr.number_of_split_sides()) {
|
||||
|
@ -612,23 +640,28 @@ Vec3i TriangleSelector::child_neighbors(const Triangle &tr, const Vec3i &neighbo
|
|||
Vec3i TriangleSelector::child_neighbors_propagated(const Triangle &tr, const Vec3i &neighbors, int child_idx) const
|
||||
{
|
||||
int i = tr.special_side();
|
||||
int j = i + 1;
|
||||
if (j >= 3) j = 0;
|
||||
int k = j + 1;
|
||||
if (k >= 3) k = 0;
|
||||
int j = next_idx_modulo(i, 3);
|
||||
int k = next_idx_modulo(j, 3);
|
||||
|
||||
Vec3i out;
|
||||
auto replace_if_not_exists = [&out](int index_to_replace, int neighbor) {
|
||||
if (out(index_to_replace) == -1)
|
||||
out(index_to_replace) = neighbor;
|
||||
};
|
||||
|
||||
switch (tr.number_of_split_sides()) {
|
||||
case 1:
|
||||
switch (child_idx) {
|
||||
case 0:
|
||||
out(0) = neighbors(i);
|
||||
out(1) = neighbors(j);
|
||||
out(1) = this->neighbor_child(neighbors(j), tr.verts_idxs[k], tr.verts_idxs[j], Partition::Second);
|
||||
replace_if_not_exists(1, neighbors(j));
|
||||
out(2) = tr.children[1];
|
||||
break;
|
||||
default:
|
||||
assert(child_idx == 1);
|
||||
out(0) = neighbors(j);
|
||||
out(0) = this->neighbor_child(neighbors(j), tr.verts_idxs[k], tr.verts_idxs[j], Partition::First);
|
||||
replace_if_not_exists(0, neighbors(j));
|
||||
out(1) = neighbors(k);
|
||||
out(2) = tr.children[0];
|
||||
break;
|
||||
|
@ -638,20 +671,24 @@ Vec3i TriangleSelector::child_neighbors_propagated(const Triangle &tr, const Vec
|
|||
case 2:
|
||||
switch (child_idx) {
|
||||
case 0:
|
||||
out(0) = neighbors(i);
|
||||
out(0) = this->neighbor_child(neighbors(i), tr.verts_idxs[j], tr.verts_idxs[i], Partition::Second);
|
||||
replace_if_not_exists(0, neighbors(i));
|
||||
out(1) = tr.children[1];
|
||||
out(2) = neighbors(k);
|
||||
out(2) = this->neighbor_child(neighbors(k), tr.verts_idxs[i], tr.verts_idxs[k], Partition::First);
|
||||
replace_if_not_exists(2, neighbors(k));
|
||||
break;
|
||||
case 1:
|
||||
assert(child_idx == 1);
|
||||
out(0) = neighbors(i);
|
||||
out(0) = this->neighbor_child(neighbors(i), tr.verts_idxs[j], tr.verts_idxs[i], Partition::First);
|
||||
replace_if_not_exists(0, neighbors(i));
|
||||
out(1) = tr.children[2];
|
||||
out(2) = tr.children[0];
|
||||
break;
|
||||
default:
|
||||
assert(child_idx == 2);
|
||||
out(0) = neighbors(j);
|
||||
out(1) = neighbors(k);
|
||||
out(1) = this->neighbor_child(neighbors(k), tr.verts_idxs[i], tr.verts_idxs[k], Partition::Second);
|
||||
replace_if_not_exists(1, neighbors(k));
|
||||
out(2) = tr.children[1];
|
||||
break;
|
||||
}
|
||||
|
@ -661,18 +698,24 @@ Vec3i TriangleSelector::child_neighbors_propagated(const Triangle &tr, const Vec
|
|||
assert(tr.special_side() == 0);
|
||||
switch (child_idx) {
|
||||
case 0:
|
||||
out(0) = neighbors(0);
|
||||
out(0) = this->neighbor_child(neighbors(0), tr.verts_idxs[1], tr.verts_idxs[0], Partition::Second);
|
||||
replace_if_not_exists(0, neighbors(0));
|
||||
out(1) = tr.children[3];
|
||||
out(2) = neighbors(2);
|
||||
out(2) = this->neighbor_child(neighbors(2), tr.verts_idxs[0], tr.verts_idxs[2], Partition::First);
|
||||
replace_if_not_exists(2, neighbors(2));
|
||||
break;
|
||||
case 1:
|
||||
out(0) = neighbors(0);
|
||||
out(1) = neighbors(1);
|
||||
out(0) = this->neighbor_child(neighbors(0), tr.verts_idxs[1], tr.verts_idxs[0], Partition::First);
|
||||
replace_if_not_exists(0, neighbors(0));
|
||||
out(1) = this->neighbor_child(neighbors(1), tr.verts_idxs[2], tr.verts_idxs[1], Partition::Second);
|
||||
replace_if_not_exists(1, neighbors(1));
|
||||
out(2) = tr.children[3];
|
||||
break;
|
||||
case 2:
|
||||
out(0) = neighbors(1);
|
||||
out(1) = neighbors(2);
|
||||
out(0) = this->neighbor_child(neighbors(1), tr.verts_idxs[2], tr.verts_idxs[1], Partition::First);
|
||||
replace_if_not_exists(0, neighbors(1));
|
||||
out(1) = this->neighbor_child(neighbors(2), tr.verts_idxs[0], tr.verts_idxs[2], Partition::Second);
|
||||
replace_if_not_exists(1, neighbors(2));
|
||||
out(2) = tr.children[3];
|
||||
break;
|
||||
default:
|
||||
|
@ -886,13 +929,13 @@ void TriangleSelector::undivide_triangle(int facet_idx)
|
|||
Triangle& tr = m_triangles[facet_idx];
|
||||
|
||||
if (tr.is_split()) {
|
||||
for (int i=0; i<=tr.number_of_split_sides(); ++i) {
|
||||
for (int i = 0; i <= tr.number_of_split_sides(); ++i) {
|
||||
int child = tr.children[i];
|
||||
Triangle &child_tr = m_triangles[child];
|
||||
assert(child_tr.valid());
|
||||
undivide_triangle(child);
|
||||
for (int i = 0; i < 3; ++ i) {
|
||||
int iv = child_tr.verts_idxs[i];
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
int iv = child_tr.verts_idxs[j];
|
||||
Vertex &v = m_vertices[iv];
|
||||
assert(v.ref_cnt > 0);
|
||||
if (-- v.ref_cnt == 0) {
|
||||
|
@ -1231,7 +1274,7 @@ void TriangleSelector::get_facets_strict_recursive(
|
|||
this->get_facets_split_by_tjoints({tr.verts_idxs[0], tr.verts_idxs[1], tr.verts_idxs[2]}, neighbors, out_triangles);
|
||||
}
|
||||
|
||||
void TriangleSelector::get_facets_split_by_tjoints(const Vec3i vertices, const Vec3i neighbors, std::vector<stl_triangle_vertex_indices> &out_triangles) const
|
||||
void TriangleSelector::get_facets_split_by_tjoints(const Vec3i &vertices, const Vec3i &neighbors, std::vector<stl_triangle_vertex_indices> &out_triangles) const
|
||||
{
|
||||
// Export this triangle, but first collect the T-joint vertices along its edges.
|
||||
Vec3i midpoints(
|
||||
|
@ -1393,9 +1436,10 @@ std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> TriangleSelector:
|
|||
return out.data;
|
||||
}
|
||||
|
||||
void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &data)
|
||||
void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &data, bool needs_reset)
|
||||
{
|
||||
reset(); // dump any current state
|
||||
if (needs_reset)
|
||||
reset(); // dump any current state
|
||||
|
||||
// Reserve number of triangles as if each triangle was saved with 4 bits.
|
||||
// With MMU painting this estimate may be somehow low, but better than nothing.
|
||||
|
|
|
@ -22,8 +22,8 @@ public:
|
|||
POINTER
|
||||
};
|
||||
|
||||
[[nodiscard]] std::vector<Vec3i> precompute_all_level_neighbors() const;
|
||||
void precompute_all_level_neighbors_recursive(const int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector<Vec3i> &neighbors_out) const;
|
||||
std::pair<std::vector<Vec3i>, std::vector<Vec3i>> precompute_all_neighbors() const;
|
||||
void precompute_all_neighbors_recursive(int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector<Vec3i> &neighbors_out, std::vector<Vec3i> &neighbors_normal_out) const;
|
||||
|
||||
// Set a limit to the edge length, below which the edge will not be split by select_patch().
|
||||
// Called by select_patch() internally. Made public for debugging purposes, see TriangleSelectorGUI::render_debug().
|
||||
|
@ -37,10 +37,6 @@ public:
|
|||
[[nodiscard]] int select_unsplit_triangle(const Vec3f &hit, int facet_idx) const;
|
||||
[[nodiscard]] int select_unsplit_triangle(const Vec3f &hit, int facet_idx, const Vec3i &neighbors) const;
|
||||
|
||||
[[nodiscard]] bool are_triangles_touching(int first_facet_idx, int second_facet_idx) const;
|
||||
|
||||
[[nodiscard]] std::vector<int> neighboring_triangles(int first_facet_idx, int second_facet_idx, EnforcerBlockerType second_facet_state) const;
|
||||
|
||||
// Select all triangles fully inside the circle, subdivide where needed.
|
||||
void select_patch(const Vec3f &hit, // point where to start
|
||||
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
|
||||
|
@ -60,7 +56,7 @@ public:
|
|||
bool propagate); // if bucket fill is propagated to neighbor faces or if it fills the only facet of the modified mesh that the hit point belongs to.
|
||||
|
||||
bool has_facets(EnforcerBlockerType state) const;
|
||||
static bool has_facets(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &data, const EnforcerBlockerType test_state);
|
||||
static bool has_facets(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &data, EnforcerBlockerType test_state);
|
||||
int num_facets(EnforcerBlockerType state) const;
|
||||
// Get facets at a given state. Don't triangulate T-joints.
|
||||
indexed_triangle_set get_facets(EnforcerBlockerType state) const;
|
||||
|
@ -81,7 +77,7 @@ public:
|
|||
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> serialize() const;
|
||||
|
||||
// Load serialized data. Assumes that correct mesh is loaded.
|
||||
void deserialize(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &data);
|
||||
void deserialize(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &data, bool needs_reset = true);
|
||||
|
||||
// For all triangles, remove the flag indicating that the triangle was selected by seed fill.
|
||||
void seed_fill_unselect_all_triangles();
|
||||
|
@ -128,11 +124,11 @@ protected:
|
|||
bool is_selected_by_seed_fill() const { assert(! is_split()); return m_selected_by_seed_fill; }
|
||||
|
||||
// Is this triangle valid or marked to be removed?
|
||||
bool valid() const throw() { return m_valid; }
|
||||
bool valid() const noexcept { return m_valid; }
|
||||
// Get info on how it's split.
|
||||
bool is_split() const throw() { return number_of_split_sides() != 0; }
|
||||
int number_of_split_sides() const throw() { return number_of_splits; }
|
||||
int special_side() const throw() { assert(is_split()); return special_side_idx; }
|
||||
bool is_split() const noexcept { return number_of_split_sides() != 0; }
|
||||
int number_of_split_sides() const noexcept { return number_of_splits; }
|
||||
int special_side() const noexcept { assert(is_split()); return special_side_idx; }
|
||||
|
||||
private:
|
||||
friend TriangleSelector;
|
||||
|
@ -205,7 +201,7 @@ private:
|
|||
void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant.
|
||||
bool is_pointer_in_triangle(int facet_idx) const;
|
||||
bool is_edge_inside_cursor(int facet_idx) const;
|
||||
int push_triangle(int a, int b, int c, int source_triangle, const EnforcerBlockerType state = EnforcerBlockerType{0});
|
||||
int push_triangle(int a, int b, int c, int source_triangle, EnforcerBlockerType state = EnforcerBlockerType{0});
|
||||
void perform_split(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType old_state);
|
||||
Vec3i child_neighbors(const Triangle &tr, const Vec3i &neighbors, int child_idx) const;
|
||||
Vec3i child_neighbors_propagated(const Triangle &tr, const Vec3i &neighbors, int child_idx) const;
|
||||
|
@ -221,6 +217,11 @@ private:
|
|||
int triangle_midpoint(int itriangle, int vertexi, int vertexj) const;
|
||||
int triangle_midpoint_or_allocate(int itriangle, int vertexi, int vertexj);
|
||||
|
||||
static std::pair<int, int> triangle_subtriangles(const Triangle &tr, int vertexi, int vertexj);
|
||||
std::pair<int, int> triangle_subtriangles(int itriangle, int vertexi, int vertexj) const;
|
||||
|
||||
void append_touching_subtriangles(int itriangle, int vertexi, int vertexj, std::vector<int> &touching_subtriangles_out) const;
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool verify_triangle_neighbors(const Triangle& tr, const Vec3i& neighbors) const;
|
||||
bool verify_triangle_midpoints(const Triangle& tr) const;
|
||||
|
@ -231,7 +232,7 @@ private:
|
|||
const Vec3i &neighbors,
|
||||
EnforcerBlockerType state,
|
||||
std::vector<stl_triangle_vertex_indices> &out_triangles) const;
|
||||
void get_facets_split_by_tjoints(const Vec3i vertices, const Vec3i neighbors, std::vector<stl_triangle_vertex_indices> &out_triangles) const;
|
||||
void get_facets_split_by_tjoints(const Vec3i &vertices, const Vec3i &neighbors, std::vector<stl_triangle_vertex_indices> &out_triangles) const;
|
||||
|
||||
int m_free_triangles_head { -1 };
|
||||
int m_free_vertices_head { -1 };
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
using SignalT = decltype (SIGSEGV);
|
||||
|
||||
template<class TryFn, class CatchFn, int N>
|
||||
void try_catch_signal(const SignalT (&/*sigs*/)[N], TryFn &&/*fn*/, CatchFn &&/*cfn*/)
|
||||
void try_catch_signal(const SignalT (&/*sigs*/)[N], TryFn &&fn, CatchFn &&/*cfn*/)
|
||||
{
|
||||
// TODO
|
||||
fn();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -50,6 +50,9 @@ void set_sys_shapes_dir(const std::string &path);
|
|||
// Return a full path to the system shapes gallery directory.
|
||||
const std::string& sys_shapes_dir();
|
||||
|
||||
// Return a full path to the custom shapes gallery directory.
|
||||
std::string custom_shapes_dir();
|
||||
|
||||
// Set a path with preset files.
|
||||
void set_data_dir(const std::string &path);
|
||||
// Return a full path to the GUI resource files.
|
||||
|
@ -99,6 +102,7 @@ extern bool is_gcode_file(const std::string &path);
|
|||
extern bool is_img_file(const std::string& path);
|
||||
extern bool is_stl_file(const boost::filesystem::directory_entry& path);
|
||||
extern bool is_stl_file(const std::string& path);
|
||||
extern bool is_shapes_dir(const std::string& dir);
|
||||
|
||||
// File path / name / extension splitting utilities, working with UTF-8,
|
||||
// to be published to Perl.
|
||||
|
|
|
@ -202,6 +202,11 @@ const std::string& data_dir()
|
|||
return g_data_dir;
|
||||
}
|
||||
|
||||
std::string custom_shapes_dir()
|
||||
{
|
||||
return (boost::filesystem::path(g_data_dir) / "shapes").string();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// The following helpers are borrowed from the LLVM project https://github.com/llvm
|
||||
namespace WindowsSupport
|
||||
|
@ -771,6 +776,11 @@ bool is_stl_file(const std::string &path)
|
|||
return boost::iends_with(path, ".stl");
|
||||
}
|
||||
|
||||
bool is_shapes_dir(const std::string& dir)
|
||||
{
|
||||
return dir == sys_shapes_dir() || dir == custom_shapes_dir();
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#ifdef WIN32
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue