From 645c840e5f231cdf857d7805e566721dfc8c4827 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 15 Mar 2021 13:07:10 +0100 Subject: [PATCH 01/32] Enabled print host upload notifications --- src/slic3r/GUI/NotificationManager.cpp | 2 +- src/slic3r/GUI/PrintHostDialogs.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 1dfd02c2f..4c0bf3734 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1140,7 +1140,7 @@ void NotificationManager::set_upload_job_notification_percentage(int id, const s std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host); // bool found = false; for (std::unique_ptr& notification : m_pop_notifications) { - if (notification->get_type() == NotificationType::ProgressBar && notification->compare_text(text)) { + if (notification->get_type() == NotificationType::PrintHostUpload && notification->compare_text(text)) { dynamic_cast(notification.get())->set_percentage(percentage); wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); // found = true; diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index 598b72b94..f153d603a 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -282,7 +282,7 @@ void PrintHostQueueDialog::append_job(const PrintHostJob &job) // Both strings are UTF-8 encoded. upload_names.emplace_back(job.printhost->get_host(), job.upload_data.upload_path.string()); - //wxGetApp().notification_manager()->push_upload_job_notification(this, job_list->GetItemCount(), 0, job.upload_data.upload_path.string(), job.printhost->get_host()); + wxGetApp().notification_manager()->push_upload_job_notification(this, job_list->GetItemCount(), (float)size_i / 1024 / 1024, job.upload_data.upload_path.string(), job.printhost->get_host()); } void PrintHostQueueDialog::on_dpi_changed(const wxRect &suggested_rect) From 888f3317784e49cb01a49228cecfdbaa2a49a92b Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 15 Mar 2021 14:01:21 +0100 Subject: [PATCH 02/32] Deleted debug print --- src/slic3r/GUI/PrintHostDialogs.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index f153d603a..ff82883ff 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -409,7 +409,6 @@ void PrintHostQueueDialog::get_active_jobs(std::vectorGetSize().x / em << " " << this->GetSize().y / em << " " << this->GetPosition().x << " " << this->GetPosition().y; auto *app_config = wxGetApp().app_config; if (udt & UserDataType::UDT_SIZE) { From 84a333e4eda49fe8d3c15aeda1c44ccd75933b49 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 15 Mar 2021 16:03:51 +0100 Subject: [PATCH 03/32] Fixed progress percentage passed to notification --- src/slic3r/GUI/PrintHostDialogs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index ff82883ff..d99584c1e 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -354,7 +354,7 @@ void PrintHostQueueDialog::on_progress(Event &evt) wxVariant nm, hst; job_list->GetValue(nm, evt.job_id, COL_FILENAME); job_list->GetValue(hst, evt.job_id, COL_HOST); - wxGetApp().notification_manager()->set_upload_job_notification_percentage(evt.job_id + 1, boost::nowide::narrow(nm.GetString()), boost::nowide::narrow(hst.GetString()), 100 / evt.progress); + wxGetApp().notification_manager()->set_upload_job_notification_percentage(evt.job_id + 1, boost::nowide::narrow(nm.GetString()), boost::nowide::narrow(hst.GetString()), evt.progress / 100.f); } } From 01406fd52182d72cf1789174dd64ee4284e9f2cc Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 15 Mar 2021 16:19:22 +0100 Subject: [PATCH 04/32] Worked around some quirky Linux file system issues. Namely the Chromebooks share their file system to Linux using the 9p file system, which does not support setting file ownership. Newly PrusaSlicer will detect platform and it will not panick if copy_file() cannot set file ownership after copying. It just logs the incident, and on chromebooks the loglevel for that incident is "Info", not "Error". Adjusted the full screen mode to contain menu bar. Moved Platform.cpp/hpp to libslic3r --- src/PrusaSlicer.cpp | 3 + src/libslic3r/CMakeLists.txt | 2 + src/{slic3r/Utils => libslic3r}/Platform.cpp | 8 - src/{slic3r/Utils => libslic3r}/Platform.hpp | 9 +- src/libslic3r/utils.cpp | 148 ++++++++++++++++++- src/slic3r/CMakeLists.txt | 2 - src/slic3r/GUI/GUI_Init.cpp | 3 - src/slic3r/GUI/MainFrame.cpp | 6 +- src/slic3r/GUI/OpenGLManager.cpp | 5 +- src/slic3r/GUI/Plater.cpp | 5 +- src/slic3r/GUI/PresetHints.cpp | 1 - src/slic3r/GUI/RemovableDriveManager.cpp | 6 +- 12 files changed, 168 insertions(+), 30 deletions(-) rename src/{slic3r/Utils => libslic3r}/Platform.cpp (92%) rename src/{slic3r/Utils => libslic3r}/Platform.hpp (80%) diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index ed2d70272..10da1cb07 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -38,6 +38,7 @@ #include "libslic3r/GCode/PostProcessor.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/ModelArrange.hpp" +#include "libslic3r/Platform.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/SLAPrint.hpp" #include "libslic3r/TriangleMesh.hpp" @@ -68,6 +69,8 @@ int CLI::run(int argc, char **argv) { // Mark the main thread for the debugger and for runtime checks. set_current_thread_name("slic3r_main"); + // Detect the operating system flavor. + detect_platform(); #ifdef __WXGTK__ // On Linux, wxGTK has no support for Wayland, and the app crashes on diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 6159b2c5c..4a762f7e1 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -141,6 +141,8 @@ add_library(libslic3r STATIC PerimeterGenerator.hpp PlaceholderParser.cpp PlaceholderParser.hpp + Platform.cpp + Platform.hpp Point.cpp Point.hpp Polygon.cpp diff --git a/src/slic3r/Utils/Platform.cpp b/src/libslic3r/Platform.cpp similarity index 92% rename from src/slic3r/Utils/Platform.cpp rename to src/libslic3r/Platform.cpp index 3d101d662..4c8805149 100644 --- a/src/slic3r/Utils/Platform.cpp +++ b/src/libslic3r/Platform.cpp @@ -1,15 +1,8 @@ #include "Platform.hpp" - -// For starting another PrusaSlicer instance on OSX. -// Fails to compile on Windows on the build server. - -#include - #include namespace Slic3r { -namespace GUI { static auto s_platform = Platform::Uninitialized; static auto s_platform_flavor = PlatformFlavor::Uninitialized; @@ -74,5 +67,4 @@ PlatformFlavor platform_flavor() return s_platform_flavor; } -} // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/Utils/Platform.hpp b/src/libslic3r/Platform.hpp similarity index 80% rename from src/slic3r/Utils/Platform.hpp rename to src/libslic3r/Platform.hpp index c0ee3541d..735728e89 100644 --- a/src/slic3r/Utils/Platform.hpp +++ b/src/libslic3r/Platform.hpp @@ -1,8 +1,7 @@ -#ifndef SLIC3R_GUI_Utils_Platform_HPP -#define SLIC3R_GUI_Utils_Platform_HPP +#ifndef SLIC3R_Platform_HPP +#define SLIC3R_Platform_HPP namespace Slic3r { -namespace GUI { enum class Platform { @@ -37,8 +36,6 @@ void detect_platform(); Platform platform(); PlatformFlavor platform_flavor(); - -} // namespace GUI } // namespace Slic3r -#endif // SLIC3R_GUI_Utils_Platform_HPP +#endif // SLIC3R_Platform_HPP diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 0c26d42c8..1ac45f1b5 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -6,6 +6,7 @@ #include #include +#include "Platform.hpp" #include "Time.hpp" #ifdef WIN32 @@ -19,9 +20,14 @@ #ifdef BSD #include #endif - #ifdef __APPLE__ + #ifdef __APPLE__ #include #endif + #ifdef __linux__ + #include + #include + #include + #endif #endif #include @@ -417,6 +423,140 @@ std::error_code rename_file(const std::string &from, const std::string &to) #endif } +#ifdef __linux__ +// Copied from boost::filesystem, to support copying a file to a weird filesystem, which does not support changing file attributes, +// for example ChromeOS Linux integration or FlashAIR WebDAV. +// Copied and simplified from boost::filesystem::detail::copy_file() with option = overwrite_if_exists and with just the Linux path kept, +// and only features supported by Linux 3.10 (on our build server with CentOS 7) are kept, namely sendfile with ranges and statx() are not supported. +bool copy_file_linux(const boost::filesystem::path &from, const boost::filesystem::path &to, boost::system::error_code &ec) +{ + using namespace boost::filesystem; + + struct fd_wrapper + { + int fd { -1 }; + fd_wrapper() = default; + explicit fd_wrapper(int fd) throw() : fd(fd) {} + ~fd_wrapper() throw() { if (fd >= 0) ::close(fd); } + }; + + ec.clear(); + int err = 0; + + // Note: Declare fd_wrappers here so that errno is not clobbered by close() that may be called in fd_wrapper destructors + fd_wrapper infile, outfile; + + while (true) { + infile.fd = ::open(from.c_str(), O_RDONLY | O_CLOEXEC); + if (infile.fd < 0) { + err = errno; + if (err == EINTR) + continue; + fail: + ec.assign(err, boost::system::system_category()); + return false; + } + break; + } + + struct ::stat from_stat; + if (::fstat(infile.fd, &from_stat) != 0) { + fail_errno: + err = errno; + goto fail; + } + + const mode_t from_mode = from_stat.st_mode; + if (!S_ISREG(from_mode)) { + err = ENOSYS; + goto fail; + } + + // Enable writing for the newly created files. Having write permission set is important e.g. for NFS, + // which checks the file permission on the server, even if the client's file descriptor supports writing. + mode_t to_mode = from_mode | S_IWUSR; + int oflag = O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC; + + while (true) { + outfile.fd = ::open(to.c_str(), oflag, to_mode); + if (outfile.fd < 0) { + err = errno; + if (err == EINTR) + continue; + goto fail; + } + break; + } + + struct ::stat to_stat; + if (::fstat(outfile.fd, &to_stat) != 0) + goto fail_errno; + + to_mode = to_stat.st_mode; + if (!S_ISREG(to_mode)) { + err = ENOSYS; + goto fail; + } + + if (from_stat.st_dev == to_stat.st_dev && from_stat.st_ino == to_stat.st_ino) { + err = EEXIST; + goto fail; + } + + //! copy_file implementation that uses sendfile loop. Requires sendfile to support file descriptors. + //FIXME Vojtech: This is a copy loop valid for Linux 2.6.33 and newer. + // copy_file_data_copy_file_range() supports cross-filesystem copying since 5.3, but Vojtech did not want to polute this + // function with that, we don't think the performance gain is worth it for the types of files we are copying, + // and our build server based on CentOS 7 with Linux 3.10 does not support that anyways. + { + // sendfile will not send more than this amount of data in one call + constexpr std::size_t max_send_size = 0x7ffff000u; + uintmax_t offset = 0u; + while (off_t(offset) < from_stat.st_size) { + uintmax_t size_left = from_stat.st_size - offset; + std::size_t size_to_copy = max_send_size; + if (size_left < static_cast(max_send_size)) + size_to_copy = static_cast(size_left); + ssize_t sz = ::sendfile(outfile.fd, infile.fd, nullptr, size_to_copy); + if (sz < 0) { + err = errno; + if (err == EINTR) + continue; + if (err == 0) + break; + goto fail; // err already contains the error code + } + offset += sz; + } + } + + // If we created a new file with an explicitly added S_IWUSR permission, + // we may need to update its mode bits to match the source file. + if (to_mode != from_mode && ::fchmod(outfile.fd, from_mode) != 0) { + if (platform_flavor() == PlatformFlavor::LinuxOnChromium) { + // Ignore that. 9p filesystem does not allow fmod(). + BOOST_LOG_TRIVIAL(info) << "copy_file_linux() failed to fchmod() the output file \"" << to.string() << "\" to " << from_mode << ": " << ec.message() << + " This may be expected when writing to a 9p filesystem."; + } else { + // Generic linux. Write out an error to console. At least we may get some feedback. + BOOST_LOG_TRIVIAL(error) << "copy_file_linux() failed to fchmod() the output file \"" << to.string() << "\" to " << from_mode << ": " << ec.message(); + } + } + + // Note: Use fsync/fdatasync followed by close to avoid dealing with the possibility of close failing with EINTR. + // Even if close fails, including with EINTR, most operating systems (presumably, except HP-UX) will close the + // file descriptor upon its return. This means that if an error happens later, when the OS flushes data to the + // underlying media, this error will go unnoticed and we have no way to receive it from close. Calling fsync/fdatasync + // ensures that all data have been written, and even if close fails for some unfathomable reason, we don't really + // care at that point. + err = ::fdatasync(outfile.fd); + if (err != 0) + goto fail_errno; + + return true; +} +#endif // __linux__ + CopyFileResult copy_file_inner(const std::string& from, const std::string& to, std::string& error_message) { const boost::filesystem::path source(from); @@ -434,7 +574,13 @@ CopyFileResult copy_file_inner(const std::string& from, const std::string& to, s if (ec) BOOST_LOG_TRIVIAL(debug) << "boost::filesystem::permisions before copy error message (this could be irrelevant message based on file system): " << ec.message(); ec.clear(); +#ifdef __linux__ + // We want to allow copying files on Linux to succeed even if changing the file attributes fails. + // That may happen when copying on some exotic file system, for example Linux on Chrome. + copy_file_linux(source, target, ec); +#else // __linux__ boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists, ec); +#endif // __linux__ if (ec) { error_message = ec.message(); return FAIL_COPY_FILE; diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 6436de2ca..4b3a1c6ca 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -209,8 +209,6 @@ set(SLIC3R_GUI_SOURCES Utils/Bonjour.hpp Utils/PresetUpdater.cpp Utils/PresetUpdater.hpp - Utils/Platform.cpp - Utils/Platform.hpp Utils/Process.cpp Utils/Process.hpp Utils/Profile.hpp diff --git a/src/slic3r/GUI/GUI_Init.cpp b/src/slic3r/GUI/GUI_Init.cpp index 3c77f3335..839782741 100644 --- a/src/slic3r/GUI/GUI_Init.cpp +++ b/src/slic3r/GUI/GUI_Init.cpp @@ -9,7 +9,6 @@ #include "slic3r/GUI/format.hpp" #include "slic3r/GUI/MainFrame.hpp" #include "slic3r/GUI/Plater.hpp" -#include "slic3r/Utils/Platform.hpp" // To show a message box if GUI initialization ends up with an exception thrown. #include @@ -37,8 +36,6 @@ int GUI_Run(GUI_InitParams ¶ms) signal(SIGCHLD, SIG_DFL); #endif // __APPLE__ - detect_platform(); - try { GUI::GUI_App* gui = new GUI::GUI_App(params.start_as_gcodeviewer ? GUI::GUI_App::EAppMode::GCodeViewer : GUI::GUI_App::EAppMode::Editor); if (gui->get_app_mode() != GUI::GUI_App::EAppMode::GCodeViewer) { diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index f50c7e356..ad4468683 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1218,8 +1218,10 @@ void MainFrame::init_menubar_as_editor() [this](wxCommandEvent&) { m_plater->collapse_sidebar(!m_plater->is_sidebar_collapsed()); }, this, []() { return true; }, [this]() { return m_plater->is_sidebar_collapsed(); }, this); append_menu_check_item(viewMenu, wxID_ANY, _L("&Full screen") + "\t" + "F11", _L("Full screen"), - [this](wxCommandEvent&) { this->ShowFullScreen(!this->IsFullScreen()); }, this, - []() { return true; }, [this]() { return this->IsFullScreen(); }, this); + [this](wxCommandEvent&) { this->ShowFullScreen(!this->IsFullScreen(), + // wxFULLSCREEN_ALL: wxFULLSCREEN_NOMENUBAR | wxFULLSCREEN_NOTOOLBAR | wxFULLSCREEN_NOSTATUSBAR | wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION + wxFULLSCREEN_NOSTATUSBAR | wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION); }, + this, []() { return true; }, [this]() { return this->IsFullScreen(); }, this); } // Help menu diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index 8e8774036..c843da460 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -4,7 +4,8 @@ #include "GUI.hpp" #include "I18N.hpp" #include "3DScene.hpp" -#include "slic3r/Utils/Platform.hpp" + +#include "libslic3r/Platform.hpp" #include @@ -324,7 +325,7 @@ void OpenGLManager::detect_multisample(int* attribList) enable_multisample && // Disable multi-sampling on ChromeOS, as the OpenGL virtualization swaps Red/Blue channels with multi-sampling enabled, // at least on some platforms. - (platform() != Platform::Linux || platform_flavor() != PlatformFlavor::LinuxOnChromium) && + platform_flavor() != PlatformFlavor::LinuxOnChromium && wxGLCanvas::IsDisplaySupported(attribList) ? EMultisampleState::Enabled : EMultisampleState::Disabled; // Alternative method: it was working on previous version of wxWidgets but not with the latest, at least on Windows diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 15e89168a..273f39b1e 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -76,7 +76,6 @@ #include "../Utils/FixModelByWin10.hpp" #include "../Utils/UndoRedo.hpp" #include "../Utils/PresetUpdater.hpp" -#include "../Utils/Platform.hpp" #include "../Utils/Process.hpp" #include "RemovableDriveManager.hpp" #include "InstanceCheck.hpp" @@ -89,7 +88,9 @@ #include // Needs to be last because reasons :-/ #include "WipeTowerDialog.hpp" + #include "libslic3r/CustomGCode.hpp" +#include "libslic3r/Platform.hpp" using boost::optional; namespace fs = boost::filesystem; @@ -3660,7 +3661,7 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt) show_action_buttons(false); notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, // Don't offer the "Eject" button on ChromeOS, the Linux side has no control over it. - platform() != Platform::Linux || platform_flavor() != PlatformFlavor::LinuxOnChromium); + platform_flavor() != PlatformFlavor::LinuxOnChromium); wxGetApp().removable_drive_manager()->set_exporting_finished(true); }else if (exporting_status == ExportingStatus::EXPORTING_TO_LOCAL && !has_error) notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, false); diff --git a/src/slic3r/GUI/PresetHints.cpp b/src/slic3r/GUI/PresetHints.cpp index 0e5e10d45..181dcfda4 100644 --- a/src/slic3r/GUI/PresetHints.cpp +++ b/src/slic3r/GUI/PresetHints.cpp @@ -139,7 +139,6 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle const ConfigOptionFloatOrPercent *first_layer_extrusion_width_ptr = (first_layer && first_layer_extrusion_width.value > 0) ? &first_layer_extrusion_width : nullptr; const float lh = float(first_layer ? first_layer_height : layer_height); - const float bfr = bridging ? bridge_flow_ratio : 0.f; double max_flow = 0.; std::string max_flow_extrusion_type; auto limit_by_first_layer_speed = [&first_layer_speed, first_layer](double speed_normal, double speed_max) { diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index c200956e1..33e61ab90 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -1,5 +1,5 @@ #include "RemovableDriveManager.hpp" -#include "slic3r/Utils/Platform.hpp" +#include "libslic3r/Platform.hpp" #include #include @@ -232,7 +232,7 @@ std::vector RemovableDriveManager::search_for_removable_drives() cons #else - if (platform() == Platform::Linux && platform_flavor() == PlatformFlavor::LinuxOnChromium) { + if (platform_flavor() == PlatformFlavor::LinuxOnChromium) { // ChromeOS specific: search /mnt/chromeos/removable/* folder search_for_drives_internal::search_path("/mnt/chromeos/removable/*", "/mnt/chromeos/removable", current_drives); } else { @@ -452,7 +452,7 @@ RemovableDriveManager::RemovableDrivesStatus RemovableDriveManager::status() tbb::mutex::scoped_lock lock(m_drives_mutex); out.has_eject = // Cannot control eject on Chromium. - (platform() != Platform::Linux || platform_flavor() != PlatformFlavor::LinuxOnChromium) && + platform_flavor() != PlatformFlavor::LinuxOnChromium && this->find_last_save_path_drive_data() != m_current_drives.end(); out.has_removable_drives = ! m_current_drives.empty(); } From b9c4d29fe9beb023b4e2a491b28fa67cd239c813 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 15 Mar 2021 17:00:49 +0100 Subject: [PATCH 05/32] call detect_platform() after SLIC3R_LOGLEVEL env variable is read --- src/PrusaSlicer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 10da1cb07..50e5096bf 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -69,8 +69,6 @@ int CLI::run(int argc, char **argv) { // Mark the main thread for the debugger and for runtime checks. set_current_thread_name("slic3r_main"); - // Detect the operating system flavor. - detect_platform(); #ifdef __WXGTK__ // On Linux, wxGTK has no support for Wayland, and the app crashes on @@ -606,6 +604,9 @@ bool CLI::setup(int argc, char **argv) } } + // Detect the operating system flavor after SLIC3R_LOGLEVEL is set. + detect_platform(); + #ifdef WIN32 // Notify user if blacklisted library is already loaded (Nahimic) // If there are cases of no reports with blacklisted lib - this check should be performed later. From d695e090eff0785c24b9267f122b8d5f17e2949b Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 15 Mar 2021 17:11:36 +0100 Subject: [PATCH 06/32] Changing the Fullscreen hotkey to Ctrl+Cmd+F for OSX --- src/slic3r/GUI/MainFrame.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index ad4468683..4fbb29bfd 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1217,7 +1217,13 @@ void MainFrame::init_menubar_as_editor() append_menu_check_item(viewMenu, wxID_ANY, _L("&Collapse sidebar") + sep + "Shift+" + sep_space + "Tab", _L("Collapse sidebar"), [this](wxCommandEvent&) { m_plater->collapse_sidebar(!m_plater->is_sidebar_collapsed()); }, this, []() { return true; }, [this]() { return m_plater->is_sidebar_collapsed(); }, this); - append_menu_check_item(viewMenu, wxID_ANY, _L("&Full screen") + "\t" + "F11", _L("Full screen"), + append_menu_check_item(viewMenu, wxID_ANY, _L("&Full screen") + "\t" + +#ifdef __APPLE__ + "RAWCTRL+CTRL+F" +#else + "F11" +#endif + , _L("Full screen"), [this](wxCommandEvent&) { this->ShowFullScreen(!this->IsFullScreen(), // wxFULLSCREEN_ALL: wxFULLSCREEN_NOMENUBAR | wxFULLSCREEN_NOTOOLBAR | wxFULLSCREEN_NOSTATUSBAR | wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION wxFULLSCREEN_NOSTATUSBAR | wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION); }, From fc0e2d578fa28a5e90dbcf08efb3a12a5b87c14f Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 15 Mar 2021 17:42:18 +0100 Subject: [PATCH 07/32] Checking for removable media on Chrome OS. --- src/slic3r/GUI/RemovableDriveManager.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index 33e61ab90..83f7077b4 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -186,8 +186,13 @@ namespace search_for_drives_internal { //confirms if the file is removable drive and adds it to vector - //if not same file system - could be removable drive - if (! compare_filesystem_id(path, parent_path)) { + if ( +#ifdef __linux__ + // Chromium mounts removable drives in a way that produces the same device ID. + platform_flavor() == PlatformFlavor::LinuxOnChromium || +#endif + // If not same file system - could be removable drive. + ! compare_filesystem_id(path, parent_path)) { //free space boost::system::error_code ec; boost::filesystem::space_info si = boost::filesystem::space(path, ec); From fc7762e465af81f3551f7fe795537e20f06ef7df Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 16 Mar 2021 07:25:31 +0100 Subject: [PATCH 08/32] Added a missing include (Linux) --- src/libslic3r/Platform.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/Platform.cpp b/src/libslic3r/Platform.cpp index 4c8805149..ae02c42b3 100644 --- a/src/libslic3r/Platform.cpp +++ b/src/libslic3r/Platform.cpp @@ -1,6 +1,7 @@ #include "Platform.hpp" #include +#include namespace Slic3r { From d0febbec321048135ca1f4a91250739f89570ed9 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 16 Mar 2021 08:54:37 +0100 Subject: [PATCH 09/32] Disable the new Toggle fullscreen menu item on OSX, it adds its own. --- src/slic3r/GUI/MainFrame.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 4fbb29bfd..aeac68d94 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1217,17 +1217,14 @@ void MainFrame::init_menubar_as_editor() append_menu_check_item(viewMenu, wxID_ANY, _L("&Collapse sidebar") + sep + "Shift+" + sep_space + "Tab", _L("Collapse sidebar"), [this](wxCommandEvent&) { m_plater->collapse_sidebar(!m_plater->is_sidebar_collapsed()); }, this, []() { return true; }, [this]() { return m_plater->is_sidebar_collapsed(); }, this); - append_menu_check_item(viewMenu, wxID_ANY, _L("&Full screen") + "\t" + -#ifdef __APPLE__ - "RAWCTRL+CTRL+F" -#else - "F11" -#endif - , _L("Full screen"), +#ifndef __APPLE__ + // OSX adds its own menu item to toggle fullscreen. + append_menu_check_item(viewMenu, wxID_ANY, _L("&Full screen") + "\t" + "F11", _L("Full screen"), [this](wxCommandEvent&) { this->ShowFullScreen(!this->IsFullScreen(), // wxFULLSCREEN_ALL: wxFULLSCREEN_NOMENUBAR | wxFULLSCREEN_NOTOOLBAR | wxFULLSCREEN_NOSTATUSBAR | wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION wxFULLSCREEN_NOSTATUSBAR | wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION); }, this, []() { return true; }, [this]() { return this->IsFullScreen(); }, this); +#endif // __APPLE__ } // Help menu From 4b9de0398fae01b45cdfc739f8a6c91533357672 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 8 Mar 2021 17:32:40 +0100 Subject: [PATCH 10/32] Drill holes one by one and display warning of any of them fails Drill with cgal::minus for now --- src/libslic3r/SLA/Hollowing.hpp | 1 + src/libslic3r/SLAPrintSteps.cpp | 53 +++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 21 ++++++++-- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 4 ++ 5 files changed, 54 insertions(+), 29 deletions(-) diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index 5d2181e7a..1778c692a 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -36,6 +36,7 @@ struct DrainHole Vec3f normal; float radius; float height; + bool failed = false; DrainHole() : pos(Vec3f::Zero()), normal(Vec3f::UnitZ()), radius(5.f), height(10.f) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 22fee6976..4d8abb798 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -244,6 +244,8 @@ static std::vector create_exclude_mask( Vec3f face_normal = C.normalized(); for (const sla::DrainHole &dh : holes) { + if (dh.failed) continue; + Vec3d dhpos = dh.pos.cast(); Vec3d dhend = dhpos + dh.normal.cast() * dh.height; @@ -312,41 +314,42 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) BOOST_LOG_TRIVIAL(info) << "Drilling drainage holes."; sla::DrainHoles drainholes = po.transformed_drainhole_points(); + + auto hollowed_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal(hollowed_mesh); + + bool hole_fail = false; + for (size_t i = 0; i < drainholes.size(); ++i) { + const sla::DrainHole &holept = drainholes[i]; + po.model_object()->sla_drain_holes[i].failed = false; - std::uniform_real_distribution dist(0., float(EPSILON)); - auto holes_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal({}); - for (sla::DrainHole holept : drainholes) { - holept.normal += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)}; - holept.normal.normalize(); - holept.pos += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)}; TriangleMesh m = sla::to_triangle_mesh(holept.to_mesh()); m.require_shared_vertices(); auto cgal_m = MeshBoolean::cgal::triangle_mesh_to_cgal(m); - MeshBoolean::cgal::plus(*holes_mesh_cgal, *cgal_m); + + try { + MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *cgal_m); + } catch (const std::runtime_error &) { + BOOST_LOG_TRIVIAL(error) << "Failed to drill hole"; + + hole_fail = drainholes[i].failed = + po.model_object()->sla_drain_holes[i].failed = true; + } } - if (MeshBoolean::cgal::does_self_intersect(*holes_mesh_cgal)) - throw Slic3r::SlicingError(L("Too many overlapping holes.")); + if (hole_fail) + po.active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, + L("Failed to drill some holes into the model")); - auto hollowed_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal(hollowed_mesh); - try { - MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *holes_mesh_cgal); - hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal); - mesh_view = hollowed_mesh; + hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal); + mesh_view = hollowed_mesh; - if (is_hollowed) { - auto &interior = *po.m_hollowing_data->interior; - std::vector exclude_mask = - create_exclude_mask(mesh_view.its, interior, drainholes); + if (is_hollowed) { + auto &interior = *po.m_hollowing_data->interior; + std::vector exclude_mask = + create_exclude_mask(mesh_view.its, interior, drainholes); - sla::remove_inside_triangles(mesh_view, interior, exclude_mask); - } - - } catch (const std::runtime_error &) { - throw Slic3r::SlicingError(L( - "Drilling holes into the mesh failed. " - "This is usually caused by broken model. Try to fix it first.")); + sla::remove_inside_triangles(mesh_view, interior, exclude_mask); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 3d0d9c79a..67133956d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -125,6 +125,8 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons float render_color[4]; const sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes; size_t cache_size = drain_holes.size(); + + const auto *hollowed_mesh = m_c->hollowed_mesh(); for (size_t i = 0; i < cache_size; ++i) { const sla::DrainHole& drain_hole = drain_holes[i]; @@ -136,6 +138,13 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons // First decide about the color of the point. if (picking) { std::array color = picking_color_component(i); + + if (hollowed_mesh && i < hollowed_mesh->get_drainholes().size() && hollowed_mesh->get_drainholes()[i].failed) { + render_color[0] = 1.f; + render_color[1] = 1.f; + render_color[2] = 0.f; + render_color[3] = color[3]; + } render_color[0] = color[0]; render_color[1] = color[1]; render_color[2] = color[2]; @@ -149,9 +158,15 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons render_color[2] = 1.0f; } else { // neigher hover nor picking - render_color[0] = point_selected ? 1.0f : 0.7f; - render_color[1] = point_selected ? 0.3f : 0.7f; - render_color[2] = point_selected ? 0.3f : 0.7f; + if (hollowed_mesh && i < hollowed_mesh->get_drainholes().size() && hollowed_mesh->get_drainholes()[i].failed) { + render_color[0] = 1.f; + render_color[1] = 1.f; + render_color[2] = 0.f; + } else { + render_color[0] = point_selected ? 1.0f : 0.7f; + render_color[1] = point_selected ? 0.3f : 0.7f; + render_color[2] = point_selected ? 0.3f : 0.7f; + } render_color[3] = 0.5f; } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 7f6b10670..acb85d539 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -205,6 +205,7 @@ void HollowedMesh::on_update() m_hollowed_mesh_transformed.reset(new TriangleMesh(backend_mesh)); Transform3d trafo_inv = canvas->sla_print()->sla_trafo(*mo).inverse(); m_hollowed_mesh_transformed->transform(trafo_inv); + m_drainholes = print_object->model_object()->sla_drain_holes; m_old_hollowing_timestamp = timestamp; const TriangleMesh &interior = print_object->hollowed_interior_mesh(); @@ -215,8 +216,9 @@ void HollowedMesh::on_update() m_hollowed_interior_transformed->transform(trafo_inv); } } - else + else { m_hollowed_mesh_transformed.reset(nullptr); + } } } else diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index ace256748..28be1b97f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -5,6 +5,7 @@ #include #include "slic3r/GUI/MeshUtils.hpp" +#include "libslic3r/SLA/Hollowing.hpp" namespace Slic3r { @@ -198,6 +199,8 @@ public: CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; } #endif // NDEBUG + const sla::DrainHoles &get_drainholes() const { return m_drainholes; } + const TriangleMesh* get_hollowed_mesh() const; const TriangleMesh* get_hollowed_interior() const; @@ -211,6 +214,7 @@ private: size_t m_old_hollowing_timestamp = 0; int m_print_object_idx = -1; int m_print_objects_count = 0; + sla::DrainHoles m_drainholes; }; From 6059d89bc8cfb4b21a3bdcf629a84e53544543cb Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 3 Mar 2021 14:30:05 +0100 Subject: [PATCH 11/32] Avoid mesh alteration when converting to CGAL Surface_Mesh orient_to_bound_a_volume tends produce incorrect results. --- src/libslic3r/MeshBoolean.cpp | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index 6ffc5dec3..4b18d6c1c 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -111,32 +111,15 @@ struct CGALMesh { _EpicMesh m; }; // ///////////////////////////////////////////////////////////////////////////// template void triangle_mesh_to_cgal(const TriangleMesh &M, _Mesh &out) -{ - using Index3 = std::array; - +{ if (M.empty()) return; - - std::vector points; - std::vector indices; - points.reserve(M.its.vertices.size()); - indices.reserve(M.its.indices.size()); - for (auto &v : M.its.vertices) points.emplace_back(v.x(), v.y(), v.z()); - for (auto &_f : M.its.indices) { - auto f = _f.cast(); - indices.emplace_back(Index3{f(0), f(1), f(2)}); - } - CGALProc::orient_polygon_soup(points, indices); - CGALProc::polygon_soup_to_polygon_mesh(points, indices, out); - - // Number the faces because 'orient_to_bound_a_volume' needs a face <--> index map - unsigned index = 0; - for (auto face : out.faces()) face = CGAL::SM_Face_index(index++); - - if(CGAL::is_closed(out)) - CGALProc::orient_to_bound_a_volume(out); - else - throw Slic3r::RuntimeError("Mesh not watertight"); + for (auto &v : M.its.vertices) + out.add_vertex(typename _Mesh::Point{v.x(), v.y(), v.z()}); + + using VI = typename _Mesh::Vertex_index; + for (auto &f : M.its.indices) + out.add_face(VI(f(0)), VI(f(1)), VI(f(2))); } inline Vec3d to_vec3d(const _EpicMesh::Point &v) From 3d0d96d8f9566847df740b4551fd2db7b07a5a81 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 10 Mar 2021 18:21:36 +0100 Subject: [PATCH 12/32] Working hole drilling one by one without linear slowdown. --- src/libslic3r/AABBTreeIndirect.hpp | 79 ++++++++++++++++++++ src/libslic3r/MeshBoolean.cpp | 27 ++++--- src/libslic3r/MeshBoolean.hpp | 14 +++- src/libslic3r/SLAPrintSteps.cpp | 111 +++++++++++++++++++++++------ 4 files changed, 199 insertions(+), 32 deletions(-) diff --git a/src/libslic3r/AABBTreeIndirect.hpp b/src/libslic3r/AABBTreeIndirect.hpp index b11c570f6..76aa36194 100644 --- a/src/libslic3r/AABBTreeIndirect.hpp +++ b/src/libslic3r/AABBTreeIndirect.hpp @@ -11,6 +11,8 @@ #include #include +#include + #include "Utils.hpp" // for next_highest_power_of_2() extern "C" @@ -752,6 +754,83 @@ void get_candidate_idxs(const TreeType& tree, const VectorType& v, std::vector struct Intersecting {}; + +// Intersection predicate specialization for box-box intersections +template +struct Intersecting> { + Eigen::AlignedBox box; + + Intersecting(const Eigen::AlignedBox &bb): box{bb} {} + + bool operator() (const typename Tree::Node &node) const + { + return box.intersects(node.bbox); + } +}; + +template auto intersecting(const G &g) { return Intersecting{g}; } + +template struct Containing {}; + +// Intersection predicate specialization for box-box intersections +template +struct Containing> { + Eigen::AlignedBox box; + + Containing(const Eigen::AlignedBox &bb): box{bb} {} + + bool operator() (const typename Tree::Node &node) const + { + return box.contains(node.bbox); + } +}; + +template auto containing(const G &g) { return Containing{g}; } + +namespace detail { + +template +void traverse_recurse(const Tree &tree, + size_t idx, + Pred && pred, + Fn && callback) +{ + assert(tree.node(idx).is_valid()); + + if (!pred(tree.node(idx))) return; + + if (tree.node(idx).is_leaf()) { + callback(tree.node(idx).idx); + } else { + + // call this with left and right node idx: + auto trv = [&](size_t idx) { + traverse_recurse(tree, idx, std::forward(pred), + std::forward(callback)); + }; + + // Left / right child node index. + trv(Tree::left_child_idx(idx)); + trv(Tree::right_child_idx(idx)); + } +} + +} // namespace detail + +// Tree traversal with a predicate. Example usage: +// traverse(tree, intersecting(QueryBox), [](size_t face_idx) { +// /* ... */ +// }); +template +void traverse(const Tree &tree, Predicate &&pred, Fn &&callback) +{ + if (tree.empty()) return; + + detail::traverse_recurse(tree, size_t(0), std::forward(pred), + std::forward(callback)); +} } // namespace AABBTreeIndirect } // namespace Slic3r diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index 4b18d6c1c..5a518d580 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -110,15 +110,18 @@ struct CGALMesh { _EpicMesh m; }; // Converions from and to CGAL mesh // ///////////////////////////////////////////////////////////////////////////// -template void triangle_mesh_to_cgal(const TriangleMesh &M, _Mesh &out) -{ - if (M.empty()) return; +template +void triangle_mesh_to_cgal(const std::vector & V, + const std::vector &F, + _Mesh &out) +{ + if (F.empty()) return; - for (auto &v : M.its.vertices) + for (auto &v : V) out.add_vertex(typename _Mesh::Point{v.x(), v.y(), v.z()}); using VI = typename _Mesh::Vertex_index; - for (auto &f : M.its.indices) + for (auto &f : F) out.add_face(VI(f(0)), VI(f(1)), VI(f(2))); } @@ -155,14 +158,16 @@ template TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh) } TriangleMesh out{points, facets}; - out.require_shared_vertices(); + out.repair(); return out; } -std::unique_ptr triangle_mesh_to_cgal(const TriangleMesh &M) +std::unique_ptr +triangle_mesh_to_cgal(const std::vector &V, + const std::vector &F) { std::unique_ptr out(new CGALMesh{}); - triangle_mesh_to_cgal(M, out->m); + triangle_mesh_to_cgal(V, F, out->m); return out; } @@ -221,8 +226,8 @@ template void _mesh_boolean_do(Op &&op, TriangleMesh &A, const Triangl { CGALMesh meshA; CGALMesh meshB; - triangle_mesh_to_cgal(A, meshA.m); - triangle_mesh_to_cgal(B, meshB.m); + triangle_mesh_to_cgal(A.its.vertices, A.its.indices, meshA.m); + triangle_mesh_to_cgal(B.its.vertices, B.its.indices, meshB.m); _cgal_do(op, meshA, meshB); @@ -247,7 +252,7 @@ void intersect(TriangleMesh &A, const TriangleMesh &B) bool does_self_intersect(const TriangleMesh &mesh) { CGALMesh cgalm; - triangle_mesh_to_cgal(mesh, cgalm.m); + triangle_mesh_to_cgal(mesh.its.vertices, mesh.its.indices, cgalm.m); return CGALProc::does_self_intersect(cgalm.m); } diff --git a/src/libslic3r/MeshBoolean.hpp b/src/libslic3r/MeshBoolean.hpp index ce17a1328..8e88e2536 100644 --- a/src/libslic3r/MeshBoolean.hpp +++ b/src/libslic3r/MeshBoolean.hpp @@ -27,7 +27,19 @@ namespace cgal { struct CGALMesh; struct CGALMeshDeleter { void operator()(CGALMesh *ptr); }; -std::unique_ptr triangle_mesh_to_cgal(const TriangleMesh &M); +std::unique_ptr +triangle_mesh_to_cgal(const std::vector &V, + const std::vector &F); + +inline std::unique_ptr triangle_mesh_to_cgal(const indexed_triangle_set &M) +{ + return triangle_mesh_to_cgal(M.vertices, M.indices); +} +inline std::unique_ptr triangle_mesh_to_cgal(const TriangleMesh &M) +{ + return triangle_mesh_to_cgal(M.its); +} + TriangleMesh cgal_to_triangle_mesh(const CGALMesh &cgalmesh); // Do boolean mesh difference with CGAL bypassing igl. diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 4d8abb798..fc6686201 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -12,6 +12,7 @@ #include #include +#include #include @@ -272,6 +273,36 @@ static std::vector create_exclude_mask( return exclude_mask; } +static indexed_triangle_set +remove_unconnected_vertices(const indexed_triangle_set &its) +{ + if (its.indices.empty()) {}; + + indexed_triangle_set M; + + std::vector vtransl(its.vertices.size(), -1); + int vcnt = 0; + for (auto &f : its.indices) { + + for (int i = 0; i < 3; ++i) + if (vtransl[size_t(f(i))] < 0) { + + M.vertices.emplace_back(its.vertices[size_t(f(i))]); + vtransl[size_t(f(i))] = vcnt++; + } + + std::array new_f = { + vtransl[size_t(f(0))], + vtransl[size_t(f(1))], + vtransl[size_t(f(2))] + }; + + M.indices.emplace_back(new_f[0], new_f[1], new_f[2]); + } + + return M; +} + // Drill holes into the hollowed/original mesh. void SLAPrint::Steps::drill_holes(SLAPrintObject &po) { @@ -314,43 +345,83 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) BOOST_LOG_TRIVIAL(info) << "Drilling drainage holes."; sla::DrainHoles drainholes = po.transformed_drainhole_points(); - - auto hollowed_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal(hollowed_mesh); + + auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( + hollowed_mesh.its.vertices, + hollowed_mesh.its.indices + ); + + std::uniform_real_distribution dist(0., float(EPSILON)); + auto holes_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal({}, {}); + indexed_triangle_set part_to_drill = hollowed_mesh.its; bool hole_fail = false; for (size_t i = 0; i < drainholes.size(); ++i) { - const sla::DrainHole &holept = drainholes[i]; - po.model_object()->sla_drain_holes[i].failed = false; + sla::DrainHole holept = drainholes[i]; + holept.normal += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)}; + holept.normal.normalize(); + holept.pos += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)}; TriangleMesh m = sla::to_triangle_mesh(holept.to_mesh()); m.require_shared_vertices(); - auto cgal_m = MeshBoolean::cgal::triangle_mesh_to_cgal(m); - try { - MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *cgal_m); - } catch (const std::runtime_error &) { + part_to_drill.indices.clear(); + auto bb = m.bounding_box(); + Eigen::AlignedBox ebb{bb.min.cast(), + bb.max.cast()}; + + AABBTreeIndirect::traverse( + tree, + AABBTreeIndirect::intersecting(ebb), + [&part_to_drill, &hollowed_mesh](size_t faceid) + { + part_to_drill.indices.emplace_back(hollowed_mesh.its.indices[faceid]); + }); + + auto cgal_meshpart = MeshBoolean::cgal::triangle_mesh_to_cgal( + remove_unconnected_vertices(part_to_drill)); + + if (MeshBoolean::cgal::does_self_intersect(*cgal_meshpart)) { BOOST_LOG_TRIVIAL(error) << "Failed to drill hole"; hole_fail = drainholes[i].failed = po.model_object()->sla_drain_holes[i].failed = true; + + continue; } + + auto cgal_hole = MeshBoolean::cgal::triangle_mesh_to_cgal(m); + MeshBoolean::cgal::plus(*holes_mesh_cgal, *cgal_hole); + } + + if (MeshBoolean::cgal::does_self_intersect(*holes_mesh_cgal)) + throw Slic3r::SlicingError(L("Too many overlapping holes.")); + + auto hollowed_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal(hollowed_mesh); + + try { + MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *holes_mesh_cgal); + + hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal); + mesh_view = hollowed_mesh; + + if (is_hollowed) { + auto &interior = *po.m_hollowing_data->interior; + std::vector exclude_mask = + create_exclude_mask(mesh_view.its, interior, drainholes); + + sla::remove_inside_triangles(mesh_view, interior, exclude_mask); + } + + } catch (const std::runtime_error &) { + throw Slic3r::SlicingError(L( + "Drilling holes into the mesh failed. " + "This is usually caused by broken model. Try to fix it first.")); } if (hole_fail) po.active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, L("Failed to drill some holes into the model")); - - - hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal); - mesh_view = hollowed_mesh; - - if (is_hollowed) { - auto &interior = *po.m_hollowing_data->interior; - std::vector exclude_mask = - create_exclude_mask(mesh_view.its, interior, drainholes); - - sla::remove_inside_triangles(mesh_view, interior, exclude_mask); - } } // The slicing will be performed on an imaginary 1D grid which starts from From 6d58546aef97b49a036960ff4daa2f8754588bd2 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 11 Mar 2021 14:15:45 +0100 Subject: [PATCH 13/32] Fix unmarked failed holes on first gizmo opening --- src/libslic3r/SLA/Hollowing.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index 1778c692a..d9a77cd35 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -42,12 +42,12 @@ struct DrainHole : pos(Vec3f::Zero()), normal(Vec3f::UnitZ()), radius(5.f), height(10.f) {} - DrainHole(Vec3f p, Vec3f n, float r, float h) - : pos(p), normal(n), radius(r), height(h) + DrainHole(Vec3f p, Vec3f n, float r, float h, bool fl = false) + : pos(p), normal(n), radius(r), height(h), failed(fl) {} DrainHole(const DrainHole& rhs) : - DrainHole(rhs.pos, rhs.normal, rhs.radius, rhs.height) {} + DrainHole(rhs.pos, rhs.normal, rhs.radius, rhs.height, rhs.failed) {} bool operator==(const DrainHole &sp) const; @@ -62,7 +62,7 @@ struct DrainHole template inline void serialize(Archive &ar) { - ar(pos, normal, radius, height); + ar(pos, normal, radius, height, failed); } static constexpr size_t steps = 32; From 96ac6ff2fa9939737e7df3dada622ba64059ab5c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 11 Mar 2021 17:14:40 +0100 Subject: [PATCH 14/32] Fix undrillable hole color and picking --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 39 +++++++++---------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 67133956d..9db50e6f1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -122,11 +122,10 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons glsafe(::glTranslated(0.0, 0.0, m_c->selection_info()->get_sla_shift())); glsafe(::glMultMatrixd(instance_matrix.data())); - float render_color[4]; + std::array render_color; const sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes; size_t cache_size = drain_holes.size(); - const auto *hollowed_mesh = m_c->hollowed_mesh(); for (size_t i = 0; i < cache_size; ++i) { const sla::DrainHole& drain_hole = drain_holes[i]; @@ -139,38 +138,26 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons if (picking) { std::array color = picking_color_component(i); - if (hollowed_mesh && i < hollowed_mesh->get_drainholes().size() && hollowed_mesh->get_drainholes()[i].failed) { - render_color[0] = 1.f; - render_color[1] = 1.f; - render_color[2] = 0.f; - render_color[3] = color[3]; - } - render_color[0] = color[0]; - render_color[1] = color[1]; - render_color[2] = color[2]; - render_color[3] = color[3]; + render_color = color; } else { - render_color[3] = 1.f; if (size_t(m_hover_id) == i) { - render_color[0] = 0.f; - render_color[1] = 1.0f; - render_color[2] = 1.0f; + render_color = {0.f, 1.f, 1.f, 1.f}; + } else if (m_c->hollowed_mesh() && + i < m_c->hollowed_mesh()->get_drainholes().size() && + m_c->hollowed_mesh()->get_drainholes()[i].failed) { + render_color = {1.f, 0.f, 0.f, .5f}; } else { // neigher hover nor picking - if (hollowed_mesh && i < hollowed_mesh->get_drainholes().size() && hollowed_mesh->get_drainholes()[i].failed) { - render_color[0] = 1.f; - render_color[1] = 1.f; - render_color[2] = 0.f; - } else { - render_color[0] = point_selected ? 1.0f : 0.7f; - render_color[1] = point_selected ? 0.3f : 0.7f; - render_color[2] = point_selected ? 0.3f : 0.7f; - } + + render_color[0] = point_selected ? 1.0f : 0.7f; + render_color[1] = point_selected ? 0.3f : 0.7f; + render_color[2] = point_selected ? 0.3f : 0.7f; render_color[3] = 0.5f; } } - glsafe(::glColor4fv(render_color)); + + glsafe(::glColor4fv(render_color.data())); float render_color_emissive[4] = { 0.5f * render_color[0], 0.5f * render_color[1], 0.5f * render_color[2], 1.f}; glsafe(::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive)); From 80b9a3c62bb674204fbb971868c2a99718f0c090 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 16 Mar 2021 10:31:30 +0100 Subject: [PATCH 15/32] Fix of #6212 - Cannot select "New Project" after deleting models in saved project, continuing overwrites old project --- src/slic3r/GUI/MainFrame.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index aeac68d94..858744229 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -672,7 +672,7 @@ bool MainFrame::is_active_and_shown_tab(Tab* tab) bool MainFrame::can_start_new_project() const { - return (m_plater != nullptr) && !m_plater->model().objects.empty(); + return (m_plater != nullptr) && (!m_plater->get_project_filename(".3mf").IsEmpty() || !m_plater->model().objects.empty()); } bool MainFrame::can_save() const diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 273f39b1e..d7224cedc 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4789,7 +4789,8 @@ void Plater::remove(size_t obj_idx) { p->remove(obj_idx); } void Plater::reset() { p->reset(); } void Plater::reset_with_confirm() { - if (wxMessageDialog(static_cast(this), _L("All objects will be removed, continue?"), wxString(SLIC3R_APP_NAME) + " - " + _L("Delete all"), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) + if (p->model.objects.empty() || + wxMessageDialog(static_cast(this), _L("All objects will be removed, continue?"), wxString(SLIC3R_APP_NAME) + " - " + _L("Delete all"), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) reset(); } From 5daaaa2845922f15b819c9b7d1460f072cea66ba Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 15 Mar 2021 17:40:17 +0100 Subject: [PATCH 16/32] Fixed upload notification cancel button --- src/slic3r/GUI/NotificationManager.cpp | 26 +++++--------------------- src/slic3r/GUI/NotificationManager.hpp | 2 +- src/slic3r/GUI/PrintHostDialogs.cpp | 5 ++--- 3 files changed, 8 insertions(+), 25 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 4c0bf3734..358f709a7 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -5,6 +5,7 @@ #include "ImGuiWrapper.hpp" #include "PrintHostDialogs.hpp" #include "wxExtensions.hpp" +#include "../Utils/PrintHost.hpp" #include #include @@ -827,8 +828,6 @@ void NotificationManager::ProgressBarNotification::render_bar(ImGuiWrapper& imgu //------PrintHostUploadNotification---------------- void NotificationManager::PrintHostUploadNotification::set_percentage(float percent) { - if (m_uj_state == UploadJobState::PB_CANCELLED) - return; m_percentage = percent; if (percent >= 1.0f) { m_uj_state = UploadJobState::PB_COMPLETED; @@ -914,11 +913,7 @@ void NotificationManager::PrintHostUploadNotification::render_cancel_button(ImGu ImGui::SetCursorPosY(win_size.y / 2 - button_size.y); if (imgui.button(button_text.c_str(), button_size.x, button_size.y)) { - assert(m_evt_handler != nullptr); - if (m_evt_handler != nullptr) { - auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_CANCEL, m_job_id, m_job_id); - wxQueueEvent(m_evt_handler, evt); - } + wxGetApp().printhost_job_queue().cancel(m_job_id - 1); } //invisible large button @@ -926,11 +921,7 @@ void NotificationManager::PrintHostUploadNotification::render_cancel_button(ImGu ImGui::SetCursorPosY(0); if (imgui.button(" ", m_line_height * 2.f, win_size.y)) { - assert(m_evt_handler != nullptr); - if (m_evt_handler != nullptr) { - auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_CANCEL, m_job_id, m_job_id); - wxQueueEvent(m_evt_handler, evt); - } + wxGetApp().printhost_job_queue().cancel(m_job_id - 1); } ImGui::PopStyleColor(); ImGui::PopStyleColor(); @@ -1129,28 +1120,21 @@ void NotificationManager::push_exporting_finished_notification(const std::string push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, on_removable, path, dir_path), 0); } -void NotificationManager::push_upload_job_notification(wxEvtHandler* evt_handler, int id, float filesize, const std::string& filename, const std::string& host, float percentage) +void NotificationManager::push_upload_job_notification(int id, float filesize, const std::string& filename, const std::string& host, float percentage) { std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host); NotificationData data{ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotification, 0, text }; - push_notification_data(std::make_unique(data, m_id_provider, evt_handler, 0, id, filesize), 0); + push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, 0, id, filesize), 0); } void NotificationManager::set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage) { std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host); -// bool found = false; for (std::unique_ptr& notification : m_pop_notifications) { if (notification->get_type() == NotificationType::PrintHostUpload && notification->compare_text(text)) { dynamic_cast(notification.get())->set_percentage(percentage); wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); -// found = true; } } - /* - if (!found) { - push_upload_job_notification(id, filename, host, percentage); - } - */ } void NotificationManager::upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host) { diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 651deace8..f709a2a7d 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -147,7 +147,7 @@ public: // Exporting finished, show this information with path, button to open containing folder and if ejectable - eject button void push_exporting_finished_notification(const std::string& path, const std::string& dir_path, bool on_removable); // notification with progress bar - void push_upload_job_notification(wxEvtHandler* evt_handler, int id, float filesize, const std::string& filename, const std::string& host, float percentage = 0); + void push_upload_job_notification(int id, float filesize, const std::string& filename, const std::string& host, float percentage = 0); void set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage); void upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host); void upload_job_notification_show_error(int id, const std::string& filename, const std::string& host); diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index d99584c1e..afbc604bc 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -225,7 +225,7 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent) SetSize(load_user_data(UDT_SIZE, size) ? wxSize(size[0] * em, size[1] * em) : wxSize(HEIGHT * em, WIDTH * em)); Bind(wxEVT_SIZE, [this, em](wxSizeEvent& evt) { - OnSize(evt); + OnSize(evt); save_user_data(UDT_SIZE | UDT_POSITION | UDT_COLS); }); @@ -245,7 +245,6 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent) const JobState state = get_state(selected); if (state < ST_ERROR) { - // TODO: cancel GUI::wxGetApp().printhost_job_queue().cancel(selected); } }); @@ -282,7 +281,7 @@ void PrintHostQueueDialog::append_job(const PrintHostJob &job) // Both strings are UTF-8 encoded. upload_names.emplace_back(job.printhost->get_host(), job.upload_data.upload_path.string()); - wxGetApp().notification_manager()->push_upload_job_notification(this, job_list->GetItemCount(), (float)size_i / 1024 / 1024, job.upload_data.upload_path.string(), job.printhost->get_host()); + wxGetApp().notification_manager()->push_upload_job_notification(job_list->GetItemCount(), (float)size_i / 1024 / 1024, job.upload_data.upload_path.string(), job.printhost->get_host()); } void PrintHostQueueDialog::on_dpi_changed(const wxRect &suggested_rect) From 0c4fb6dfcc56bdc1227ea355da1926389569721e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 17 Mar 2021 09:42:03 +0100 Subject: [PATCH 17/32] CGAL may have infinite face vertex ranges. Calling vtc.size() may end up in an infinite loop. Also, quads do not appear in CGAL meshes. --- src/libslic3r/MeshBoolean.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index 5a518d580..7507e1c54 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -150,11 +150,20 @@ template TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh) } for (auto &face : cgalmesh.faces()) { - auto vtc = cgalmesh.vertices_around_face(cgalmesh.halfedge(face)); - int i = 0; - Vec3i trface; - for (auto v : vtc) trface(i++) = static_cast(v); - facets.emplace_back(trface); + auto vtc = cgalmesh.vertices_around_face(cgalmesh.halfedge(face)); + + int i = 0; + Vec3i facet; + for (const auto &v : vtc) { + if (i > 2) { i = 0; break; } + facet(i++) = v; + } + + if (i == 3) { + facets.emplace_back(facet); + } else { + BOOST_LOG_TRIVIAL(error) << "CGAL face is not a triangle."; + } } TriangleMesh out{points, facets}; From b13239e4ca55d7333b70beb92f0219d6ce81e436 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 17 Mar 2021 10:05:24 +0100 Subject: [PATCH 18/32] Fix previous commit's missing include --- src/libslic3r/MeshBoolean.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index 7507e1c54..da5c95638 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -1,6 +1,7 @@ #include "Exception.hpp" #include "MeshBoolean.hpp" #include "libslic3r/TriangleMesh.hpp" +#include "boost/log/trivial.hpp" #undef PI // Include igl first. It defines "L" macro which then clashes with our localization From a95607d7bf68489d251d8386010f67e95b28076a Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 17 Mar 2021 12:25:43 +0100 Subject: [PATCH 19/32] Fixing an FDM support generator bug, where some of the support columns were missing abruptly when going down. The issue was caused by extracting support areas from a grid and filtering the extracted islands by intersection with the input islands. Sometimes the input islands were a bit bigger than the extracted contour, thus some of the samples of the input islands did not fall into the extracted contour. --- src/libslic3r/SupportMaterial.cpp | 201 +++++++++++++++--------------- 1 file changed, 99 insertions(+), 102 deletions(-) diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 9caac5769..95d65f224 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -665,24 +665,22 @@ Polygons collect_slices_outer(const Layer &layer) class SupportGridPattern { public: - // Achtung! The support_polygons need to be trimmed by trimming_polygons, otherwise - // the selection by island_samples (see the island_samples() method) will not work! SupportGridPattern( // Support islands, to be stretched into a grid. Already trimmed with min(lower_layer_offset, m_gap_xy) - const Polygons &support_polygons, + const Polygons *support_polygons, // Trimming polygons, to trim the stretched support islands. support_polygons were already trimmed with trimming_polygons. - const Polygons &trimming_polygons, + const Polygons *trimming_polygons, // Grid spacing, given by "support_material_spacing" + m_support_material_flow.spacing() coordf_t support_spacing, coordf_t support_angle, coordf_t line_spacing) : - m_support_polygons(&support_polygons), m_trimming_polygons(&trimming_polygons), + m_support_polygons(support_polygons), m_trimming_polygons(trimming_polygons), m_support_spacing(support_spacing), m_support_angle(support_angle) { if (m_support_angle != 0.) { // Create a copy of the rotated contours. - m_support_polygons_rotated = support_polygons; - m_trimming_polygons_rotated = trimming_polygons; + m_support_polygons_rotated = *support_polygons; + m_trimming_polygons_rotated = *trimming_polygons; m_support_polygons = &m_support_polygons_rotated; m_trimming_polygons = &m_trimming_polygons_rotated; polygons_rotate(m_support_polygons_rotated, - support_angle); @@ -696,10 +694,6 @@ public: // Align the bounding box with the sparse support grid. bbox.align_to_grid(grid_resolution); - // Sample a single point per input support polygon, keep it as a reference to maintain corresponding - // polygons if ever these polygons get split into parts by the trimming polygons. - m_island_samples = island_samples(*m_support_polygons); - #ifdef SUPPORT_USE_AGG_RASTERIZER m_bbox = bbox; // Oversample the grid to avoid leaking of supports through or around the object walls. @@ -755,29 +749,46 @@ public: // and trim the extracted polygons by trimming_polygons. // Trimming by the trimming_polygons may split the extracted polygons into pieces. // Remove all the pieces, which do not contain any of the island_samples. - Polygons extract_support(const coord_t offset_in_grid, bool fill_holes) + Polygons extract_support(const coord_t offset_in_grid, bool fill_holes +#ifdef SLIC3R_DEBUG + , const char *step_name, int iRun, size_t layer_id, double print_z +#endif + ) { #ifdef SUPPORT_USE_AGG_RASTERIZER Polygons support_polygons_simplified = contours_simplified(m_grid_size, m_pixel_size, m_bbox.min, m_grid2, offset_in_grid, fill_holes); #else // SUPPORT_USE_AGG_RASTERIZER - // Generate islands, so each island may be tested for overlap with m_island_samples. + // Generate islands, so each island may be tested for overlap with island_samples. assert(std::abs(2 * offset_in_grid) < m_grid.resolution()); Polygons support_polygons_simplified = m_grid.contours_simplified(offset_in_grid, fill_holes); #endif // SUPPORT_USE_AGG_RASTERIZER ExPolygons islands = diff_ex(support_polygons_simplified, *m_trimming_polygons, false); - // Extract polygons, which contain some of the m_island_samples. + // Extract polygons, which contain some of the island_samples. Polygons out; #if 0 out = to_polygons(std::move(islands)); #else + + // Sample a single point per input support polygon, keep it as a reference to maintain corresponding + // polygons if ever these polygons get split into parts by the trimming polygons. + // As offset_in_grid may be negative, m_support_polygons may stick slightly outside of islands. + // Trim ti with islands. + Points samples = island_samples( + offset_in_grid > 0 ? + // Expanding, thus m_support_polygons are all inside islands. + union_ex(*m_support_polygons) : + // Shrinking, thus m_support_polygons may be trimmed a tiny bit by islands. + intersection_ex(*m_support_polygons, to_polygons(islands))); + + std::vector> samples_inside; for (ExPolygon &island : islands) { BoundingBox bbox = get_extents(island.contour); // Samples are sorted lexicographically. - auto it_lower = std::lower_bound(m_island_samples.begin(), m_island_samples.end(), Point(bbox.min - Point(1, 1))); - auto it_upper = std::upper_bound(m_island_samples.begin(), m_island_samples.end(), Point(bbox.max + Point(1, 1))); - std::vector> samples_inside; + auto it_lower = std::lower_bound(samples.begin(), samples.end(), Point(bbox.min - Point(1, 1))); + auto it_upper = std::upper_bound(samples.begin(), samples.end(), Point(bbox.max + Point(1, 1))); + samples_inside.clear(); for (auto it = it_lower; it != it_upper; ++ it) if (bbox.contains(*it)) samples_inside.push_back(std::make_pair(*it, false)); @@ -811,9 +822,7 @@ public: } #endif - #ifdef SLIC3R_DEBUG - static int iRun = 0; - ++iRun; +#ifdef SLIC3R_DEBUG BoundingBox bbox = get_extents(*m_trimming_polygons); if (! islands.empty()) bbox.merge(get_extents(islands)); @@ -821,7 +830,7 @@ public: bbox.merge(get_extents(out)); if (!support_polygons_simplified.empty()) bbox.merge(get_extents(support_polygons_simplified)); - SVG svg(debug_out_path("extract_support_from_grid_trimmed-%d.svg", iRun).c_str(), bbox); + SVG svg(debug_out_path("extract_support_from_grid_trimmed-%s-%d-%d-%lf.svg", step_name, iRun, layer_id, print_z).c_str(), bbox); svg.draw(union_ex(support_polygons_simplified), "gray", 0.25f); svg.draw(islands, "red", 0.5f); svg.draw(union_ex(out), "green", 0.5f); @@ -829,10 +838,10 @@ public: svg.draw_outline(islands, "red", "red", scale_(0.05)); svg.draw_outline(union_ex(out), "green", "green", scale_(0.05)); svg.draw_outline(union_ex(*m_support_polygons), "blue", "blue", scale_(0.05)); - for (const Point &pt : m_island_samples) + for (const Point &pt : samples) svg.draw(pt, "black", coord_t(scale_(0.15))); svg.Close(); - #endif /* SLIC3R_DEBUG */ +#endif /* SLIC3R_DEBUG */ if (m_support_angle != 0.) polygons_rotate(out, m_support_angle); @@ -940,9 +949,6 @@ public: m_grid.set_bbox(bbox); m_grid.create(*m_support_polygons, grid_resolution); m_grid.calculate_sdf(); - // Sample a single point per input support polygon, keep it as a reference to maintain corresponding - // polygons if ever these polygons get split into parts by the trimming polygons. - m_island_samples = island_samples(*m_support_polygons); return true; } @@ -1075,11 +1081,6 @@ private: return pts; } - static Points island_samples(const Polygons &polygons) - { - return island_samples(union_ex(polygons)); - } - const Polygons *m_support_polygons; const Polygons *m_trimming_polygons; Polygons m_support_polygons_rotated; @@ -1098,10 +1099,6 @@ private: Slic3r::EdgeGrid::Grid m_grid; #endif // SUPPORT_USE_AGG_RASTERIZER - // Internal sample points of supporting expolygons. These internal points are used to pick regions corresponding - // to the initial supporting regions, after these regions werre grown and possibly split to many by the trimming polygons. - Points m_island_samples; - #ifdef SLIC3R_DEBUG // support for deserialization of m_support_polygons, m_trimming_polygons Polygons m_support_polygons_deserialized; @@ -1631,48 +1628,57 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ } } - // Achtung! The contact_polygons need to be trimmed by slices_margin_cached, otherwise - // the selection by island_samples (see the SupportGridPattern::island_samples() method) will not work! SupportGridPattern support_grid_pattern( // Support islands, to be stretched into a grid. - contact_polygons, + &contact_polygons, // Trimming polygons, to trim the stretched support islands. - slices_margin_cached, + &slices_margin_cached, // Grid resolution. m_object_config->support_material_spacing.value + m_support_material_flow.spacing(), Geometry::deg2rad(m_object_config->support_material_angle.value), m_support_material_flow.spacing()); // 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells. - new_layer.contact_polygons = new Polygons(support_grid_pattern.extract_support(-3, true)); + new_layer.contact_polygons = new Polygons(support_grid_pattern.extract_support(-3, true + #ifdef SLIC3R_DEBUG + , "top_contact_polygons", iRun, layer_id, layer.print_z + #endif // SLIC3R_DEBUG + )); // 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra. if (layer_id == 0 || m_slicing_params.soluble_interface) { // if (no_interface_offset == 0.f) { - new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, true); + new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, true + #ifdef SLIC3R_DEBUG + , "top_contact_polygons2", iRun, layer_id, layer.print_z + #endif // SLIC3R_DEBUG + ); } else { // Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions. Polygons dense_interface_polygons = diff(overhang_polygons, offset2(lower_layer_polygons, - no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS)); if (! dense_interface_polygons.empty()) { dense_interface_polygons = - // Achtung! The dense_interface_polygons need to be trimmed by slices_margin_cached, otherwise - // the selection by island_samples (see the SupportGridPattern::island_samples() method) will not work! diff( // Regularize the contour. offset(dense_interface_polygons, no_interface_offset * 0.1f), slices_margin_cached); + // Support islands, to be stretched into a grid. + //FIXME The regularization of dense_interface_polygons above may stretch dense_interface_polygons outside of the contact polygons, + // thus some dense interface areas may not get supported. Trim the excess with contact_polygons at the following line. + // See for example GH #4874. + Polygons dense_interface_polygons_trimmed = intersection(dense_interface_polygons, *new_layer.contact_polygons); SupportGridPattern support_grid_pattern( - // Support islands, to be stretched into a grid. - //FIXME The regularization of dense_interface_polygons above may stretch dense_interface_polygons outside of the contact polygons, - // thus some dense interface areas may not get supported. Trim the excess with contact_polygons at the following line. - // See for example GH #4874. - intersection(dense_interface_polygons, *new_layer.contact_polygons), + &dense_interface_polygons_trimmed, // Trimming polygons, to trim the stretched support islands. - slices_margin_cached, + &slices_margin_cached, // Grid resolution. m_object_config->support_material_spacing.value + m_support_material_flow.spacing(), Geometry::deg2rad(m_object_config->support_material_angle.value), m_support_material_flow.spacing()); - new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, false); + new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, false + #ifdef SLIC3R_DEBUG + , "top_contact_polygons3", iRun, layer_id, layer.print_z + #endif // SLIC3R_DEBUG + ); #ifdef SLIC3R_DEBUG SVG::export_expolygons(debug_out_path("support-top-contacts-final0-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z), { { { union_ex(lower_layer_polygons, false) }, { "lower_layer_polygons", "gray", 0.2f } }, @@ -1848,17 +1854,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta ] { Polygons top = collect_region_slices_by_type(layer, stTop); #ifdef SLIC3R_DEBUG - { - BoundingBox bbox = get_extents(projection_raw); - bbox.merge(get_extents(top)); - ::Slic3r::SVG svg(debug_out_path("support-bottom-layers-raw-%d-%lf.svg", iRun, layer.print_z), bbox); - svg.draw(union_ex(top, false), "blue", 0.5f); - svg.draw(union_ex(projection_raw, true), "red", 0.5f); - svg.draw_outline(union_ex(projection_raw, true), "red", "blue", scale_(0.1f)); - svg.draw(layer.lslices, "green", 0.5f); - svg.draw(union_ex(polygons_new, true), "magenta", 0.5f); - svg.draw_outline(union_ex(polygons_new, true), "magenta", "magenta", scale_(0.1f)); - } + SVG::export_expolygons(debug_out_path("support-bottom-layers-raw-%d-%lf.svg", iRun, layer.print_z), + { { { union_ex(top, false) }, { "top", "blue", 0.5f } }, + { { union_ex(projection_raw, true) }, { "projection_raw", "magenta", 0.5f } }, + { layer.lslices, { "layer.lslices", "green", 0.5f } }, + { { union_ex(polygons_new, true) }, { "polygons_new", "red", "black", "", scaled(0.1f), 0.5f } } }); #endif /* SLIC3R_DEBUG */ // Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any @@ -1929,16 +1929,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta if (layer_above.print_z > layer_new.print_z - EPSILON) break; if (! layer_support_areas[layer_id_above].empty()) { -#ifdef SLIC3R_DEBUG - { - BoundingBox bbox = get_extents(touching); - bbox.merge(get_extents(layer_support_areas[layer_id_above])); - ::Slic3r::SVG svg(debug_out_path("support-support-areas-raw-before-trimming-%d-with-%f-%lf.svg", iRun, layer.print_z, layer_above.print_z), bbox); - svg.draw(union_ex(touching, false), "blue", 0.5f); - svg.draw(union_ex(layer_support_areas[layer_id_above], true), "red", 0.5f); - svg.draw_outline(union_ex(layer_support_areas[layer_id_above], true), "red", "blue", scale_(0.1f)); - } -#endif /* SLIC3R_DEBUG */ + #ifdef SLIC3R_DEBUG + SVG::export_expolygons(debug_out_path("support-support-areas-raw-before-trimming-%d-with-%f-%lf.svg", iRun, layer.print_z, layer_above.print_z), + { { { union_ex(touching, false) }, { "touching", "blue", 0.5f } }, + { { union_ex(layer_support_areas[layer_id_above], true) }, { "above", "red", "black", "", scaled(0.1f), 0.5f } } }); + #endif /* SLIC3R_DEBUG */ layer_support_areas[layer_id_above] = diff(layer_support_areas[layer_id_above], touching); #ifdef SLIC3R_DEBUG Slic3r::SVG::export_expolygons( @@ -1952,33 +1947,32 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta }); Polygons &layer_support_area = layer_support_areas[layer_id]; - task_group.run([this, &projection, &projection_raw, &layer, &layer_support_area] { + task_group.run([this, &projection, &projection_raw, &layer, &layer_support_area +#ifdef SLIC3R_DEBUG + , layer_id +#endif /* SLIC3R_DEBUG */ + ] { // Remove the areas that touched from the projection that will continue on next, lower, top surfaces. // Polygons trimming = union_(to_polygons(layer.slices), touching, true); Polygons trimming = offset(layer.lslices, float(SCALED_EPSILON)); projection = diff(projection_raw, trimming, false); - #ifdef SLIC3R_DEBUG - { - BoundingBox bbox = get_extents(projection_raw); - bbox.merge(get_extents(trimming)); - ::Slic3r::SVG svg(debug_out_path("support-support-areas-raw-%d-%lf.svg", iRun, layer.print_z), bbox); - svg.draw(union_ex(trimming, false), "blue", 0.5f); - svg.draw(union_ex(projection, true), "red", 0.5f); - svg.draw_outline(union_ex(projection, true), "red", "blue", scale_(0.1f)); - } - #endif /* SLIC3R_DEBUG */ + #ifdef SLIC3R_DEBUG + SVG::export_expolygons(debug_out_path("support-support-areas-raw-%d-%lf.svg", iRun, layer.print_z), + { { { union_ex(trimming, false) }, { "trimming", "blue", 0.5f } }, + { { union_ex(projection, true) }, { "projection", "red", "black", "", scaled(0.1f), 0.5f } } }); + #endif /* SLIC3R_DEBUG */ remove_sticks(projection); remove_degenerate(projection); #ifdef SLIC3R_DEBUG - Slic3r::SVG::export_expolygons( - debug_out_path("support-support-areas-raw-cleaned-%d-%lf.svg", iRun, layer.print_z), - union_ex(projection, false)); + SVG::export_expolygons(debug_out_path("support-support-areas-raw-cleaned-%d-%lf.svg", iRun, layer.print_z), + { { { union_ex(trimming, false) }, { "trimming", "blue", 0.5f } }, + { { union_ex(projection, false) }, { "projection", "red", "black", "", scaled(0.1f), 0.5f } } }); #endif /* SLIC3R_DEBUG */ SupportGridPattern support_grid_pattern( // Support islands, to be stretched into a grid. - projection, + &projection, // Trimming polygons, to trim the stretched support islands. - trimming, + &trimming, // Grid spacing. m_object_config->support_material_spacing.value + m_support_material_flow.spacing(), Geometry::deg2rad(m_object_config->support_material_angle.value), @@ -1988,10 +1982,14 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta // to allow a placement of suppot zig-zag snake along the grid lines. task_group_inner.run([this, &support_grid_pattern, &layer_support_area #ifdef SLIC3R_DEBUG - , &layer + , &layer, layer_id #endif /* SLIC3R_DEBUG */ ] { - layer_support_area = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 25, true); + layer_support_area = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 25, true + #ifdef SLIC3R_DEBUG + , "support_area", iRun, layer_id, layer.print_z + #endif // SLIC3R_DEBUG + ); #ifdef SLIC3R_DEBUG Slic3r::SVG::export_expolygons( debug_out_path("support-layer_support_area-gridded-%d-%lf.svg", iRun, layer.print_z), @@ -2002,26 +2000,25 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta Polygons projection_new; task_group_inner.run([&projection_new, &support_grid_pattern #ifdef SLIC3R_DEBUG - , &layer, &projection, &trimming + , &layer, layer_id, &projection, &trimming #endif /* SLIC3R_DEBUG */ ] { - projection_new = support_grid_pattern.extract_support(-5, true); + projection_new = support_grid_pattern.extract_support(-5, true + #ifdef SLIC3R_DEBUG + , "support_projection", iRun, layer_id, layer.print_z + #endif // SLIC3R_DEBUG + ); #ifdef SLIC3R_DEBUG Slic3r::SVG::export_expolygons( debug_out_path("support-projection_new-gridded-%d-%lf.svg", iRun, layer.print_z), union_ex(projection_new, false)); #endif /* SLIC3R_DEBUG */ -#ifdef SLIC3R_DEBUG - { - BoundingBox bbox = get_extents(projection); - bbox.merge(get_extents(projection_new)); - bbox.merge(get_extents(trimming)); - ::Slic3r::SVG svg(debug_out_path("support-projection_new-gridded-%d-%lf.svg", iRun, layer.print_z), bbox); - svg.draw(union_ex(trimming, false), "gray", 0.5f); - svg.draw(union_ex(projection_new, false), "red", 0.5f); - svg.draw(union_ex(projection, false), "blue", 0.5f); - } -#endif /* SLIC3R_DEBUG */ + #ifdef SLIC3R_DEBUG + SVG::export_expolygons(debug_out_path("support-projection_new-gridded-%d-%lf.svg", iRun, layer.print_z), + { { { union_ex(trimming, false) }, { "trimming", "gray", 0.5f } }, + { { union_ex(projection, true) }, { "projection", "blue", 0.5f } }, + { { union_ex(projection_new, true) }, { "projection_new", "red", "black", "", scaled(0.1f), 0.5f } } }); + #endif /* SLIC3R_DEBUG */ }); task_group_inner.wait(); projection = std::move(projection_new); From d2ec2b33f95cf90763db53176c7682eb2ab0fae6 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 17 Mar 2021 15:40:59 +0100 Subject: [PATCH 20/32] missing override keyword missing override keyword --- src/slic3r/GUI/NotificationManager.hpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index f709a2a7d..83d94d907 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -319,7 +319,7 @@ private: void set_large(bool l); bool get_large() { return m_is_large; } void set_print_info(const std::string &info); - virtual void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width) + virtual void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width) override { // This notification is always hidden if !large (means side bar is collapsed) if (!get_large() && !is_finished()) @@ -348,9 +348,9 @@ private: { public: PlaterWarningNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler) : PopNotification(n, id_provider, evt_handler) {} - virtual void close() { if(is_finished()) return; m_state = EState::Hidden; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); } - void real_close() { m_state = EState::ClosePending; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); } - void show() { m_state = EState::Unknown; } + virtual void close() override { if(is_finished()) return; m_state = EState::Hidden; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); } + void real_close() { m_state = EState::ClosePending; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); } + void show() { m_state = EState::Unknown; } }; @@ -365,10 +365,10 @@ private: virtual void count_spaces() override; virtual void render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, - const float win_pos_x, const float win_pos_y); + const float win_pos_x, const float win_pos_y) override; virtual void render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, - const float win_pos_x, const float win_pos_y); + const float win_pos_x, const float win_pos_y) ; virtual void render_cancel_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) @@ -400,16 +400,16 @@ private: m_has_cancel_button = true; } static std::string get_upload_job_text(int id, const std::string& filename, const std::string& host) { return "[" + std::to_string(id) + "] " + filename + " -> " + host; } - virtual void set_percentage(float percent); + virtual void set_percentage(float percent) override; void cancel() { m_uj_state = UploadJobState::PB_CANCELLED; m_has_cancel_button = false; } void error() { m_uj_state = UploadJobState::PB_ERROR; m_has_cancel_button = false; } protected: virtual void render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, - const float win_pos_x, const float win_pos_y); + const float win_pos_x, const float win_pos_y) override; virtual void render_cancel_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, - const float win_pos_x, const float win_pos_y); + const float win_pos_x, const float win_pos_y) override; // Identifies job in cancel callback int m_job_id; // Size of uploaded size to be displayed in MB From 8c075a878fd6a834cb6e558a2f765e172952736d Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 17 Mar 2021 15:43:41 +0100 Subject: [PATCH 21/32] warning fix --- src/slic3r/GUI/PrintHostDialogs.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index afbc604bc..f69db2ea3 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -224,7 +224,7 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent) std::vector size; SetSize(load_user_data(UDT_SIZE, size) ? wxSize(size[0] * em, size[1] * em) : wxSize(HEIGHT * em, WIDTH * em)); - Bind(wxEVT_SIZE, [this, em](wxSizeEvent& evt) { + Bind(wxEVT_SIZE, [this](wxSizeEvent& evt) { OnSize(evt); save_user_data(UDT_SIZE | UDT_POSITION | UDT_COLS); }); @@ -233,7 +233,7 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent) if (load_user_data(UDT_POSITION, pos)) SetPosition(wxPoint(pos[0], pos[1])); - Bind(wxEVT_MOVE, [this, em](wxMoveEvent& evt) { + Bind(wxEVT_MOVE, [this](wxMoveEvent& evt) { save_user_data(UDT_SIZE | UDT_POSITION | UDT_COLS); }); From a6cf840540624edd27749fffb9dcb7a83d2ca777 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 17 Mar 2021 17:10:19 +0100 Subject: [PATCH 22/32] Auto color change improvements: Show info notification just ones, if there is new print --- src/slic3r/GUI/DoubleSlider.cpp | 16 ++++++++++++++++ src/slic3r/GUI/DoubleSlider.hpp | 3 +++ src/slic3r/GUI/GUI_Preview.cpp | 9 +++++---- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 00b9c2e29..77cc5bec7 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -424,6 +424,22 @@ void Control::SetExtruderColors( const std::vector& extruder_colors m_extruder_colors = extruder_colors; } +bool Control::IsNewPrint() +{ + if (GUI::wxGetApp().plater()->printer_technology() == ptSLA) + return false; + const Print& print = GUI::wxGetApp().plater()->fff_print(); + std::string idxs; + for (auto object : print.objects()) + idxs += std::to_string(object->id().id) + "_"; + + if (idxs == m_print_obj_idxs) + return false; + + m_print_obj_idxs = idxs; + return true; +} + void Control::get_lower_and_higher_position(int& lower_pos, int& higher_pos) { const double step = get_scroll_step(); diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index 52f7be629..49588f4f0 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -233,6 +233,8 @@ public: void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder); void SetExtruderColors(const std::vector& extruder_colors); + bool IsNewPrint(); + void set_render_as_disabled(bool value) { m_render_as_disabled = value; } bool is_rendering_as_disabled() const { return m_render_as_disabled; } @@ -395,6 +397,7 @@ private: TickCodeInfo m_ticks; std::vector m_layers_times; std::vector m_extruder_colors; + std::string m_print_obj_idxs; #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER std::vector m_alternate_values; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 9f9f20ffb..8bda3b8e6 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -1,3 +1,4 @@ +//#include "stdlib.h" #include "libslic3r/libslic3r.h" #include "libslic3r/Layer.hpp" #include "GUI_Preview.hpp" @@ -642,10 +643,10 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee m_layers_slider->SetLayersTimes(m_gcode_result->time_statistics.modes.front().layers_times); // Suggest the auto color change, if model looks like sign - if (ticks_info_from_model.gcodes.empty()) + if (m_layers_slider->IsNewPrint()) { NotificationManager* notif_mngr = wxGetApp().plater()->get_notification_manager(); - notif_mngr->close_notification_of_type(NotificationType::SignDetected); +// notif_mngr->close_notification_of_type(NotificationType::SignDetected); const Print& print = wxGetApp().plater()->fff_print(); double delta_area = scale_(scale_(25)); // equal to 25 mm2 @@ -658,8 +659,8 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee continue; const ExPolygons& bottom = object->get_layer(0)->lslices; - if (bottom.size() > 1 || !bottom[0].holes.empty()) - continue; + //if (bottom.size() > 1 || !bottom[0].holes.empty()) + // continue; double bottom_area = area(bottom); int i; From 7c7c3553763bc85a4a45226a03ca5dcb8ad7662d Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 18 Mar 2021 12:42:42 +0100 Subject: [PATCH 23/32] Notification manager - apply_in_preview function --- src/slic3r/GUI/GUI_Preview.cpp | 3 +-- src/slic3r/GUI/NotificationManager.cpp | 2 +- src/slic3r/GUI/NotificationManager.hpp | 4 +++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 8bda3b8e6..224ccfc36 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -677,12 +677,11 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee _u8L("NOTE:") + "\n" + _u8L("Sliced object looks like the sign") + "\n", _u8L("Apply auto color change to print"), [this, notif_mngr](wxEvtHandler*) { - notif_mngr->close_notification_of_type(NotificationType::SignDetected); m_layers_slider->auto_color_change(); return true; }); - notif_mngr->set_in_preview(true); + notif_mngr->apply_in_preview(); break; } diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 358f709a7..e83a0014c 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1014,7 +1014,7 @@ void NotificationManager::push_plater_warning_notification(const std::string& te auto notification = std::make_unique(data, m_id_provider, m_evt_handler); push_notification_data(std::move(notification), 0); // dissaper if in preview - set_in_preview(m_in_preview); + apply_in_preview(); } void NotificationManager::close_plater_warning_notification(const std::string& text) diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 83d94d907..4f3900aeb 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -159,8 +159,10 @@ public: void render_notifications(GLCanvas3D& canvas, float overlay_width); // finds and closes all notifications of given type void close_notification_of_type(const NotificationType type); - // Which view is active? Plater or G-code preview? Hide warnings in G-code preview. + // Hides warnings in G-code preview. Should be called from plater only when 3d view/ preview is changed void set_in_preview(bool preview); + // Calls set_in_preview to apply appearing or disappearing of some notificatons; + void apply_in_preview() { set_in_preview(m_in_preview); } // Move to left to avoid colision with variable layer height gizmo. void set_move_from_overlay(bool move) { m_move_from_overlay = move; } // perform update_state on each notification and ask for more frames if needed, return true for render needed From bfd87dc93abd76dd0757e57cfe11be96e0f60bc8 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 18 Mar 2021 13:21:11 +0100 Subject: [PATCH 24/32] cleanup --- src/slic3r/GUI/GUI_Preview.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 224ccfc36..244ef52d3 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -646,7 +646,6 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee if (m_layers_slider->IsNewPrint()) { NotificationManager* notif_mngr = wxGetApp().plater()->get_notification_manager(); -// notif_mngr->close_notification_of_type(NotificationType::SignDetected); const Print& print = wxGetApp().plater()->fff_print(); double delta_area = scale_(scale_(25)); // equal to 25 mm2 @@ -676,7 +675,7 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee NotificationType::SignDetected, NotificationManager::NotificationLevel::RegularNotification, _u8L("NOTE:") + "\n" + _u8L("Sliced object looks like the sign") + "\n", _u8L("Apply auto color change to print"), - [this, notif_mngr](wxEvtHandler*) { + [this](wxEvtHandler*) { m_layers_slider->auto_color_change(); return true; }); From eda19a7e56ed26a5a26f11bfa57b7dc9cc5ce2b0 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 18 Mar 2021 17:55:17 +0100 Subject: [PATCH 25/32] Fixed a crash after switch from Filament Settings Tab to some another, when "Custom G-code" category is active. --- src/slic3r/GUI/Tab.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 6f7a21fcc..33dc54444 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3841,7 +3841,7 @@ bool Tab::validate_custom_gcodes() assert(opt_group->opt_map().size() == 1); std::string key = opt_group->opt_map().begin()->first; std::string value = boost::any_cast(opt_group->get_value(key)); - std::string config_value = m_config->opt_string(key); + std::string config_value = m_type == Preset::TYPE_FILAMENT ? m_config->opt_string(key, unsigned int(0)) : m_config->opt_string(key); valid &= validate_custom_gcode(opt_group->title, value); Field* field = opt_group->get_field(key); TextCtrl* text_ctrl = dynamic_cast(field); From 995512f2807d1e026533c814f6fabf655a7610ab Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 18 Mar 2021 18:51:08 +0100 Subject: [PATCH 26/32] Fix of #6232 - Layer preview number scale and print time problem. Problem description: When "Print Settings -> Multiple Extruders -> No sparse layer" is enabled, then "Smart" Wipe Tower is used for wiping. As a result, each layer with tool changes is splited for min 3 parts: first tool, wiping, second tool ... But vertical slider wasn't respect to this case. --- src/slic3r/GUI/DoubleSlider.cpp | 78 ++++++++++++++++++++++++++++++--- src/slic3r/GUI/DoubleSlider.hpp | 4 ++ 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 77cc5bec7..9dc6c5c74 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -328,7 +328,12 @@ double Control::get_double_value(const SelectedSlider& selection) int Control::get_tick_from_value(double value) { - auto it = std::lower_bound(m_values.begin(), m_values.end(), value - epsilon()); + std::vector::iterator it; + if (m_is_smart_wipe_tower) + it = std::find_if(m_values.begin(), m_values.end(), + [value](const double & val) { return fabs(value - val) <= epsilon(); }); + else + it = std::lower_bound(m_values.begin(), m_values.end(), value - epsilon()); if (it == m_values.end()) return -1; @@ -391,6 +396,16 @@ void Control::SetLayersTimes(const std::vector& layers_times) m_layers_times[0] = layers_times[0]; for (size_t i = 1; i < layers_times.size(); i++) m_layers_times[i] = m_layers_times[i - 1] + layers_times[i]; + + // Erase duplicates values from m_values and save it to the m_layers_values + // They will be used for show the correct estimated time for MM print, when "No sparce layer" is enabled + // See https://github.com/prusa3d/PrusaSlicer/issues/6232 + m_is_smart_wipe_tower = m_values.size() != m_layers_times.size(); + if (m_is_smart_wipe_tower) { + m_layers_values = m_values; + sort(m_layers_values.begin(), m_layers_values.end()); + m_layers_values.erase(unique(m_layers_values.begin(), m_layers_values.end()), m_layers_values.end()); + } } void Control::SetLayersTimes(const std::vector& layers_times) @@ -511,6 +526,18 @@ void Control::render() } } +bool Control::is_wipe_tower_layer(int tick) const +{ + if (!m_is_smart_wipe_tower || tick >= (int)m_values.size()) + return false; + if (tick == 0 || (tick == (int)m_values.size() - 1 && m_values[tick] > m_values[tick - 1])) + return false; + if (m_values[tick - 1] == m_values[tick + 1] && m_values[tick] < m_values[tick + 1]) + return true; + + return false; +} + void Control::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end) { const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; @@ -522,6 +549,11 @@ void Control::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_ if (tick == 0) return; + if (is_wipe_tower_layer(tick)) { + m_rect_tick_action = wxRect(); + return; + } + wxBitmap* icon = m_focus == fiActionIcon ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp(); if (m_ticks.ticks.find(TickCode{tick}) != m_ticks.ticks.end()) icon = m_focus == fiActionIcon ? &m_bmp_del_tick_off.bmp() : &m_bmp_del_tick_on.bmp(); @@ -670,6 +702,21 @@ wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer if (value >= m_values.size()) return "ErrVal"; + // When "Print Settings -> Multiple Extruders -> No sparse layer" is enabled, then "Smart" Wipe Tower is used for wiping. + // As a result, each layer with tool changes is splited for min 3 parts: first tool, wiping, second tool ... + // So, vertical slider have to respect to this case. + // see https://github.com/prusa3d/PrusaSlicer/issues/6232. + // m_values contains data for all layer's parts, + // but m_layers_values contains just unique Z values. + // Use this function for correct conversion slider position to number of printed layer + auto get_layer_number = [this](int value) { + double layer_print_z = m_values[is_wipe_tower_layer(value) ? std::max(value - 1, 0) : value]; + auto it = std::lower_bound(m_layers_values.begin(), m_layers_values.end(), layer_print_z - epsilon()); + if (it == m_layers_values.end()) + return -1; + return int(it - m_layers_values.begin()); + }; + #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER if (m_draw_mode == dmSequentialGCodeView) { return (Slic3r::GUI::get_app_config()->get("seq_top_gcode_indices") == "1") ? @@ -682,15 +729,21 @@ wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer #endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER else { if (label_type == ltEstimatedTime) { - return (value < m_layers_times.size()) ? short_and_splitted_time(get_time_dhms(m_layers_times[value])) : ""; + if (m_is_smart_wipe_tower) { + int layer_number = get_layer_number(value); + return layer_number < 0 ? "" : short_and_splitted_time(get_time_dhms(m_layers_times[layer_number])); + } + return value < m_layers_times.size() ? short_and_splitted_time(get_time_dhms(m_layers_times[value])) : ""; } wxString str = m_values.empty() ? wxString::Format("%.*f", 2, m_label_koef * value) : wxString::Format("%.*f", 2, m_values[value]); if (label_type == ltHeight) return str; - if (label_type == ltHeightWithLayer) - return format_wxstr("%1%\n(%2%)", str, m_values.empty() ? value : value + 1); + if (label_type == ltHeightWithLayer) { + size_t layer_number = m_is_smart_wipe_tower ? (size_t)get_layer_number(value) : (m_values.empty() ? value : value + 1); + return format_wxstr("%1%\n(%2%)", str, layer_number); + } } return wxEmptyString; @@ -725,10 +778,17 @@ void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, LabelType l text_pos = wxPoint(std::max(2, pos.x - text_width - 1 - m_thumb_size.x), pos.y - 0.5 * text_height + 1); } + wxColour old_clr = dc.GetTextForeground(); + const wxPen& pen = is_wipe_tower_layer(tick) && (tick == m_lower_value || tick == m_higher_value) ? DARK_ORANGE_PEN : wxPen(old_clr); + dc.SetPen(pen); + dc.SetTextForeground(pen.GetColour()); + if (label_type == ltEstimatedTime) dc.DrawLabel(label, wxRect(text_pos, wxSize(text_width, text_height)), wxALIGN_RIGHT); else dc.DrawText(label, text_pos); + + dc.SetTextForeground(old_clr); } void Control::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const @@ -1291,6 +1351,8 @@ wxString Control::get_tooltip(int tick/*=-1*/) if (m_focus == fiColorBand) return m_mode != SingleExtruder ? "" : _L("Edit current color - Right click the colored slider segment"); + if (m_focus == fiSmartWipeTower) + return _L("This is wipe tower layer"); if (m_draw_mode == dmSlaPrint) return ""; // no drawn ticks and no tooltips for them in SlaPrinting mode @@ -1424,8 +1486,14 @@ void Control::OnMotion(wxMouseEvent& event) else if (is_point_in_rect(pos, m_rect_higher_thumb)) m_focus = fiHigherThumb; else { - m_focus = fiTick; tick = get_tick_near_point(pos); + if (tick < 0 && m_is_smart_wipe_tower) { + tick = get_value_from_position(pos); + m_focus = tick > 0 && is_wipe_tower_layer(tick) && (tick == m_lower_value || tick == m_higher_value) ? + fiSmartWipeTower : fiTick; + } + else + m_focus = fiTick; } m_moving_pos = pos; } diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index 49588f4f0..de50bb0a2 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -43,6 +43,7 @@ enum FocusedItem { fiActionIcon, fiLowerThumb, fiHigherThumb, + fiSmartWipeTower, fiTick }; @@ -305,6 +306,7 @@ protected: void correct_higher_value(); void move_current_thumb(const bool condition); void enter_window(wxMouseEvent& event, const bool enter); + bool is_wipe_tower_layer(int tick) const; private: @@ -368,6 +370,7 @@ private: bool m_is_focused = false; bool m_force_mode_apply = true; bool m_enable_action_icon = true; + bool m_is_smart_wipe_tower = false; //This flag indicates that for current print is used "smart" wipe tower (Print Settings->Multiple Extruders->No sparse layer is enabled) DrawMode m_draw_mode = dmRegular; @@ -396,6 +399,7 @@ private: std::vector m_values; TickCodeInfo m_ticks; std::vector m_layers_times; + std::vector m_layers_values; std::vector m_extruder_colors; std::string m_print_obj_idxs; From 4602f40813fa3404f93a3151aa41f6a4371324c9 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 19 Mar 2021 11:11:46 +0100 Subject: [PATCH 27/32] Next auto color change improvements --- src/slic3r/GUI/DoubleSlider.cpp | 7 +++++++ src/slic3r/GUI/GUI_Preview.cpp | 16 +++++++++------- src/slic3r/GUI/Tab.cpp | 2 +- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 9dc6c5c74..de7a944f2 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -2022,6 +2022,9 @@ void Control::auto_color_change() Layer* layer = object->get_layer(i); double cur_area = area(layer->lslices); + if (cur_area > prev_area) + break; + if (prev_area - cur_area > delta_area) { int tick = get_tick_from_value(layer->print_z); if (tick >= 0 && !m_ticks.has_tick(tick)) { @@ -2035,6 +2038,10 @@ void Control::auto_color_change() extruder = 1; } } + + // allow max 3 auto color changes + if (m_ticks.ticks.size() == 3) + break; } prev_area = cur_area; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 244ef52d3..0042c0702 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -645,23 +645,24 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee // Suggest the auto color change, if model looks like sign if (m_layers_slider->IsNewPrint()) { - NotificationManager* notif_mngr = wxGetApp().plater()->get_notification_manager(); - const Print& print = wxGetApp().plater()->fff_print(); double delta_area = scale_(scale_(25)); // equal to 25 mm2 //bool is_possible_auto_color_change = false; for (auto object : print.objects()) { + // bottom layer have to be a biggest, so control relation between bottom lazer and object size + const ExPolygons& bottom = object->get_layer(0)->lslices; + double bottom_area = area(bottom); + if (bottom_area < double(object->size().x()) * double(object->size().y())) + continue; + + // if it's sign, than object have not to be a too height double height = object->height(); coord_t longer_side = std::max(object->size().x(), object->size().y()); if (height / longer_side > 0.3) continue; - const ExPolygons& bottom = object->get_layer(0)->lslices; - //if (bottom.size() > 1 || !bottom[0].holes.empty()) - // continue; - - double bottom_area = area(bottom); + // at least 30% of object's height have to be a solid int i; for (i = 1; i < int(0.3 * object->layers().size()); i++) if (area(object->get_layer(1)->lslices) != bottom_area) @@ -671,6 +672,7 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee double top_area = area(object->get_layer(int(object->layers().size()) - 1)->lslices); if( bottom_area - top_area > delta_area) { + NotificationManager* notif_mngr = wxGetApp().plater()->get_notification_manager(); notif_mngr->push_notification( NotificationType::SignDetected, NotificationManager::NotificationLevel::RegularNotification, _u8L("NOTE:") + "\n" + _u8L("Sliced object looks like the sign") + "\n", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 33dc54444..6916c04a5 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1443,7 +1443,7 @@ void TabPrint::build() optgroup = page->new_optgroup(L("Fuzzy skin (experimental)")); Option option = optgroup->get_option("fuzzy_skin"); - option.opt.width = 30; +// option.opt.width = 30; optgroup->append_single_option_line(option); optgroup->append_single_option_line(optgroup->get_option("fuzzy_skin_thickness")); optgroup->append_single_option_line(optgroup->get_option("fuzzy_skin_point_dist")); From 9f09f03228478aa77549efec845717b06e46cbc3 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 19 Mar 2021 11:21:29 +0100 Subject: [PATCH 28/32] Refactoring of FDM support generator: 1) If "support on build plate only" is enabled, the support columns are newly trimmed to not land on top of an object. However this may make the column too small to be stable. 2) Support enforcers newly take precedence over "supports on build plate only" and over "don't support bridges". 3) Some refactoring of the support generator code for clarity: Reduced some of the worst spagetti offenders. Fixes Support generated even if support on build only activated #915 Fixes Bug: supports on build plate only #1340 Fixes Bottom interface layer is not generated , support on build plate only. (long open defect) #4199 Fixes option "supports on build plate only" does not work #3980 Fixes No support interface layers generated #1997 Fixes Feature Request: Option to combine results of 'support from build plate only' and 'support enforcers only' #2801 Fixes Support interface isn't generated: build plate only + blocked by model + support enforcer #3831 Fixes Support Enforcer don't create interface layers #5748 Fixes Support Enforcers Don't Have Top Loops/Raft #1870 Fixes Don't cancel support enforcers with "don't support bridges" #5105 --- src/libslic3r/GCode.hpp | 2 +- src/libslic3r/Layer.hpp | 5 +- src/libslic3r/LayerRegion.cpp | 1 + src/libslic3r/SupportMaterial.cpp | 1714 ++++++++++++++++------------- src/libslic3r/SupportMaterial.hpp | 105 +- 5 files changed, 1024 insertions(+), 803 deletions(-) diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 51621ed40..9ecabd47d 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -373,7 +373,7 @@ private: void print_machine_envelope(FILE *file, Print &print); void _print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); void _print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); - // this flag triggers first layer speeds + // On the first printing layer. This flag triggers first layer speeds. bool on_first_layer() const { return m_layer != nullptr && m_layer->id() == 0; } friend ObjectByExtruder& object_by_extruder( diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 8e2348530..87296f8f1 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -100,6 +100,7 @@ typedef std::vector LayerRegionPtrs; class Layer { public: + // Sequential index of this layer in PrintObject::m_layers, offsetted by the number of raft layers. size_t id() const { return m_id; } void set_id(size_t id) { m_id = id; } PrintObject* object() { return m_object; } @@ -115,7 +116,7 @@ public: // Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry // (with possibly differing extruder ID and slicing parameters) and merged. - // For the first layer, if the ELephant foot compensation is applied, this lslice is uncompensated, therefore + // For the first layer, if the Elephant foot compensation is applied, this lslice is uncompensated, therefore // it includes the Elephant foot effect, thus it corresponds to the shape of the printed 1st layer. // These lslices aka islands are chained by the shortest traverse distance and this traversal // order will be applied by the G-code generator to the extrusions fitting into these lslices. @@ -170,7 +171,7 @@ protected: virtual ~Layer(); private: - // sequential number of layer, 0-based + // Sequential index of layer, 0-based, offsetted by number of raft layers. size_t m_id; PrintObject *m_object; LayerRegionPtrs m_regions; diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 1bca95ca3..f6c0c9c7a 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -74,6 +74,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec const PrintRegionConfig ®ion_config = this->region()->config(); // This needs to be in sync with PrintObject::_slice() slicing_mode_normal_below_layer! bool spiral_vase = print_config.spiral_vase && + //FIXME account for raft layers. (this->layer()->id() >= size_t(region_config.bottom_solid_layers.value) && this->layer()->print_z >= region_config.bottom_solid_min_thickness - EPSILON); diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 95d65f224..d721e12ca 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -324,20 +324,21 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object m_object (object), m_print_config (&object->print()->config()), m_object_config (&object->config()), - m_slicing_params (slicing_params), - m_first_layer_flow (support_material_1st_layer_flow(object, float(slicing_params.first_print_layer_height))), - m_support_material_flow (support_material_flow(object, float(slicing_params.layer_height))), - m_support_material_interface_flow(support_material_interface_flow(object, float(slicing_params.layer_height))), - m_support_layer_height_min(0.01) + m_slicing_params (slicing_params) { - // Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um. - m_support_layer_height_min = 1000000.; - for (auto lh : m_print_config->min_layer_height.values) - m_support_layer_height_min = std::min(m_support_layer_height_min, std::max(0.01, lh)); + m_support_params.first_layer_flow = support_material_1st_layer_flow(object, float(slicing_params.first_print_layer_height)); + m_support_params.support_material_flow = support_material_flow(object, float(slicing_params.layer_height)); + m_support_params.support_material_interface_flow = support_material_interface_flow(object, float(slicing_params.layer_height)); + m_support_params.support_layer_height_min = 0.01; - if (m_slicing_params.soluble_interface) { + // Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um. + m_support_params.support_layer_height_min = 1000000.; + for (auto lh : m_print_config->min_layer_height.values) + m_support_params.support_layer_height_min = std::min(m_support_params.support_layer_height_min, std::max(0.01, lh)); + + if (m_object_config->support_material_interface_layers.value == 0) { // No interface layers allowed, print everything with the base support pattern. - m_support_material_interface_flow = m_support_material_flow; + m_support_params.support_material_interface_flow = m_support_params.support_material_flow; } // Evaluate the XY gap between the object outer perimeters and the support structures. @@ -352,21 +353,21 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(*object, frExternalPerimeter, slicing_params.layer_height).width())); bridge_flow_ratio += region.config().bridge_flow_ratio; } - m_gap_xy = m_object_config->support_material_xy_spacing.get_abs_value(external_perimeter_width); + m_support_params.gap_xy = m_object_config->support_material_xy_spacing.get_abs_value(external_perimeter_width); bridge_flow_ratio /= num_nonempty_regions; - m_support_material_bottom_interface_flow = m_slicing_params.soluble_interface || ! m_object_config->thick_bridges ? - m_support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) : - Flow::bridging_flow(bridge_flow_ratio * m_support_material_interface_flow.nozzle_diameter(), m_support_material_interface_flow.nozzle_diameter()); + m_support_params.support_material_bottom_interface_flow = m_slicing_params.soluble_interface || ! m_object_config->thick_bridges ? + m_support_params.support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) : + Flow::bridging_flow(bridge_flow_ratio * m_support_params.support_material_interface_flow.nozzle_diameter(), m_support_params.support_material_interface_flow.nozzle_diameter()); - m_can_merge_support_regions = m_object_config->support_material_extruder.value == m_object_config->support_material_interface_extruder.value; - if (! m_can_merge_support_regions && (m_object_config->support_material_extruder.value == 0 || m_object_config->support_material_interface_extruder.value == 0)) { + m_support_params.can_merge_support_regions = m_object_config->support_material_extruder.value == m_object_config->support_material_interface_extruder.value; + if (!m_support_params.can_merge_support_regions && (m_object_config->support_material_extruder.value == 0 || m_object_config->support_material_interface_extruder.value == 0)) { // One of the support extruders is of "don't care" type. auto object_extruders = m_object->print()->object_extruders(); if (object_extruders.size() == 1 && *object_extruders.begin() == std::max(m_object_config->support_material_extruder.value, m_object_config->support_material_interface_extruder.value)) // Object is printed with the same extruder as the support. - m_can_merge_support_regions = true; + m_support_params.can_merge_support_regions = true; } } @@ -412,13 +413,15 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) BOOST_LOG_TRIVIAL(info) << "Support generator - Creating top contacts"; + std::vector buildplate_covered = this->buildplate_covered(object); + // Determine the top contact surfaces of the support, defined as: // contact = overhangs - clearance + margin // This method is responsible for identifying what contact surfaces // should the support material expose to the object in order to guarantee // that it will be effective, regardless of how it's built below. // If raft is to be generated, the 1st top_contact layer will contain the 1st object layer silhouette without holes. - MyLayersPtr top_contacts = this->top_contact_layers(object, layer_storage); + MyLayersPtr top_contacts = this->top_contact_layers(object, buildplate_covered, layer_storage); if (top_contacts.empty()) // Nothing is supported, no supports are generated. return; @@ -440,8 +443,8 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // may get merged and trimmed by this->generate_base_layers() if the support layers are not synchronized with object layers. std::vector layer_support_areas; MyLayersPtr bottom_contacts = this->bottom_contact_layers_and_layer_support_areas( - object, top_contacts, layer_storage, - layer_support_areas); + object, top_contacts, buildplate_covered, + layer_storage, layer_support_areas); #ifdef SLIC3R_DEBUG for (size_t layer_id = 0; layer_id < object.layers().size(); ++ layer_id) @@ -460,7 +463,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) MyLayersPtr intermediate_layers = this->raft_and_intermediate_support_layers( object, bottom_contacts, top_contacts, layer_storage); - this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.gap_support_object, m_slicing_params.gap_object_support, m_gap_xy); + this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.gap_support_object, m_slicing_params.gap_object_support, m_support_params.gap_xy); #ifdef SLIC3R_DEBUG for (const MyLayer *layer : top_contacts) @@ -662,20 +665,32 @@ Polygons collect_slices_outer(const Layer &layer) return out; } +struct SupportGridParams { + SupportGridParams(const PrintObjectConfig &object_config, const Flow &support_material_flow) : + grid_resolution(object_config.support_material_spacing.value + support_material_flow.spacing()), + support_angle(Geometry::deg2rad(object_config.support_material_angle.value)), + extrusion_width(support_material_flow.spacing()), + expansion_to_slice(coord_t(support_material_flow.scaled_spacing() / 2 + 5)), + expansion_to_propagate(-3) {} + + double grid_resolution; + double support_angle; + double extrusion_width; + coord_t expansion_to_slice; + coord_t expansion_to_propagate; +}; + class SupportGridPattern { public: SupportGridPattern( // Support islands, to be stretched into a grid. Already trimmed with min(lower_layer_offset, m_gap_xy) - const Polygons *support_polygons, + const Polygons *support_polygons, // Trimming polygons, to trim the stretched support islands. support_polygons were already trimmed with trimming_polygons. - const Polygons *trimming_polygons, - // Grid spacing, given by "support_material_spacing" + m_support_material_flow.spacing() - coordf_t support_spacing, - coordf_t support_angle, - coordf_t line_spacing) : + const Polygons *trimming_polygons, + const SupportGridParams ¶ms) : m_support_polygons(support_polygons), m_trimming_polygons(trimming_polygons), - m_support_spacing(support_spacing), m_support_angle(support_angle) + m_support_spacing(params.grid_resolution), m_support_angle(params.support_angle) { if (m_support_angle != 0.) { // Create a copy of the rotated contours. @@ -683,8 +698,8 @@ public: m_trimming_polygons_rotated = *trimming_polygons; m_support_polygons = &m_support_polygons_rotated; m_trimming_polygons = &m_trimming_polygons_rotated; - polygons_rotate(m_support_polygons_rotated, - support_angle); - polygons_rotate(m_trimming_polygons_rotated, - support_angle); + polygons_rotate(m_support_polygons_rotated, - params.support_angle); + polygons_rotate(m_trimming_polygons_rotated, - params.support_angle); } // Resolution of the sparse support grid. @@ -697,9 +712,9 @@ public: #ifdef SUPPORT_USE_AGG_RASTERIZER m_bbox = bbox; // Oversample the grid to avoid leaking of supports through or around the object walls. - int oversampling = std::min(8, int(scale_(m_support_spacing) / (scale_(line_spacing) + 100))); + int oversampling = std::min(8, int(scale_(m_support_spacing) / (scale_(params.extrusion_width) + 100))); m_pixel_size = scale_(m_support_spacing / oversampling); - assert(scale_(line_spacing) + 20 < m_pixel_size); + assert(scale_(params.extrusion_width) + 20 < m_pixel_size); // Add one empty column / row boundaries. m_bbox.offset(m_pixel_size); // Grid size fitting the support polygons plus one pixel boundary around the polygons. @@ -1272,65 +1287,14 @@ namespace SupportMaterialInternal { } } -#if 0 -static int Test() +std::vector PrintObjectSupportMaterial::buildplate_covered(const PrintObject &object) const { -// for (int i = 0; i < 30; ++ i) - { - int i = -1; -// SupportGridPattern grid("d:\\temp\\support-top-contacts-final-run1-layer460-z70.300000-prev.bin", i); -// SupportGridPattern grid("d:\\temp\\support-top-contacts-final-run1-layer460-z70.300000.bin", i); - auto grid = SupportGridPattern::deserialize("d:\\temp\\support-top-contacts-final-run1-layer27-z5.650000.bin", i); - std::vector> intersections = grid.grid().intersecting_edges(); - if (! intersections.empty()) - printf("Intersections between contours!\n"); - Slic3r::export_intersections_to_svg("d:\\temp\\support_polygon_intersections.svg", grid.support_polygons()); - Slic3r::SVG::export_expolygons("d:\\temp\\support_polygons.svg", union_ex(grid.support_polygons(), false)); - Slic3r::SVG::export_expolygons("d:\\temp\\trimming_polygons.svg", union_ex(grid.trimming_polygons(), false)); - Polygons extracted = grid.extract_support(scale_(0.21 / 2), true); - Slic3r::SVG::export_expolygons("d:\\temp\\extracted.svg", union_ex(extracted, false)); - printf("hu!"); - } - return 0; -} -static int run_support_test = Test(); -#endif /* SLIC3R_DEBUG */ - -// Generate top contact layers supporting overhangs. -// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined. -// If supports over bed surface only are requested, don't generate contact layers over an object. -PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_layers( - const PrintObject &object, MyLayerStorage &layer_storage) const -{ -#ifdef SLIC3R_DEBUG - static int iRun = 0; - ++ iRun; -#endif /* SLIC3R_DEBUG */ - - // Slice support enforcers / support blockers. - std::vector enforcers = object.slice_support_enforcers(); - std::vector blockers = object.slice_support_blockers(); - - // Append custom supports. - object.project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers); - object.project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers); - - // Output layers, sorted by top Z. - MyLayersPtr contact_out; - - const bool support_auto = m_object_config->support_material.value && m_object_config->support_material_auto.value; - // If user specified a custom angle threshold, convert it to radians. - // Zero means automatic overhang detection. - const double threshold_rad = (m_object_config->support_material_threshold.value > 0) ? - M_PI * double(m_object_config->support_material_threshold.value + 1) / 180. : // +1 makes the threshold inclusive - 0.; - // Build support on a build plate only? If so, then collect and union all the surfaces below the current layer. // Unfortunately this is an inherently serial process. const bool buildplate_only = this->build_plate_only(); std::vector buildplate_covered; if (buildplate_only) { - BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() - collecting regions covering the print bed."; + BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::buildplate_covered() - start"; buildplate_covered.assign(object.layers().size(), Polygons()); for (size_t layer_id = 1; layer_id < object.layers().size(); ++ layer_id) { const Layer &lower_layer = *object.layers()[layer_id-1]; @@ -1344,7 +1308,548 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ polygons_append(covered, offset(lower_layer.lslices, scale_(0.01))); covered = union_(covered, false); // don't apply the safety offset. } + BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::buildplate_covered() - end"; } + return buildplate_covered; +} + +struct SupportAnnotations +{ + SupportAnnotations(const PrintObject &object, const std::vector &buildplate_covered) : + enforcers_layers(object.slice_support_enforcers()), + blockers_layers(object.slice_support_blockers()), + buildplate_covered(buildplate_covered) + { + // Append custom supports. + object.project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers_layers); + object.project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers_layers); + } + + std::vector enforcers_layers; + std::vector blockers_layers; + const std::vector& buildplate_covered; +}; + +struct SlicesMarginCache +{ + float offset { -1 }; + // Trimming polygons, including possibly the "build plate only" mask. + Polygons polygons; + // Trimming polygons, without the "build plate only" mask. If empty, use polygons. + Polygons all_polygons; +}; + +static inline std::tuple detect_overhangs( + const Layer &layer, + const size_t layer_id, + const Polygons &lower_layer_polygons, + const PrintConfig &print_config, + const PrintObjectConfig &object_config, + SupportAnnotations &annotations, + SlicesMarginCache &slices_margin, + const double gap_xy +#ifdef SLIC3R_DEBUG + , size_t iRun +#endif // SLIC3R_DEBUG + ) +{ + // Snug overhang polygons. + Polygons overhang_polygons; + // Expanded for stability, trimmed by gap_xy. + Polygons contact_polygons; + // Enforcers projected to overhangs, trimmed + Polygons enforcer_polygons; + + const bool support_auto = object_config.support_material.value && object_config.support_material_auto.value; + const bool buildplate_only = ! annotations.buildplate_covered.empty(); + // If user specified a custom angle threshold, convert it to radians. + // Zero means automatic overhang detection. + const double threshold_rad = (object_config.support_material_threshold.value > 0) ? + M_PI * double(object_config.support_material_threshold.value + 1) / 180. : // +1 makes the threshold inclusive + 0.; + float no_interface_offset = 0.f; + + if (layer_id == 0) + { + // This is the first object layer, so the object is being printed on a raft and + // we're here just to get the object footprint for the raft. +#if 0 + // The following line was filling excessive holes in the raft, see GH #430 + overhang_polygons = collect_slices_outer(layer); +#else + // Don't fill in the holes. The user may apply a higher raft_expansion if one wants a better 1st layer adhesion. + overhang_polygons = to_polygons(layer.lslices); +#endif + // Expand for better stability. + contact_polygons = offset(overhang_polygons, scaled(object_config.raft_expansion.value)); + } + else + { + // Generate overhang / contact_polygons for non-raft layers. + const Layer &lower_layer = *layer.lower_layer; + const bool has_enforcer = ! annotations.enforcers_layers.empty() && ! annotations.enforcers_layers[layer_id].empty(); + float fw; + for (LayerRegion *layerm : layer.regions()) { + // Extrusion width accounts for the roundings of the extrudates. + // It is the maximum widh of the extrudate. + fw = float(layerm->flow(frExternalPerimeter).scaled_width()); + no_interface_offset = (no_interface_offset == 0.f) ? fw : std::min(no_interface_offset, fw); + float lower_layer_offset = + (layer_id < (size_t)object_config.support_material_enforce_layers.value) ? + // Enforce a full possible support, ignore the overhang angle. + 0.f : + (threshold_rad > 0. ? + // Overhang defined by an angle. + float(scale_(lower_layer.height / tan(threshold_rad))) : + // Overhang defined by half the extrusion width. + 0.5f * fw); + // Overhang polygons for this layer and region. + Polygons diff_polygons; + Polygons layerm_polygons = to_polygons(layerm->slices); + if (lower_layer_offset == 0.f) { + // Support everything. + diff_polygons = diff(layerm_polygons, lower_layer_polygons); + if (buildplate_only) { + // Don't support overhangs above the top surfaces. + // This step is done before the contact surface is calculated by growing the overhang region. + diff_polygons = diff(diff_polygons, annotations.buildplate_covered[layer_id]); + } + } else { + if (support_auto) { + // Get the regions needing a suport, collapse very tiny spots. + //FIXME cache the lower layer offset if this layer has multiple regions. +#if 1 + //FIXME this solution will trigger stupid supports for sharp corners, see GH #4874 + diff_polygons = offset2( + diff(layerm_polygons, + offset2(lower_layer_polygons, - 0.5f * fw, lower_layer_offset + 0.5f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)), + //FIXME This offset2 is targeted to reduce very thin regions to support, but it may lead to + // no support at all for not so steep overhangs. + - 0.1f * fw, 0.1f * fw); +#else + diff_polygons = + diff(layerm_polygons, + offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)); +#endif + if (buildplate_only && ! annotations.buildplate_covered[layer_id].empty()) { + // Don't support overhangs above the top surfaces. + // This step is done before the contact surface is calculated by growing the overhang region. + diff_polygons = diff(diff_polygons, annotations.buildplate_covered[layer_id]); + } + if (! diff_polygons.empty()) { + // Offset the support regions back to a full overhang, restrict them to the full overhang. + // This is done to increase size of the supporting columns below, as they are calculated by + // propagating these contact surfaces downwards. + diff_polygons = diff( + intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons), + lower_layer_polygons); + } + } + } + + if (diff_polygons.empty()) + continue; + + // Apply the "support blockers". + if (! annotations.blockers_layers.empty() && ! annotations.blockers_layers[layer_id].empty()) { + // Expand the blocker a bit. Custom blockers produce strips + // spanning just the projection between the two slices. + // Subtracting them as they are may leave unwanted narrow + // residues of diff_polygons that would then be supported. + diff_polygons = diff(diff_polygons, + offset(union_(to_polygons(std::move(annotations.blockers_layers[layer_id]))), float(1000.*SCALED_EPSILON))); + } + + #ifdef SLIC3R_DEBUG + { + ::Slic3r::SVG svg(debug_out_path("support-top-contacts-raw-run%d-layer%d-region%d.svg", + iRun, layer_id, + std::find_if(layer.regions().begin(), layer.regions().end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions().begin()), + get_extents(diff_polygons)); + Slic3r::ExPolygons expolys = union_ex(diff_polygons, false); + svg.draw(expolys); + } + #endif /* SLIC3R_DEBUG */ + + if (object_config.dont_support_bridges) + SupportMaterialInternal::remove_bridges_from_contacts( + print_config, lower_layer, lower_layer_polygons, layerm, fw, diff_polygons); + + if (diff_polygons.empty()) + continue; + + #ifdef SLIC3R_DEBUG + Slic3r::SVG::export_expolygons( + debug_out_path("support-top-contacts-filtered-run%d-layer%d-region%d-z%f.svg", + iRun, layer_id, + std::find_if(layer.regions().begin(), layer.regions().end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions().begin(), + layer.print_z), + union_ex(diff_polygons, false)); + #endif /* SLIC3R_DEBUG */ + + //FIXME the overhang_polygons are used to construct the support towers as well. + //if (this->has_contact_loops()) + // Store the exact contour of the overhang for the contact loops. + polygons_append(overhang_polygons, diff_polygons); + + // Let's define the required contact area by using a max gap of half the upper + // extrusion width and extending the area according to the configured margin. + // We increment the area in steps because we don't want our support to overflow + // on the other side of the object (if it's very thin). + { + //FIMXE 1) Make the offset configurable, 2) Make the Z span configurable. + //FIXME one should trim with the layer span colliding with the support layer, this layer + // may be lower than lower_layer, so the support area needed may need to be actually bigger! + // For the same reason, the non-bridging support area may be smaller than the bridging support area! + float slices_margin_offset = std::min(lower_layer_offset, float(scale_(gap_xy))); + if (slices_margin.offset != slices_margin_offset) { + slices_margin.offset = slices_margin_offset; + slices_margin.polygons = (slices_margin_offset == 0.f) ? + lower_layer_polygons : + offset2(to_polygons(lower_layer.lslices), - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS); + if (buildplate_only && ! annotations.buildplate_covered[layer_id].empty()) { + if (has_enforcer) + // Make a backup of trimming polygons before enforcing "on build plate only". + slices_margin.all_polygons = slices_margin.polygons; + // Trim the inflated contact surfaces by the top surfaces as well. + slices_margin.polygons = union_(slices_margin.polygons, annotations.buildplate_covered[layer_id]); + } + } + // Offset the contact polygons outside. +#if 0 + for (size_t i = 0; i < NUM_MARGIN_STEPS; ++ i) { + diff_polygons = diff( + offset( + diff_polygons, + scaled(SUPPORT_MATERIAL_MARGIN / NUM_MARGIN_STEPS), + ClipperLib::jtRound, + // round mitter limit + scale_(0.05)), + slices_margin_cached); + } +#else + diff_polygons = diff(diff_polygons, slices_margin.polygons); +#endif + } + polygons_append(contact_polygons, diff_polygons); + } // for each layer.region + + if (has_enforcer) { + // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes. +#ifdef SLIC3R_DEBUG + ExPolygons enforcers_united = union_ex(to_polygons(annotations.enforcers_layers[layer_id]), false); +#endif // SLIC3R_DEBUG + enforcer_polygons = diff(intersection(to_polygons(layer.lslices), to_polygons(std::move(annotations.enforcers_layers[layer_id]))), + // Inflate just a tiny bit to avoid intersection of the overhang areas with the object. + offset(lower_layer_polygons, 0.05f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)); +#ifdef SLIC3R_DEBUG + SVG::export_expolygons(debug_out_path("support-top-contacts-enforcers-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z), + { { layer.lslices, { "layer.lslices", "gray", 0.2f } }, + { { union_ex(lower_layer_polygons, false) }, { "lower_layer_polygons", "green", 0.5f } }, + { enforcers_united, { "enforcers", "blue", 0.5f } }, + { { union_ex(enforcer_polygons, true) }, { "new_contacts", "red", "black", "", scaled(0.1f), 0.5f } } }); +#endif /* SLIC3R_DEBUG */ + polygons_append(overhang_polygons, enforcer_polygons); + polygons_append(contact_polygons, diff(enforcer_polygons, slices_margin.all_polygons.empty() ? slices_margin.polygons : slices_margin.all_polygons)); + } + } + + return std::make_tuple(std::move(overhang_polygons), std::move(contact_polygons), std::move(enforcer_polygons), no_interface_offset); +} + +static inline std::pair new_contact_layer( + const PrintConfig &print_config, + const PrintObjectConfig &object_config, + const SlicingParameters &slicing_params, + const Layer &layer, + std::deque &layer_storage, + tbb::spin_mutex &layer_storage_mutex) +{ + double print_z, bottom_z, height; + PrintObjectSupportMaterial::MyLayer* bridging_layer = nullptr; + assert(layer.id() >= slicing_params.raft_layers()); + size_t layer_id = layer.id() - slicing_params.raft_layers(); + + if (layer_id == 0) { + // This is a raft contact layer sitting directly on the print bed. + assert(slicing_params.has_raft()); + print_z = slicing_params.raft_contact_top_z; + bottom_z = slicing_params.raft_interface_top_z; + height = slicing_params.contact_raft_layer_height; + } else if (slicing_params.soluble_interface) { + // Align the contact surface height with a layer immediately below the supported layer. + // Interface layer will be synchronized with the object. + print_z = layer.bottom_z(); + height = layer.lower_layer->height; + bottom_z = (layer_id == 1) ? slicing_params.object_print_z_min : layer.lower_layer->lower_layer->print_z; + } else { + print_z = layer.bottom_z() - slicing_params.gap_object_support; + bottom_z = print_z; + height = 0.; + // Ignore this contact area if it's too low. + // Don't want to print a layer below the first layer height as it may not stick well. + //FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact + // and it may actually make sense to do it with a thinner layer than the first layer height. + if (print_z < slicing_params.first_print_layer_height - EPSILON) { + // This contact layer is below the first layer height, therefore not printable. Don't support this surface. + return std::pair(nullptr, nullptr); + } else if (print_z < slicing_params.first_print_layer_height + EPSILON) { + // Align the layer with the 1st layer height. + print_z = slicing_params.first_print_layer_height; + bottom_z = 0; + height = slicing_params.first_print_layer_height; + } else { + // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and + // its height will be set adaptively later on. + } + + // Contact layer will be printed with a normal flow, but + // it will support layers printed with a bridging flow. + if (object_config.thick_bridges && SupportMaterialInternal::has_bridging_extrusions(layer)) { + coordf_t bridging_height = 0.; + for (const LayerRegion* region : layer.regions()) + bridging_height += region->region()->bridging_height_avg(print_config); + bridging_height /= coordf_t(layer.regions().size()); + coordf_t bridging_print_z = layer.print_z - bridging_height - slicing_params.gap_support_object; + if (bridging_print_z >= slicing_params.first_print_layer_height - EPSILON) { + // Not below the first layer height means this layer is printable. + if (print_z < slicing_params.first_print_layer_height + EPSILON) { + // Align the layer with the 1st layer height. + bridging_print_z = slicing_params.first_print_layer_height; + } + if (bridging_print_z < print_z - EPSILON) { + // Allocate the new layer. + bridging_layer = &layer_allocate(layer_storage, layer_storage_mutex, PrintObjectSupportMaterial::sltTopContact); + bridging_layer->idx_object_layer_above = layer_id; + bridging_layer->print_z = bridging_print_z; + if (bridging_print_z == slicing_params.first_print_layer_height) { + bridging_layer->bottom_z = 0; + bridging_layer->height = slicing_params.first_print_layer_height; + } + else { + // Don't know the height yet. + bridging_layer->bottom_z = bridging_print_z; + bridging_layer->height = 0; + } + } + } + } + } + + PrintObjectSupportMaterial::MyLayer &new_layer = layer_allocate(layer_storage, layer_storage_mutex, PrintObjectSupportMaterial::sltTopContact); + new_layer.idx_object_layer_above = layer_id; + new_layer.print_z = print_z; + new_layer.bottom_z = bottom_z; + new_layer.height = height; + return std::make_pair(&new_layer, bridging_layer); +} + +static inline void fill_contact_layer( + PrintObjectSupportMaterial::MyLayer &new_layer, + size_t layer_id, + const SlicingParameters &slicing_params, + const PrintObjectConfig &object_config, + const SlicesMarginCache &slices_margin, + const Polygons &overhang_polygons, + const Polygons &contact_polygons, + const Polygons &enforcer_polygons, + const Polygons &lower_layer_polygons, + const Flow &support_material_flow, + float no_interface_offset +#ifdef SLIC3R_DEBUG + , size_t iRun, + const Layer &layer +#endif // SLIC3R_DEBUG + ) +{ + const SupportGridParams grid_params(object_config, support_material_flow); + + Polygons lower_layer_polygons_for_dense_interface_cache; + auto lower_layer_polygons_for_dense_interface = [&lower_layer_polygons_for_dense_interface_cache, &lower_layer_polygons, no_interface_offset]() -> const Polygons& { + if (lower_layer_polygons_for_dense_interface_cache.empty()) + lower_layer_polygons_for_dense_interface_cache = + offset2(lower_layer_polygons, - no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS); + return lower_layer_polygons_for_dense_interface_cache; + }; + + // Stretch support islands into a grid, trim them. + SupportGridPattern support_grid_pattern(&contact_polygons, &slices_margin.polygons, grid_params); + // 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells. + new_layer.contact_polygons = std::make_unique(support_grid_pattern.extract_support(grid_params.expansion_to_propagate, true +#ifdef SLIC3R_DEBUG + , "top_contact_polygons", iRun, layer_id, layer.print_z +#endif // SLIC3R_DEBUG + )); + // 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra. + if (layer_id == 0 || slicing_params.soluble_interface) { + // if (no_interface_offset == 0.f) { + new_layer.polygons = support_grid_pattern.extract_support(grid_params.expansion_to_slice, true +#ifdef SLIC3R_DEBUG + , "top_contact_polygons2", iRun, layer_id, layer.print_z +#endif // SLIC3R_DEBUG + ); + } else { + // Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions. + Polygons dense_interface_polygons = diff(overhang_polygons, lower_layer_polygons_for_dense_interface()); + if (! dense_interface_polygons.empty()) { + dense_interface_polygons = + diff( + // Regularize the contour. + offset(dense_interface_polygons, no_interface_offset * 0.1f), + slices_margin.polygons); + // Support islands, to be stretched into a grid. + //FIXME The regularization of dense_interface_polygons above may stretch dense_interface_polygons outside of the contact polygons, + // thus some dense interface areas may not get supported. Trim the excess with contact_polygons at the following line. + // See for example GH #4874. + Polygons dense_interface_polygons_trimmed = intersection(dense_interface_polygons, *new_layer.contact_polygons); + // Stretch support islands into a grid, trim them. + SupportGridPattern support_grid_pattern(&dense_interface_polygons_trimmed, &slices_margin.polygons, grid_params); + new_layer.polygons = support_grid_pattern.extract_support(grid_params.expansion_to_slice, false +#ifdef SLIC3R_DEBUG + , "top_contact_polygons3", iRun, layer_id, layer.print_z +#endif // SLIC3R_DEBUG + ); + #ifdef SLIC3R_DEBUG + SVG::export_expolygons(debug_out_path("support-top-contacts-final0-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z), + { { { union_ex(lower_layer_polygons, false) }, { "lower_layer_polygons", "gray", 0.2f } }, + { { union_ex(*new_layer.contact_polygons, false) }, { "new_layer.contact_polygons", "yellow", 0.5f } }, + { { union_ex(slices_margin.polygons, false) }, { "slices_margin_cached", "blue", 0.5f } }, + { { union_ex(dense_interface_polygons, false) }, { "dense_interface_polygons", "green", 0.5f } }, + { { union_ex(new_layer.polygons, true) }, { "new_layer.polygons", "red", "black", "", scaled(0.1f), 0.5f } } }); + //support_grid_pattern.serialize(debug_out_path("support-top-contacts-final-run%d-layer%d-z%f.bin", iRun, layer_id, layer.print_z)); + SVG::export_expolygons(debug_out_path("support-top-contacts-final0-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z), + { { { union_ex(lower_layer_polygons, false) }, { "lower_layer_polygons", "gray", 0.2f } }, + { { union_ex(*new_layer.contact_polygons, false) }, { "new_layer.contact_polygons", "yellow", 0.5f } }, + { { union_ex(contact_polygons, false) }, { "contact_polygons", "blue", 0.5f } }, + { { union_ex(dense_interface_polygons, false) }, { "dense_interface_polygons", "green", 0.5f } }, + { { union_ex(new_layer.polygons, true) }, { "new_layer.polygons", "red", "black", "", scaled(0.1f), 0.5f } } }); + #endif /* SLIC3R_DEBUG */ + } + } + + if (! enforcer_polygons.empty() && ! slices_margin.all_polygons.empty() && layer_id > 0) { + // Support enforcers used together with support enforcers. The support enforcers need to be handled separately from the rest of the support. + + { + SupportGridPattern support_grid_pattern(&enforcer_polygons, &slices_margin.all_polygons, grid_params); + // 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells. + new_layer.enforcer_polygons = std::make_unique(support_grid_pattern.extract_support(grid_params.expansion_to_propagate, true + #ifdef SLIC3R_DEBUG + , "top_contact_polygons4", iRun, layer_id, layer.print_z + #endif // SLIC3R_DEBUG + )); + } + // 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra. + // Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions. + Polygons dense_interface_polygons = diff(enforcer_polygons, lower_layer_polygons_for_dense_interface()); + if (! dense_interface_polygons.empty()) { + dense_interface_polygons = + diff( + // Regularize the contour. + offset(dense_interface_polygons, no_interface_offset * 0.1f), + slices_margin.all_polygons); + // Support islands, to be stretched into a grid. + //FIXME The regularization of dense_interface_polygons above may stretch dense_interface_polygons outside of the contact polygons, + // thus some dense interface areas may not get supported. Trim the excess with contact_polygons at the following line. + // See for example GH #4874. + Polygons dense_interface_polygons_trimmed = intersection(dense_interface_polygons, *new_layer.enforcer_polygons); + SupportGridPattern support_grid_pattern(&dense_interface_polygons_trimmed, &slices_margin.all_polygons, grid_params); + // Extend the polygons to extrude with the contact polygons of support enforcers. + bool needs_union = ! new_layer.polygons.empty(); + append(new_layer.polygons, support_grid_pattern.extract_support(grid_params.expansion_to_slice, false +#ifdef SLIC3R_DEBUG + , "top_contact_polygons5", iRun, layer_id, layer.print_z +#endif // SLIC3R_DEBUG + )); + if (needs_union) + new_layer.polygons = union_(new_layer.polygons); + } + } + +#ifdef SLIC3R_DEBUG + SVG::export_expolygons(debug_out_path("support-top-contacts-final0-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z), + { { { union_ex(lower_layer_polygons, false) }, { "lower_layer_polygons", "gray", 0.2f } }, + { { union_ex(*new_layer.contact_polygons, false) }, { "new_layer.contact_polygons", "yellow", 0.5f } }, + { { union_ex(contact_polygons, false) }, { "contact_polygons", "blue", 0.5f } }, + { { union_ex(overhang_polygons, false) }, { "overhang_polygons", "green", 0.5f } }, + { { union_ex(new_layer.polygons, true) }, { "new_layer.polygons", "red", "black", "", scaled(0.1f), 0.5f } } }); +#endif /* SLIC3R_DEBUG */ + + // Even after the contact layer was expanded into a grid, some of the contact islands may be too tiny to be extruded. + // Remove those tiny islands from new_layer.polygons and new_layer.contact_polygons. + + // Store the overhang polygons. + // The overhang polygons are used in the path generator for planning of the contact loops. + // if (this->has_contact_loops()). Compared to "polygons", "overhang_polygons" are snug. + new_layer.overhang_polygons = std::make_unique(std::move(overhang_polygons)); + if (! enforcer_polygons.empty()) + new_layer.enforcer_polygons = std::make_unique(std::move(enforcer_polygons)); +} + +// Merge close contact layers conservatively: If two layers are closer than the minimum allowed print layer height (the min_layer_height parameter), +// the top contact layer is merged into the bottom contact layer. +static void merge_contact_layers(const SlicingParameters &slicing_params, double support_layer_height_min, PrintObjectSupportMaterial::MyLayersPtr &layers) +{ + // Sort the layers, as one layer may produce bridging and non-bridging contact layers with different print_z. + std::sort(layers.begin(), layers.end(), [](const PrintObjectSupportMaterial::MyLayer *l1, const PrintObjectSupportMaterial::MyLayer *l2) { return l1->print_z < l2->print_z; }); + + int i = 0; + int k = 0; + { + // Find the span of layers, which are to be printed at the first layer height. + int j = 0; + for (; j < (int)layers.size() && layers[j]->print_z < slicing_params.first_print_layer_height + support_layer_height_min - EPSILON; ++ j); + if (j > 0) { + // Merge the layers layers (0) to (j - 1) into the layers[0]. + PrintObjectSupportMaterial::MyLayer &dst = *layers.front(); + for (int u = 1; u < j; ++ u) + dst.merge(std::move(*layers[u])); + // Snap the first layer to the 1st layer height. + dst.print_z = slicing_params.first_print_layer_height; + dst.height = slicing_params.first_print_layer_height; + dst.bottom_z = 0; + ++ k; + } + i = j; + } + for (; i < int(layers.size()); ++ k) { + // Find the span of layers closer than m_support_layer_height_min. + int j = i + 1; + coordf_t zmax = layers[i]->print_z + support_layer_height_min + EPSILON; + for (; j < (int)layers.size() && layers[j]->print_z < zmax; ++ j) ; + if (i + 1 < j) { + // Merge the layers layers (i + 1) to (j - 1) into the layers[i]. + PrintObjectSupportMaterial::MyLayer &dst = *layers[i]; + for (int u = i + 1; u < j; ++ u) + dst.merge(std::move(*layers[u])); + } + if (k < i) + layers[k] = layers[i]; + i = j; + } + if (k < (int)layers.size()) + layers.erase(layers.begin() + k, layers.end()); +} + +// Generate top contact layers supporting overhangs. +// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined. +// If supports over bed surface only are requested, don't generate contact layers over an object. +PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_layers( + const PrintObject &object, const std::vector &buildplate_covered, MyLayerStorage &layer_storage) const +{ +#ifdef SLIC3R_DEBUG + static int iRun = 0; + ++ iRun; + #define SLIC3R_IRUN , iRun +#endif /* SLIC3R_DEBUG */ + + // Slice support enforcers / support blockers. + SupportAnnotations annotations(object, buildplate_covered); + + // Output layers, sorted by top Z. + MyLayersPtr contact_out; + + const bool support_auto = m_object_config->support_material.value && m_object_config->support_material_auto.value; BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() in parallel - start"; // Determine top contact areas. @@ -1358,448 +1863,282 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ contact_out.assign(num_layers * 2, nullptr); tbb::spin_mutex layer_storage_mutex; tbb::parallel_for(tbb::blocked_range(this->has_raft() ? 0 : 1, num_layers), - [this, &object, &buildplate_covered, &enforcers, &blockers, support_auto, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out] + [this, &object, &annotations, support_auto, &layer_storage, &layer_storage_mutex, &contact_out] (const tbb::blocked_range& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { - const Layer &layer = *object.layers()[layer_id]; + const Layer &layer = *object.layers()[layer_id]; + Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id - 1]->lslices); + SlicesMarginCache slices_margin; - // Detect overhangs and contact areas needed to support them. - // Collect overhangs and contacts of all regions of this layer supported by the layer immediately below. - Polygons overhang_polygons; - Polygons contact_polygons; - Polygons slices_margin_cached; - float slices_margin_cached_offset = -1.; - Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id-1]->lslices); - // Offset of the lower layer, to trim the support polygons with to calculate dense supports. - float no_interface_offset = 0.f; - if (layer_id == 0) { - // This is the first object layer, so the object is being printed on a raft and - // we're here just to get the object footprint for the raft. -#if 0 - // The following line was filling excessive holes in the raft, see GH #430 - overhang_polygons = collect_slices_outer(layer); -#else - // Don't fill in the holes. The user may apply a higher raft_expansion if one wants a better 1st layer adhesion. - overhang_polygons = to_polygons(layer.lslices); -#endif - // Expand for better stability. - contact_polygons = offset(overhang_polygons, scaled(m_object_config->raft_expansion.value)); - } else { - // Generate overhang / contact_polygons for non-raft layers. - const Layer &lower_layer = *object.layers()[layer_id-1]; - for (LayerRegion *layerm : layer.regions()) { - // Extrusion width accounts for the roundings of the extrudates. - // It is the maximum widh of the extrudate. - float fw = float(layerm->flow(frExternalPerimeter).scaled_width()); - no_interface_offset = (no_interface_offset == 0.f) ? fw : std::min(no_interface_offset, fw); - float lower_layer_offset = - (layer_id < (size_t)m_object_config->support_material_enforce_layers.value) ? - // Enforce a full possible support, ignore the overhang angle. - 0.f : - (threshold_rad > 0. ? - // Overhang defined by an angle. - float(scale_(lower_layer.height / tan(threshold_rad))) : - // Overhang defined by half the extrusion width. - 0.5f * fw); - // Overhang polygons for this layer and region. - Polygons diff_polygons; - Polygons layerm_polygons = to_polygons(layerm->slices); - if (lower_layer_offset == 0.f) { - // Support everything. - diff_polygons = diff(layerm_polygons, lower_layer_polygons); - if (! buildplate_covered.empty()) { - // Don't support overhangs above the top surfaces. - // This step is done before the contact surface is calculated by growing the overhang region. - diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]); - } - } else { - if (support_auto) { - // Get the regions needing a suport, collapse very tiny spots. - //FIXME cache the lower layer offset if this layer has multiple regions. - #if 1 - //FIXME this solution will trigger stupid supports for sharp corners, see GH #4874 - diff_polygons = offset2( - diff(layerm_polygons, - offset2(lower_layer_polygons, - 0.5f * fw, lower_layer_offset + 0.5f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)), - //FIXME This offset2 is targeted to reduce very thin regions to support, but it may lead to - // no support at all for not so steep overhangs. - - 0.1f * fw, 0.1f * fw); - #else - diff_polygons = - diff(layerm_polygons, - offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)); - #endif - if (! buildplate_covered.empty()) { - // Don't support overhangs above the top surfaces. - // This step is done before the contact surface is calculated by growing the overhang region. - diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]); - } - if (! diff_polygons.empty()) { - // Offset the support regions back to a full overhang, restrict them to the full overhang. - // This is done to increase size of the supporting columns below, as they are calculated by - // propagating these contact surfaces downwards. - diff_polygons = diff( - intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons), - lower_layer_polygons); - } - } - if (! enforcers.empty()) { - // Apply the "support enforcers". - //FIXME add the "enforcers" to the sparse support regions only. - const ExPolygons &enforcer = enforcers[layer_id]; - if (! enforcer.empty()) { - // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes. -#ifdef SLIC3R_DEBUG - ExPolygons enforcers_united = union_ex(to_polygons(enforcer), false); -#endif // SLIC3R_DEBUG - Polygons new_contacts = diff(intersection(layerm_polygons, to_polygons(std::move(enforcer))), - offset(lower_layer_polygons, 0.05f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)); -#ifdef SLIC3R_DEBUG - SVG::export_expolygons(debug_out_path("support-top-contacts-enforcers-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z), - { { { union_ex(layerm_polygons, false) }, { "layerm_polygons", "gray", 0.2f } }, - { { union_ex(lower_layer_polygons, false) }, { "lower_layer_polygons", "green", 0.5f } }, - { enforcers_united, { "enforcers", "blue", 0.5f } }, - { { union_ex(new_contacts, true) }, { "new_contacts", "red", "black", "", scaled(0.1f), 0.5f } } }); -#endif /* SLIC3R_DEBUG */ - if (! new_contacts.empty()) { - if (diff_polygons.empty()) - diff_polygons = std::move(new_contacts); - else - diff_polygons = union_(diff_polygons, new_contacts); - } - } - } - } + auto [overhang_polygons, contact_polygons, enforcer_polygons, no_interface_offset] = + detect_overhangs(layer, layer_id, lower_layer_polygons, *m_print_config, *m_object_config, annotations, slices_margin, m_support_params.gap_xy + #ifdef SLIC3R_DEBUG + , iRun + #endif // SLIC3R_DEBUG + ); - if (diff_polygons.empty()) - continue; - - // Apply the "support blockers". - if (! blockers.empty() && ! blockers[layer_id].empty()) { - // Expand the blocker a bit. Custom blockers produce strips - // spanning just the projection between the two slices. - // Subtracting them as they are may leave unwanted narrow - // residues of diff_polygons that would then be supported. - diff_polygons = diff(diff_polygons, - offset(union_(to_polygons(std::move(blockers[layer_id]))), float(1000.*SCALED_EPSILON))); - } - - #ifdef SLIC3R_DEBUG - { - ::Slic3r::SVG svg(debug_out_path("support-top-contacts-raw-run%d-layer%d-region%d.svg", - iRun, layer_id, - std::find_if(layer.regions().begin(), layer.regions().end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions().begin()), - get_extents(diff_polygons)); - Slic3r::ExPolygons expolys = union_ex(diff_polygons, false); - svg.draw(expolys); - } - #endif /* SLIC3R_DEBUG */ - - if (this->m_object_config->dont_support_bridges) - SupportMaterialInternal::remove_bridges_from_contacts( - *m_print_config, lower_layer, lower_layer_polygons, layerm, fw, diff_polygons); - - if (diff_polygons.empty()) - continue; - - #ifdef SLIC3R_DEBUG - Slic3r::SVG::export_expolygons( - debug_out_path("support-top-contacts-filtered-run%d-layer%d-region%d-z%f.svg", - iRun, layer_id, - std::find_if(layer.regions().begin(), layer.regions().end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions().begin(), - layer.print_z), - union_ex(diff_polygons, false)); - #endif /* SLIC3R_DEBUG */ - - //FIXME the overhang_polygons are used to construct the support towers as well. - //if (this->has_contact_loops()) - // Store the exact contour of the overhang for the contact loops. - polygons_append(overhang_polygons, diff_polygons); - - // Let's define the required contact area by using a max gap of half the upper - // extrusion width and extending the area according to the configured margin. - // We increment the area in steps because we don't want our support to overflow - // on the other side of the object (if it's very thin). - { - //FIMXE 1) Make the offset configurable, 2) Make the Z span configurable. - //FIXME one should trim with the layer span colliding with the support layer, this layer - // may be lower than lower_layer, so the support area needed may need to be actually bigger! - // For the same reason, the non-bridging support area may be smaller than the bridging support area! - float slices_margin_offset = std::min(lower_layer_offset, float(scale_(m_gap_xy))); - if (slices_margin_cached_offset != slices_margin_offset) { - slices_margin_cached_offset = slices_margin_offset; - slices_margin_cached = (slices_margin_offset == 0.f) ? - lower_layer_polygons : - offset2(to_polygons(lower_layer.lslices), - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS); - if (! buildplate_covered.empty()) { - // Trim the inflated contact surfaces by the top surfaces as well. - polygons_append(slices_margin_cached, buildplate_covered[layer_id]); - slices_margin_cached = union_(slices_margin_cached); - } - } - // Offset the contact polygons outside. -#if 0 - for (size_t i = 0; i < NUM_MARGIN_STEPS; ++ i) { - diff_polygons = diff( - offset( - diff_polygons, - scaled(SUPPORT_MATERIAL_MARGIN / NUM_MARGIN_STEPS), - ClipperLib::jtRound, - // round mitter limit - scale_(0.05)), - slices_margin_cached); - } -#else - diff_polygons = diff(diff_polygons, slices_margin_cached); -#endif - } - polygons_append(contact_polygons, diff_polygons); - } // for each layer.region - } // end of Generate overhang/contact_polygons for non-raft layers. - // Now apply the contact areas to the layer where they need to be made. if (! contact_polygons.empty()) { - MyLayer &new_layer = layer_allocate(layer_storage, layer_storage_mutex, sltTopContact); - new_layer.idx_object_layer_above = layer_id; - MyLayer *bridging_layer = nullptr; - if (layer_id == 0) { - // This is a raft contact layer sitting directly on the print bed. - assert(this->has_raft()); - new_layer.print_z = m_slicing_params.raft_contact_top_z; - new_layer.bottom_z = m_slicing_params.raft_interface_top_z; - new_layer.height = m_slicing_params.contact_raft_layer_height; - } else if (m_slicing_params.soluble_interface) { - // Align the contact surface height with a layer immediately below the supported layer. - // Interface layer will be synchronized with the object. - new_layer.print_z = layer.bottom_z(); - new_layer.height = object.layers()[layer_id - 1]->height; - new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers()[layer_id - 2]->print_z; - } else { - new_layer.print_z = layer.bottom_z() - m_slicing_params.gap_object_support; - new_layer.bottom_z = new_layer.print_z; - new_layer.height = 0.; - // Ignore this contact area if it's too low. - // Don't want to print a layer below the first layer height as it may not stick well. - //FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact - // and it may actually make sense to do it with a thinner layer than the first layer height. - if (new_layer.print_z < m_slicing_params.first_print_layer_height - EPSILON) { - // This contact layer is below the first layer height, therefore not printable. Don't support this surface. - continue; - } else if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) { - // Align the layer with the 1st layer height. - new_layer.print_z = m_slicing_params.first_print_layer_height; - new_layer.bottom_z = 0; - new_layer.height = m_slicing_params.first_print_layer_height; - } else { - // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and - // its height will be set adaptively later on. - } - - // Contact layer will be printed with a normal flow, but - // it will support layers printed with a bridging flow. - if (m_object_config->thick_bridges && SupportMaterialInternal::has_bridging_extrusions(layer)) { - coordf_t bridging_height = 0.; - for (const LayerRegion *region : layer.regions()) - bridging_height += region->region()->bridging_height_avg(*m_print_config); - bridging_height /= coordf_t(layer.regions().size()); - coordf_t bridging_print_z = layer.print_z - bridging_height - m_slicing_params.gap_support_object; - if (bridging_print_z >= m_slicing_params.first_print_layer_height - EPSILON) { - // Not below the first layer height means this layer is printable. - if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) { - // Align the layer with the 1st layer height. - bridging_print_z = m_slicing_params.first_print_layer_height; - } - if (bridging_print_z < new_layer.print_z - EPSILON) { - // Allocate the new layer. - bridging_layer = &layer_allocate(layer_storage, layer_storage_mutex, sltTopContact); - bridging_layer->idx_object_layer_above = layer_id; - bridging_layer->print_z = bridging_print_z; - if (bridging_print_z == m_slicing_params.first_print_layer_height) { - bridging_layer->bottom_z = 0; - bridging_layer->height = m_slicing_params.first_print_layer_height; - } else { - // Don't know the height yet. - bridging_layer->bottom_z = bridging_print_z; - bridging_layer->height = 0; - } - } - } - } - } - - SupportGridPattern support_grid_pattern( - // Support islands, to be stretched into a grid. - &contact_polygons, - // Trimming polygons, to trim the stretched support islands. - &slices_margin_cached, - // Grid resolution. - m_object_config->support_material_spacing.value + m_support_material_flow.spacing(), - Geometry::deg2rad(m_object_config->support_material_angle.value), - m_support_material_flow.spacing()); - // 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells. - new_layer.contact_polygons = new Polygons(support_grid_pattern.extract_support(-3, true - #ifdef SLIC3R_DEBUG - , "top_contact_polygons", iRun, layer_id, layer.print_z - #endif // SLIC3R_DEBUG - )); - // 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra. - if (layer_id == 0 || m_slicing_params.soluble_interface) { - // if (no_interface_offset == 0.f) { - new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, true - #ifdef SLIC3R_DEBUG - , "top_contact_polygons2", iRun, layer_id, layer.print_z - #endif // SLIC3R_DEBUG - ); - } else { - // Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions. - Polygons dense_interface_polygons = diff(overhang_polygons, - offset2(lower_layer_polygons, - no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS)); - if (! dense_interface_polygons.empty()) { - dense_interface_polygons = - diff( - // Regularize the contour. - offset(dense_interface_polygons, no_interface_offset * 0.1f), - slices_margin_cached); - // Support islands, to be stretched into a grid. - //FIXME The regularization of dense_interface_polygons above may stretch dense_interface_polygons outside of the contact polygons, - // thus some dense interface areas may not get supported. Trim the excess with contact_polygons at the following line. - // See for example GH #4874. - Polygons dense_interface_polygons_trimmed = intersection(dense_interface_polygons, *new_layer.contact_polygons); - SupportGridPattern support_grid_pattern( - &dense_interface_polygons_trimmed, - // Trimming polygons, to trim the stretched support islands. - &slices_margin_cached, - // Grid resolution. - m_object_config->support_material_spacing.value + m_support_material_flow.spacing(), - Geometry::deg2rad(m_object_config->support_material_angle.value), - m_support_material_flow.spacing()); - new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, false - #ifdef SLIC3R_DEBUG - , "top_contact_polygons3", iRun, layer_id, layer.print_z - #endif // SLIC3R_DEBUG - ); + auto [new_layer, bridging_layer] = new_contact_layer(*m_print_config, *m_object_config, m_slicing_params, layer, layer_storage, layer_storage_mutex); + if (new_layer) { + fill_contact_layer(*new_layer, layer_id, m_slicing_params, + *m_object_config, slices_margin, overhang_polygons, contact_polygons, enforcer_polygons, lower_layer_polygons, + m_support_params.support_material_flow, no_interface_offset #ifdef SLIC3R_DEBUG - SVG::export_expolygons(debug_out_path("support-top-contacts-final0-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z), - { { { union_ex(lower_layer_polygons, false) }, { "lower_layer_polygons", "gray", 0.2f } }, - { { union_ex(*new_layer.contact_polygons, false) }, { "new_layer.contact_polygons", "yellow", 0.5f } }, - { { union_ex(slices_margin_cached, false) }, { "slices_margin_cached", "blue", 0.5f } }, - { { union_ex(dense_interface_polygons, false) }, { "dense_interface_polygons", "green", 0.5f } }, - { { union_ex(new_layer.polygons, true) }, { "new_layer.polygons", "red", "black", "", scaled(0.1f), 0.5f } } }); - //support_grid_pattern.serialize(debug_out_path("support-top-contacts-final-run%d-layer%d-z%f.bin", iRun, layer_id, layer.print_z)); - SVG::export_expolygons(debug_out_path("support-top-contacts-final0-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z), - { { { union_ex(lower_layer_polygons, false) }, { "lower_layer_polygons", "gray", 0.2f } }, - { { union_ex(*new_layer.contact_polygons, false) }, { "new_layer.contact_polygons", "yellow", 0.5f } }, - { { union_ex(contact_polygons, false) }, { "contact_polygons", "blue", 0.5f } }, - { { union_ex(dense_interface_polygons, false) }, { "dense_interface_polygons", "green", 0.5f } }, - { { union_ex(new_layer.polygons, true) }, { "new_layer.polygons", "red", "black", "", scaled(0.1f), 0.5f } } }); - #endif /* SLIC3R_DEBUG */ + , iRun, layer + #endif // SLIC3R_DEBUG + ); + contact_out[layer_id * 2] = new_layer; + if (bridging_layer != nullptr) { + bridging_layer->polygons = new_layer->polygons; + bridging_layer->contact_polygons = std::make_unique(*new_layer->contact_polygons); + bridging_layer->overhang_polygons = std::make_unique(*new_layer->overhang_polygons); + if (new_layer->enforcer_polygons) + bridging_layer->enforcer_polygons = std::make_unique(*new_layer->enforcer_polygons); + contact_out[layer_id * 2 + 1] = bridging_layer; } } - #ifdef SLIC3R_DEBUG - SVG::export_expolygons(debug_out_path("support-top-contacts-final0-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z), - { { { union_ex(lower_layer_polygons, false) }, { "lower_layer_polygons", "gray", 0.2f } }, - { { union_ex(*new_layer.contact_polygons, false) }, { "new_layer.contact_polygons", "yellow", 0.5f } }, - { { union_ex(contact_polygons, false) }, { "contact_polygons", "blue", 0.5f } }, - { { union_ex(overhang_polygons, false) }, { "overhang_polygons", "green", 0.5f } }, - { { union_ex(new_layer.polygons, true) }, { "new_layer.polygons", "red", "black", "", scaled(0.1f), 0.5f } } }); - #endif /* SLIC3R_DEBUG */ - - // Even after the contact layer was expanded into a grid, some of the contact islands may be too tiny to be extruded. - // Remove those tiny islands from new_layer.polygons and new_layer.contact_polygons. - - // Store the overhang polygons. - // The overhang polygons are used in the path generator for planning of the contact loops. - // if (this->has_contact_loops()). Compared to "polygons", "overhang_polygons" are snug. - new_layer.overhang_polygons = new Polygons(std::move(overhang_polygons)); - contact_out[layer_id * 2] = &new_layer; - if (bridging_layer != nullptr) { - bridging_layer->polygons = new_layer.polygons; - bridging_layer->contact_polygons = new Polygons(*new_layer.contact_polygons); - bridging_layer->overhang_polygons = new Polygons(*new_layer.overhang_polygons); - contact_out[layer_id * 2 + 1] = bridging_layer; - } } } }); // Compress contact_out, remove the nullptr items. remove_nulls(contact_out); - // Sort the layers, as one layer may produce bridging and non-bridging contact layers with different print_z. - std::sort(contact_out.begin(), contact_out.end(), [](const MyLayer *l1, const MyLayer *l2) { return l1->print_z < l2->print_z; }); - // Merge close contact layers conservatively: If two layers are closer than the minimum allowed print layer height (the min_layer_height parameter), - // the top contact layer is merged into the bottom contact layer. - { - int i = 0; - int k = 0; - { - // Find the span of layers, which are to be printed at the first layer height. - int j = 0; - for (; j < (int)contact_out.size() && contact_out[j]->print_z < m_slicing_params.first_print_layer_height + this->m_support_layer_height_min - EPSILON; ++ j); - if (j > 0) { - // Merge the contact_out layers (0) to (j - 1) into the contact_out[0]. - MyLayer &dst = *contact_out.front(); - for (int u = 1; u < j; ++ u) { - MyLayer &src = *contact_out[u]; - // The union_() does not support move semantic yet, but maybe one day it will. - dst.polygons = union_(dst.polygons, std::move(src.polygons)); - *dst.contact_polygons = union_(*dst.contact_polygons, std::move(*src.contact_polygons)); - *dst.overhang_polygons = union_(*dst.overhang_polygons, std::move(*src.overhang_polygons)); - // Source polygon is no more needed, it will not be refrenced. Release its data. - src.reset(); - } - // Snap the first layer to the 1st layer height. - dst.print_z = m_slicing_params.first_print_layer_height; - dst.height = m_slicing_params.first_print_layer_height; - dst.bottom_z = 0; - ++ k; - } - i = j; - } - for (; i < int(contact_out.size()); ++ k) { - // Find the span of layers closer than m_support_layer_height_min. - int j = i + 1; - coordf_t zmax = contact_out[i]->print_z + m_support_layer_height_min + EPSILON; - for (; j < (int)contact_out.size() && contact_out[j]->print_z < zmax; ++ j) ; - if (i + 1 < j) { - // Merge the contact_out layers (i + 1) to (j - 1) into the contact_out[i]. - MyLayer &dst = *contact_out[i]; - for (int u = i + 1; u < j; ++ u) { - MyLayer &src = *contact_out[u]; - // The union_() does not support move semantic yet, but maybe one day it will. - dst.polygons = union_(dst.polygons, std::move(src.polygons)); - *dst.contact_polygons = union_(*dst.contact_polygons, std::move(*src.contact_polygons)); - *dst.overhang_polygons = union_(*dst.overhang_polygons, std::move(*src.overhang_polygons)); - // Source polygon is no more needed, it will not be refrenced. Release its data. - src.reset(); - } - } - if (k < i) - contact_out[k] = contact_out[i]; - i = j; - } - if (k < (int)contact_out.size()) - contact_out.erase(contact_out.begin() + k, contact_out.end()); - } + merge_contact_layers(m_slicing_params, m_support_params.support_layer_height_min, contact_out); BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() in parallel - end"; return contact_out; } +// Find the bottom contact layers above the top surfaces of this layer. +static inline PrintObjectSupportMaterial::MyLayer* detect_bottom_contacts( + const SlicingParameters &slicing_params, + const PrintObjectSupportMaterial::SupportParams &support_params, + const PrintObject &object, + const Layer &layer, + // Existing top contact layers, to which this newly created bottom contact layer will be snapped to guarantee a minimum layer height. + const PrintObjectSupportMaterial::MyLayersPtr &top_contacts, + // First top contact layer index overlapping with this new bottom interface layer. + size_t contact_idx, + // To allocate a new layer from. + std::deque &layer_storage, + // To trim the support areas above this bottom interface layer with this newly created bottom interface layer. + std::vector &layer_support_areas, + // Support areas projected from top to bottom, starting with top support interfaces. + const Polygons &supports_projected +#ifdef SLIC3R_DEBUG + , size_t iRun + , const Polygons &polygons_new +#endif // SLIC3R_DEBUG + ) +{ + Polygons top = collect_region_slices_by_type(layer, stTop); +#ifdef SLIC3R_DEBUG + SVG::export_expolygons(debug_out_path("support-bottom-layers-raw-%d-%lf.svg", iRun, layer.print_z), + { { { union_ex(top, false) }, { "top", "blue", 0.5f } }, + { { union_ex(supports_projected, true) }, { "overhangs", "magenta", 0.5f } }, + { layer.lslices, { "layer.lslices", "green", 0.5f } }, + { { union_ex(polygons_new, true) }, { "polygons_new", "red", "black", "", scaled(0.1f), 0.5f } } }); +#endif /* SLIC3R_DEBUG */ + + // Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any + // top surfaces above layer.print_z falls onto this top surface. + // Touching are the contact surfaces supported exclusively by this top surfaces. + // Don't use a safety offset as it has been applied during insertion of polygons. + if (top.empty()) + return nullptr; + + Polygons touching = intersection(top, supports_projected, false); + if (touching.empty()) + return nullptr; + + assert(layer.id() >= slicing_params.raft_layers()); + size_t layer_id = layer.id() - slicing_params.raft_layers(); + + // Allocate a new bottom contact layer. + PrintObjectSupportMaterial::MyLayer &layer_new = layer_allocate(layer_storage, PrintObjectSupportMaterial::sltBottomContact); + // Grow top surfaces so that interface and support generation are generated + // with some spacing from object - it looks we don't need the actual + // top shapes so this can be done here + //FIXME calculate layer height based on the actual thickness of the layer: + // If the layer is extruded with no bridging flow, support just the normal extrusions. + layer_new.height = slicing_params.soluble_interface ? + // Align the interface layer with the object's layer height. + layer.upper_layer->height : + // Place a bridge flow interface layer or the normal flow interface layer over the top surface. + support_params.support_material_bottom_interface_flow.height(); + layer_new.print_z = slicing_params.soluble_interface ? layer.upper_layer->print_z : + layer.print_z + layer_new.height + slicing_params.gap_object_support; + layer_new.bottom_z = layer.print_z; + layer_new.idx_object_layer_below = layer_id; + layer_new.bridging = !slicing_params.soluble_interface && object.config().thick_bridges; + //FIXME how much to inflate the bottom surface, as it is being extruded with a bridging flow? The following line uses a normal flow. + //FIXME why is the offset positive? It will be trimmed by the object later on anyway, but then it just wastes CPU clocks. + layer_new.polygons = offset(touching, float(support_params.support_material_flow.scaled_width()), SUPPORT_SURFACES_OFFSET_PARAMETERS); + + if (! slicing_params.soluble_interface) { + // Walk the top surfaces, snap the top of the new bottom surface to the closest top of the top surface, + // so there will be no support surfaces generated with thickness lower than m_support_layer_height_min. + for (size_t top_idx = size_t(std::max(0, contact_idx)); + top_idx < top_contacts.size() && top_contacts[top_idx]->print_z < layer_new.print_z + support_params.support_layer_height_min + EPSILON; + ++ top_idx) { + if (top_contacts[top_idx]->print_z > layer_new.print_z - support_params.support_layer_height_min - EPSILON) { + // A top layer has been found, which is close to the new bottom layer. + coordf_t diff = layer_new.print_z - top_contacts[top_idx]->print_z; + assert(std::abs(diff) <= support_params.support_layer_height_min + EPSILON); + if (diff > 0.) { + // The top contact layer is below this layer. Make the bridging layer thinner to align with the existing top layer. + assert(diff < layer_new.height + EPSILON); + assert(layer_new.height - diff >= support_params.support_layer_height_min - EPSILON); + layer_new.print_z = top_contacts[top_idx]->print_z; + layer_new.height -= diff; + } + else { + // The top contact layer is above this layer. One may either make this layer thicker or thinner. + // By making the layer thicker, one will decrease the number of discrete layers with the price of extruding a bit too thick bridges. + // By making the layer thinner, one adds one more discrete layer. + layer_new.print_z = top_contacts[top_idx]->print_z; + layer_new.height -= diff; + } + break; + } + } + } + +#ifdef SLIC3R_DEBUG + Slic3r::SVG::export_expolygons( + debug_out_path("support-bottom-contacts-%d-%lf.svg", iRun, layer_new.print_z), + union_ex(layer_new.polygons, false)); +#endif /* SLIC3R_DEBUG */ + + // Trim the already created base layers above the current layer intersecting with the new bottom contacts layer. + //FIXME Maybe this is no more needed, as the overlapping base layers are trimmed by the bottom layers at the final stage? + touching = offset(touching, float(SCALED_EPSILON)); + for (int layer_id_above = layer_id + 1; layer_id_above < int(object.total_layer_count()); ++layer_id_above) { + const Layer &layer_above = *object.layers()[layer_id_above]; + if (layer_above.print_z > layer_new.print_z - EPSILON) + break; + if (! layer_support_areas[layer_id_above].empty()) { +#ifdef SLIC3R_DEBUG + SVG::export_expolygons(debug_out_path("support-support-areas-raw-before-trimming-%d-with-%f-%lf.svg", iRun, layer.print_z, layer_above.print_z), + { { { union_ex(touching, false) }, { "touching", "blue", 0.5f } }, + { { union_ex(layer_support_areas[layer_id_above], true) }, { "above", "red", "black", "", scaled(0.1f), 0.5f } } }); +#endif /* SLIC3R_DEBUG */ + layer_support_areas[layer_id_above] = diff(layer_support_areas[layer_id_above], touching); +#ifdef SLIC3R_DEBUG + Slic3r::SVG::export_expolygons( + debug_out_path("support-support-areas-raw-after-trimming-%d-with-%f-%lf.svg", iRun, layer.print_z, layer_above.print_z), + union_ex(layer_support_areas[layer_id_above], false)); +#endif /* SLIC3R_DEBUG */ + } + } + + return &layer_new; +} + +// Returns polygons to print + polygons to propagate downwards. +// Called twice: First for normal supports, possibly trimmed by "on build plate only", second for support enforcers not trimmed by "on build plate only". +static inline std::pair project_support_to_grid(const Layer &layer, const SupportGridParams &grid_params, const Polygons &overhangs, Polygons *layer_buildplate_covered +#ifdef SLIC3R_DEBUG + , size_t iRun, size_t layer_id, const char *debug_name +#endif /* SLIC3R_DEBUG */ +) +{ + // Remove the areas that touched from the projection that will continue on next, lower, top surfaces. +// Polygons trimming = union_(to_polygons(layer.slices), touching, true); + Polygons trimming = layer_buildplate_covered ? std::move(*layer_buildplate_covered) : offset(layer.lslices, float(SCALED_EPSILON)); + Polygons overhangs_projection = diff(overhangs, trimming, false); + +#ifdef SLIC3R_DEBUG + SVG::export_expolygons(debug_out_path("support-support-areas-%s-raw-%d-%lf.svg", debug_name, iRun, layer.print_z), + { { { union_ex(trimming, false) }, { "trimming", "blue", 0.5f } }, + { { union_ex(overhangs_projection, true) }, { "overhangs_projection", "red", "black", "", scaled(0.1f), 0.5f } } }); +#endif /* SLIC3R_DEBUG */ + + remove_sticks(overhangs_projection); + remove_degenerate(overhangs_projection); + +#ifdef SLIC3R_DEBUG + SVG::export_expolygons(debug_out_path("support-support-areas-%s-raw-cleaned-%d-%lf.svg", debug_name, iRun, layer.print_z), + { { { union_ex(trimming, false) }, { "trimming", "blue", 0.5f } }, + { { union_ex(overhangs_projection, false) }, { "overhangs_projection", "red", "black", "", scaled(0.1f), 0.5f } } }); +#endif /* SLIC3R_DEBUG */ + + SupportGridPattern support_grid_pattern(&overhangs_projection, &trimming, grid_params); + tbb::task_group task_group_inner; + + std::pair out; + + // 1) Cache the slice of a support volume. The support volume is expanded by 1/2 of support material flow spacing + // to allow a placement of suppot zig-zag snake along the grid lines. + task_group_inner.run([&grid_params, &support_grid_pattern, &out +#ifdef SLIC3R_DEBUG + , &layer, layer_id, iRun, debug_name +#endif /* SLIC3R_DEBUG */ + ] { + out.first = support_grid_pattern.extract_support(grid_params.expansion_to_slice, true +#ifdef SLIC3R_DEBUG + , (std::string(debug_name) + "_support_area").c_str(), iRun, layer_id, layer.print_z +#endif // SLIC3R_DEBUG + ); +#ifdef SLIC3R_DEBUG + Slic3r::SVG::export_expolygons( + debug_out_path("support-layer_support_area-gridded-%s-%d-%lf.svg", debug_name, iRun, layer.print_z), + union_ex(out.first, false)); +#endif /* SLIC3R_DEBUG */ + }); + + // 2) Support polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells. + task_group_inner.run([&grid_params, &support_grid_pattern, &out +#ifdef SLIC3R_DEBUG + , &layer, layer_id, &overhangs_projection, &trimming, iRun, debug_name +#endif /* SLIC3R_DEBUG */ + ] { + out.second = support_grid_pattern.extract_support(grid_params.expansion_to_propagate, true +#ifdef SLIC3R_DEBUG + , "support_projection", iRun, layer_id, layer.print_z +#endif // SLIC3R_DEBUG + ); +#ifdef SLIC3R_DEBUG + Slic3r::SVG::export_expolygons( + debug_out_path("support-projection_new-gridded-%d-%lf.svg", iRun, layer.print_z), + union_ex(out.second, false)); +#endif /* SLIC3R_DEBUG */ +#ifdef SLIC3R_DEBUG + SVG::export_expolygons(debug_out_path("support-projection_new-gridded-%d-%lf.svg", iRun, layer.print_z), + { { { union_ex(trimming, false) }, { "trimming", "gray", 0.5f } }, + { { union_ex(overhangs_projection, true) }, { "overhangs_projection", "blue", 0.5f } }, + { { union_ex(out.second, true) }, { "projection_new", "red", "black", "", scaled(0.1f), 0.5f } } }); +#endif /* SLIC3R_DEBUG */ + }); + + task_group_inner.wait(); + return out; +} + // Generate bottom contact layers supporting the top contact layers. // For a soluble interface material synchronize the layer heights with the object, // otherwise set the layer height to a bridging flow of a support interface nozzle. PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_contact_layers_and_layer_support_areas( - const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage, - std::vector &layer_support_areas) const + const PrintObject &object, const MyLayersPtr &top_contacts, std::vector &buildplate_covered, + MyLayerStorage &layer_storage, std::vector &layer_support_areas) const { + if (top_contacts.empty()) + return MyLayersPtr(); + #ifdef SLIC3R_DEBUG - static int iRun = 0; - ++ iRun; + static size_t s_iRun = 0; + size_t iRun = s_iRun ++; #endif /* SLIC3R_DEBUG */ + //FIXME higher expansion_to_slice here? why? + //const auto expansion_to_slice = m_support_material_flow.scaled_spacing() / 2 + 25; + const SupportGridParams grid_params(*m_object_config, m_support_params.support_material_flow); + const bool buildplate_only = ! buildplate_covered.empty(); + // Allocate empty surface areas, one per object layer. layer_support_areas.assign(object.total_layer_count(), Polygons()); @@ -1807,228 +2146,117 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta // we'll use them to clip our support and detect where does it stick MyLayersPtr bottom_contacts; - if (! top_contacts.empty()) - { - // There is some support to be built, if there are non-empty top surfaces detected. - // Sum of unsupported contact areas above the current layer.print_z. - Polygons projection; - // Last top contact layer visited when collecting the projection of contact areas. - int contact_idx = int(top_contacts.size()) - 1; - for (int layer_id = int(object.total_layer_count()) - 2; layer_id >= 0; -- layer_id) { - BOOST_LOG_TRIVIAL(trace) << "Support generator - bottom_contact_layers - layer " << layer_id; - const Layer &layer = *object.get_layer(layer_id); - // Collect projections of all contact areas above or at the same level as this top surface. + // There is some support to be built, if there are non-empty top surfaces detected. + // Sum of unsupported contact areas above the current layer.print_z. + Polygons overhangs_projection; + // Sum of unsupported enforcer contact areas above the current layer.print_z. + // Only used if "supports on build plate only" is enabled and both automatic and support enforcers are enabled. + Polygons enforcers_projection; + // Last top contact layer visited when collecting the projection of contact areas. + int contact_idx = int(top_contacts.size()) - 1; + for (int layer_id = int(object.total_layer_count()) - 2; layer_id >= 0; -- layer_id) { + BOOST_LOG_TRIVIAL(trace) << "Support generator - bottom_contact_layers - layer " << layer_id; + const Layer &layer = *object.get_layer(layer_id); + // Collect projections of all contact areas above or at the same level as this top surface. #ifdef SLIC3R_DEBUG - Polygons polygons_new; + Polygons polygons_new; + Polygons enforcers_new; #endif // SLIC3R_DEBUG - for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z > layer.print_z - EPSILON; -- contact_idx) { + for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z > layer.print_z - EPSILON; -- contact_idx) { + MyLayer &top_contact = *top_contacts[contact_idx]; #ifndef SLIC3R_DEBUG - Polygons polygons_new; + Polygons polygons_new; + Polygons enforcers_new; #endif // SLIC3R_DEBUG - // Contact surfaces are expanded away from the object, trimmed by the object. - // Use a slight positive offset to overlap the touching regions. + // Contact surfaces are expanded away from the object, trimmed by the object. + // Use a slight positive offset to overlap the touching regions. #if 0 - // Merge and collect the contact polygons. The contact polygons are inflated, but not extended into a grid form. - polygons_append(polygons_new, offset(*top_contacts[contact_idx]->contact_polygons, SCALED_EPSILON)); + // Merge and collect the contact polygons. The contact polygons are inflated, but not extended into a grid form. + polygons_append(polygons_new, offset(*top_contact.contact_polygons, SCALED_EPSILON)); + if (top_contact.enforcer_polygons) + polygons_append(enforcers_new, offset(*top_contact.enforcer_polygons, SCALED_EPSILON)); #else - // Consume the contact_polygons. The contact polygons are already expanded into a grid form, and they are a tiny bit smaller - // than the grid cells. - polygons_append(polygons_new, std::move(*top_contacts[contact_idx]->contact_polygons)); + // Consume the contact_polygons. The contact polygons are already expanded into a grid form, and they are a tiny bit smaller + // than the grid cells. + polygons_append(polygons_new, std::move(*top_contact.contact_polygons)); + if (top_contact.enforcer_polygons) + polygons_append(enforcers_new, std::move(*top_contact.enforcer_polygons)); #endif - // These are the overhang surfaces. They are touching the object and they are not expanded away from the object. - // Use a slight positive offset to overlap the touching regions. - polygons_append(polygons_new, offset(*top_contacts[contact_idx]->overhang_polygons, float(SCALED_EPSILON))); - polygons_append(projection, union_(polygons_new)); - } - if (projection.empty()) - continue; - Polygons projection_raw = union_(projection); - - tbb::task_group task_group; - if (! m_object_config->support_material_buildplate_only) - // Find the bottom contact layers above the top surfaces of this layer. - task_group.run([this, &object, &top_contacts, contact_idx, &layer, layer_id, &layer_storage, &layer_support_areas, &bottom_contacts, &projection_raw - #ifdef SLIC3R_DEBUG - , &polygons_new - #endif // SLIC3R_DEBUG - ] { - Polygons top = collect_region_slices_by_type(layer, stTop); - #ifdef SLIC3R_DEBUG - SVG::export_expolygons(debug_out_path("support-bottom-layers-raw-%d-%lf.svg", iRun, layer.print_z), - { { { union_ex(top, false) }, { "top", "blue", 0.5f } }, - { { union_ex(projection_raw, true) }, { "projection_raw", "magenta", 0.5f } }, - { layer.lslices, { "layer.lslices", "green", 0.5f } }, - { { union_ex(polygons_new, true) }, { "polygons_new", "red", "black", "", scaled(0.1f), 0.5f } } }); - #endif /* SLIC3R_DEBUG */ - - // Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any - // top surfaces above layer.print_z falls onto this top surface. - // Touching are the contact surfaces supported exclusively by this top surfaces. - // Don't use a safety offset as it has been applied during insertion of polygons. - if (! top.empty()) { - Polygons touching = intersection(top, projection_raw, false); - if (! touching.empty()) { - // Allocate a new bottom contact layer. - MyLayer &layer_new = layer_allocate(layer_storage, sltBottomContact); - bottom_contacts.push_back(&layer_new); - // Grow top surfaces so that interface and support generation are generated - // with some spacing from object - it looks we don't need the actual - // top shapes so this can be done here - //FIXME calculate layer height based on the actual thickness of the layer: - // If the layer is extruded with no bridging flow, support just the normal extrusions. - layer_new.height = m_slicing_params.soluble_interface ? - // Align the interface layer with the object's layer height. - object.layers()[layer_id + 1]->height : - // Place a bridge flow interface layer or the normal flow interface layer over the top surface. - m_support_material_bottom_interface_flow.height(); - layer_new.print_z = m_slicing_params.soluble_interface ? object.layers()[layer_id + 1]->print_z : - layer.print_z + layer_new.height + m_slicing_params.gap_object_support; - layer_new.bottom_z = layer.print_z; - layer_new.idx_object_layer_below = layer_id; - layer_new.bridging = ! m_slicing_params.soluble_interface && m_object_config->thick_bridges; - //FIXME how much to inflate the bottom surface, as it is being extruded with a bridging flow? The following line uses a normal flow. - //FIXME why is the offset positive? It will be trimmed by the object later on anyway, but then it just wastes CPU clocks. - layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width()), SUPPORT_SURFACES_OFFSET_PARAMETERS); - if (! m_slicing_params.soluble_interface) { - // Walk the top surfaces, snap the top of the new bottom surface to the closest top of the top surface, - // so there will be no support surfaces generated with thickness lower than m_support_layer_height_min. - for (size_t top_idx = size_t(std::max(0, contact_idx)); - top_idx < top_contacts.size() && top_contacts[top_idx]->print_z < layer_new.print_z + this->m_support_layer_height_min + EPSILON; - ++ top_idx) { - if (top_contacts[top_idx]->print_z > layer_new.print_z - this->m_support_layer_height_min - EPSILON) { - // A top layer has been found, which is close to the new bottom layer. - coordf_t diff = layer_new.print_z - top_contacts[top_idx]->print_z; - assert(std::abs(diff) <= this->m_support_layer_height_min + EPSILON); - if (diff > 0.) { - // The top contact layer is below this layer. Make the bridging layer thinner to align with the existing top layer. - assert(diff < layer_new.height + EPSILON); - assert(layer_new.height - diff >= m_support_layer_height_min - EPSILON); - layer_new.print_z = top_contacts[top_idx]->print_z; - layer_new.height -= diff; - } else { - // The top contact layer is above this layer. One may either make this layer thicker or thinner. - // By making the layer thicker, one will decrease the number of discrete layers with the price of extruding a bit too thick bridges. - // By making the layer thinner, one adds one more discrete layer. - layer_new.print_z = top_contacts[top_idx]->print_z; - layer_new.height -= diff; - } - break; - } - } - } - #ifdef SLIC3R_DEBUG - Slic3r::SVG::export_expolygons( - debug_out_path("support-bottom-contacts-%d-%lf.svg", iRun, layer_new.print_z), - union_ex(layer_new.polygons, false)); - #endif /* SLIC3R_DEBUG */ - // Trim the already created base layers above the current layer intersecting with the new bottom contacts layer. - //FIXME Maybe this is no more needed, as the overlapping base layers are trimmed by the bottom layers at the final stage? - touching = offset(touching, float(SCALED_EPSILON)); - for (int layer_id_above = layer_id + 1; layer_id_above < int(object.total_layer_count()); ++ layer_id_above) { - const Layer &layer_above = *object.layers()[layer_id_above]; - if (layer_above.print_z > layer_new.print_z - EPSILON) - break; - if (! layer_support_areas[layer_id_above].empty()) { - #ifdef SLIC3R_DEBUG - SVG::export_expolygons(debug_out_path("support-support-areas-raw-before-trimming-%d-with-%f-%lf.svg", iRun, layer.print_z, layer_above.print_z), - { { { union_ex(touching, false) }, { "touching", "blue", 0.5f } }, - { { union_ex(layer_support_areas[layer_id_above], true) }, { "above", "red", "black", "", scaled(0.1f), 0.5f } } }); - #endif /* SLIC3R_DEBUG */ - layer_support_areas[layer_id_above] = diff(layer_support_areas[layer_id_above], touching); -#ifdef SLIC3R_DEBUG - Slic3r::SVG::export_expolygons( - debug_out_path("support-support-areas-raw-after-trimming-%d-with-%f-%lf.svg", iRun, layer.print_z, layer_above.print_z), - union_ex(layer_support_areas[layer_id_above], false)); -#endif /* SLIC3R_DEBUG */ - } - } - } - } // ! top.empty() - }); - - Polygons &layer_support_area = layer_support_areas[layer_id]; - task_group.run([this, &projection, &projection_raw, &layer, &layer_support_area -#ifdef SLIC3R_DEBUG - , layer_id -#endif /* SLIC3R_DEBUG */ - ] { - // Remove the areas that touched from the projection that will continue on next, lower, top surfaces. - // Polygons trimming = union_(to_polygons(layer.slices), touching, true); - Polygons trimming = offset(layer.lslices, float(SCALED_EPSILON)); - projection = diff(projection_raw, trimming, false); - #ifdef SLIC3R_DEBUG - SVG::export_expolygons(debug_out_path("support-support-areas-raw-%d-%lf.svg", iRun, layer.print_z), - { { { union_ex(trimming, false) }, { "trimming", "blue", 0.5f } }, - { { union_ex(projection, true) }, { "projection", "red", "black", "", scaled(0.1f), 0.5f } } }); - #endif /* SLIC3R_DEBUG */ - remove_sticks(projection); - remove_degenerate(projection); - #ifdef SLIC3R_DEBUG - SVG::export_expolygons(debug_out_path("support-support-areas-raw-cleaned-%d-%lf.svg", iRun, layer.print_z), - { { { union_ex(trimming, false) }, { "trimming", "blue", 0.5f } }, - { { union_ex(projection, false) }, { "projection", "red", "black", "", scaled(0.1f), 0.5f } } }); - #endif /* SLIC3R_DEBUG */ - SupportGridPattern support_grid_pattern( - // Support islands, to be stretched into a grid. - &projection, - // Trimming polygons, to trim the stretched support islands. - &trimming, - // Grid spacing. - m_object_config->support_material_spacing.value + m_support_material_flow.spacing(), - Geometry::deg2rad(m_object_config->support_material_angle.value), - m_support_material_flow.spacing()); - tbb::task_group task_group_inner; - // 1) Cache the slice of a support volume. The support volume is expanded by 1/2 of support material flow spacing - // to allow a placement of suppot zig-zag snake along the grid lines. - task_group_inner.run([this, &support_grid_pattern, &layer_support_area - #ifdef SLIC3R_DEBUG - , &layer, layer_id - #endif /* SLIC3R_DEBUG */ - ] { - layer_support_area = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 25, true - #ifdef SLIC3R_DEBUG - , "support_area", iRun, layer_id, layer.print_z - #endif // SLIC3R_DEBUG - ); - #ifdef SLIC3R_DEBUG - Slic3r::SVG::export_expolygons( - debug_out_path("support-layer_support_area-gridded-%d-%lf.svg", iRun, layer.print_z), - union_ex(layer_support_area, false)); - #endif /* SLIC3R_DEBUG */ - }); - // 2) Support polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells. - Polygons projection_new; - task_group_inner.run([&projection_new, &support_grid_pattern - #ifdef SLIC3R_DEBUG - , &layer, layer_id, &projection, &trimming - #endif /* SLIC3R_DEBUG */ - ] { - projection_new = support_grid_pattern.extract_support(-5, true - #ifdef SLIC3R_DEBUG - , "support_projection", iRun, layer_id, layer.print_z - #endif // SLIC3R_DEBUG - ); - #ifdef SLIC3R_DEBUG - Slic3r::SVG::export_expolygons( - debug_out_path("support-projection_new-gridded-%d-%lf.svg", iRun, layer.print_z), - union_ex(projection_new, false)); - #endif /* SLIC3R_DEBUG */ - #ifdef SLIC3R_DEBUG - SVG::export_expolygons(debug_out_path("support-projection_new-gridded-%d-%lf.svg", iRun, layer.print_z), - { { { union_ex(trimming, false) }, { "trimming", "gray", 0.5f } }, - { { union_ex(projection, true) }, { "projection", "blue", 0.5f } }, - { { union_ex(projection_new, true) }, { "projection_new", "red", "black", "", scaled(0.1f), 0.5f } } }); - #endif /* SLIC3R_DEBUG */ - }); - task_group_inner.wait(); - projection = std::move(projection_new); - }); - task_group.wait(); + // These are the overhang surfaces. They are touching the object and they are not expanded away from the object. + // Use a slight positive offset to overlap the touching regions. + polygons_append(polygons_new, offset(*top_contact.overhang_polygons, float(SCALED_EPSILON))); + polygons_append(overhangs_projection, union_(polygons_new)); + polygons_append(enforcers_projection, enforcers_new); } - std::reverse(bottom_contacts.begin(), bottom_contacts.end()); - trim_support_layers_by_object(object, bottom_contacts, m_slicing_params.gap_support_object, m_slicing_params.gap_object_support, m_gap_xy); - } // ! top_contacts.empty() + if (overhangs_projection.empty() && enforcers_projection.empty()) + continue; + // Overhangs_projection will be filled in asynchronously, move it away. + Polygons overhangs_projection_raw = union_(std::move(overhangs_projection)); + Polygons enforcers_projection_raw = union_(std::move(enforcers_projection)); + + tbb::task_group task_group; + const Polygons &overhangs_for_bottom_contacts = buildplate_only ? enforcers_projection_raw : overhangs_projection_raw; + if (! overhangs_for_bottom_contacts.empty()) + // Find the bottom contact layers above the top surfaces of this layer. + task_group.run([this, &object, &layer, &top_contacts, contact_idx, &layer_storage, &layer_support_areas, &bottom_contacts, &overhangs_for_bottom_contacts + #ifdef SLIC3R_DEBUG + , iRun, &polygons_new + #endif // SLIC3R_DEBUG + ] { + // Find the bottom contact layers above the top surfaces of this layer. + MyLayer *layer_new = detect_bottom_contacts( + m_slicing_params, m_support_params, object, layer, top_contacts, contact_idx, layer_storage, layer_support_areas, overhangs_for_bottom_contacts +#ifdef SLIC3R_DEBUG + , iRun, polygons_new +#endif // SLIC3R_DEBUG + ); + if (layer_new) + bottom_contacts.push_back(layer_new); + }); + + Polygons &layer_support_area = layer_support_areas[layer_id]; + Polygons *layer_buildplate_covered = buildplate_covered.empty() ? nullptr : &buildplate_covered[layer_id]; + task_group.run([&grid_params, &overhangs_projection, &overhangs_projection_raw, &layer, &layer_support_area, layer_buildplate_covered +#ifdef SLIC3R_DEBUG + , iRun, layer_id +#endif /* SLIC3R_DEBUG */ + ] { + // buildplate_covered[layer_id] will be consumed here. + std::tie(layer_support_area, overhangs_projection) = project_support_to_grid(layer, grid_params, overhangs_projection_raw, layer_buildplate_covered +#ifdef SLIC3R_DEBUG + , iRun, layer_id, "general" +#endif /* SLIC3R_DEBUG */ + ); + }); + + Polygons layer_support_area_enforcers; + if (! enforcers_projection.empty()) + // Project the enforcers polygons downwards, don't trim them with the "buildplate only" polygons. + task_group.run([&grid_params, &enforcers_projection, &enforcers_projection_raw, &layer, &layer_support_area_enforcers +#ifdef SLIC3R_DEBUG + , iRun, layer_id +#endif /* SLIC3R_DEBUG */ + ]{ + std::tie(layer_support_area_enforcers, enforcers_projection) = project_support_to_grid(layer, grid_params, enforcers_projection_raw, nullptr +#ifdef SLIC3R_DEBUG + , iRun, layer_id, "enforcers" +#endif /* SLIC3R_DEBUG */ + ); + }); + + task_group.wait(); + + if (! layer_support_area_enforcers.empty()) { + if (layer_support_area.empty()) + layer_support_area = std::move(layer_support_area_enforcers); + else + layer_support_area = union_(layer_support_area, layer_support_area_enforcers); + } + } // over all layers downwards + + std::reverse(bottom_contacts.begin(), bottom_contacts.end()); + trim_support_layers_by_object(object, bottom_contacts, m_slicing_params.gap_support_object, m_slicing_params.gap_object_support, m_support_params.gap_xy); return bottom_contacts; } @@ -2173,7 +2401,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int // Verify that the extremes are separated by m_support_layer_height_min. for (size_t i = 1; i < extremes.size(); ++ i) { assert(extremes[i]->extreme_z() - extremes[i-1]->extreme_z() == 0. || - extremes[i]->extreme_z() - extremes[i-1]->extreme_z() > m_support_layer_height_min - EPSILON); + extremes[i]->extreme_z() - extremes[i-1]->extreme_z() > m_support_params.support_layer_height_min - EPSILON); assert(extremes[i]->extreme_z() - extremes[i-1]->extreme_z() > 0. || extremes[i]->layer_type == extremes[i-1]->layer_type || (extremes[i]->layer_type == sltBottomContact && extremes[i - 1]->layer_type == sltTopContact)); @@ -2197,7 +2425,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int // This is a bottom of a synchronized (or soluble) top contact layer, its height has been decided in this->top_contact_layers(). assert(extr2->layer_type == sltTopContact); assert(extr2->bottom_z == m_slicing_params.first_print_layer_height); - assert(extr2->print_z >= m_slicing_params.first_print_layer_height + m_support_layer_height_min - EPSILON); + assert(extr2->print_z >= m_slicing_params.first_print_layer_height + m_support_params.support_layer_height_min - EPSILON); if (intermediate_layers.empty() || intermediate_layers.back()->print_z < m_slicing_params.first_print_layer_height) { MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate); layer_new.bottom_z = 0.; @@ -2233,7 +2461,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int if (dist == 0.) continue; // The new layers shall be at least m_support_layer_height_min thick. - assert(dist >= m_support_layer_height_min - EPSILON); + assert(dist >= m_support_params.support_layer_height_min - EPSILON); if (synchronize) { // Emit support layers synchronized with the object layers. // Find the first object layer, which has its print_z in this support Z range. @@ -2262,10 +2490,10 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int assert(n_layers_extra > 0); coordf_t step = dist / coordf_t(n_layers_extra); if (extr1 != nullptr && extr1->layer_type == sltTopContact && - extr1->print_z + m_support_layer_height_min > extr1->bottom_z + step) { + extr1->print_z + m_support_params.support_layer_height_min > extr1->bottom_z + step) { // The bottom extreme is a bottom of a top surface. Ensure that the gap // between the 1st intermediate layer print_z and extr1->print_z is not too small. - assert(extr1->bottom_z + m_support_layer_height_min < extr1->print_z + EPSILON); + assert(extr1->bottom_z + m_support_params.support_layer_height_min < extr1->print_z + EPSILON); // Generate the first intermediate layer. MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate); layer_new.bottom_z = extr1->bottom_z; @@ -2456,7 +2684,7 @@ void PrintObjectSupportMaterial::generate_base_layers( ++ iRun; #endif /* SLIC3R_DEBUG */ - this->trim_support_layers_by_object(object, intermediate_layers, m_slicing_params.gap_support_object, m_slicing_params.gap_object_support, m_gap_xy); + this->trim_support_layers_by_object(object, intermediate_layers, m_slicing_params.gap_support_object, m_slicing_params.gap_object_support, m_support_params.gap_xy); } void PrintObjectSupportMaterial::trim_support_layers_by_object( @@ -2646,16 +2874,16 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf new_layer.bottom_z = print_z; new_layer.polygons = interface_polygons; //FIXME misusing contact_polygons for support columns. - new_layer.contact_polygons = new Polygons(columns); + new_layer.contact_polygons = std::make_unique(columns); } } else if (columns_base != nullptr) { // Expand the bases of the support columns in the 1st layer. { Polygons &raft = columns_base->polygons; - Polygons trimming = offset(m_object->layers().front()->lslices, (float)scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS); + Polygons trimming = offset(m_object->layers().front()->lslices, (float)scale_(m_support_params.gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS); if (inflate_factor_1st_layer > SCALED_EPSILON) { // Inflate in multiple steps to avoid leaking of the support 1st layer through object walls. - auto nsteps = std::max(5, int(ceil(inflate_factor_1st_layer / m_first_layer_flow.scaled_width()))); + auto nsteps = std::max(5, int(ceil(inflate_factor_1st_layer / m_support_params.first_layer_flow.scaled_width()))); float step = inflate_factor_1st_layer / nsteps; for (int i = 0; i < nsteps; ++ i) raft = diff(offset(raft, step), trimming); @@ -3495,16 +3723,16 @@ void PrintObjectSupportMaterial::generate_toolpaths( const MyLayersPtr &base_interface_layers) const { // loop_interface_processor with a given circle radius. - LoopInterfaceProcessor loop_interface_processor(1.5 * m_support_material_interface_flow.scaled_width()); + LoopInterfaceProcessor loop_interface_processor(1.5 * m_support_params.support_material_interface_flow.scaled_width()); loop_interface_processor.n_contact_loops = this->has_contact_loops() ? 1 : 0; float base_angle = Geometry::deg2rad(float(m_object_config->support_material_angle.value)); float interface_angle = Geometry::deg2rad(float(m_object_config->support_material_angle.value + 90.)); - coordf_t interface_spacing = m_object_config->support_material_interface_spacing.value + m_support_material_interface_flow.spacing(); - coordf_t interface_density = std::min(1., m_support_material_interface_flow.spacing() / interface_spacing); - coordf_t support_spacing = m_object_config->support_material_spacing.value + m_support_material_flow.spacing(); - coordf_t support_density = std::min(1., m_support_material_flow.spacing() / support_spacing); - if (m_slicing_params.soluble_interface) { + coordf_t interface_spacing = m_object_config->support_material_interface_spacing.value + m_support_params.support_material_interface_flow.spacing(); + coordf_t interface_density = std::min(1., m_support_params.support_material_interface_flow.spacing() / interface_spacing); + coordf_t support_spacing = m_object_config->support_material_spacing.value + m_support_params.support_material_flow.spacing(); + coordf_t support_density = std::min(1., m_support_params.support_material_flow.spacing() / support_spacing); + if (m_object_config->support_material_interface_layers.value == 0) { // No interface layers allowed, print everything with the base support pattern. interface_spacing = support_spacing; interface_density = support_density; @@ -3578,10 +3806,10 @@ void PrintObjectSupportMaterial::generate_toolpaths( ((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons); if (! to_infill_polygons.empty()) { assert(! raft_layer.bridging); - Flow flow(float(m_support_material_flow.width()), float(raft_layer.height), m_support_material_flow.nozzle_diameter()); + Flow flow(float(m_support_params.support_material_flow.width()), float(raft_layer.height), m_support_params.support_material_flow.nozzle_diameter()); Fill * filler = filler_support.get(); filler->angle = raft_angle_base; - filler->spacing = m_support_material_flow.spacing(); + filler->spacing = m_support_params.support_material_flow.spacing(); filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_density)); fill_expolygons_with_sheath_generate_paths( // Destination @@ -3597,20 +3825,20 @@ void PrintObjectSupportMaterial::generate_toolpaths( } Fill *filler = filler_interface.get(); - Flow flow = m_first_layer_flow; + Flow flow = m_support_params.first_layer_flow; float density = 0.f; if (support_layer_id == 0) { // Base flange. filler->angle = raft_angle_1st_layer; - filler->spacing = m_first_layer_flow.spacing(); + filler->spacing = m_support_params.first_layer_flow.spacing(); density = float(m_object_config->raft_first_layer_density.value * 0.01); } else if (support_layer_id >= m_slicing_params.base_raft_layers) { filler->angle = raft_angle_interface; // We don't use $base_flow->spacing because we need a constant spacing // value that guarantees that all layers are correctly aligned. - filler->spacing = m_support_material_flow.spacing(); + filler->spacing = m_support_params.support_material_flow.spacing(); assert(! raft_layer.bridging); - flow = Flow(float(m_support_material_interface_flow.width()), float(raft_layer.height), m_support_material_flow.nozzle_diameter()); + flow = Flow(float(m_support_params.support_material_interface_flow.width()), float(raft_layer.height), m_support_params.support_material_flow.nozzle_diameter()); density = float(interface_density); } else continue; @@ -3717,21 +3945,21 @@ void PrintObjectSupportMaterial::generate_toolpaths( if (m_object_config->support_material_interface_layers == 0) { // If no interface layers were requested, we treat the contact layer exactly as a generic base layer. - if (m_can_merge_support_regions) { + if (m_support_params.can_merge_support_regions) { if (base_layer.could_merge(top_contact_layer)) base_layer.merge(std::move(top_contact_layer)); else if (base_layer.empty() && !top_contact_layer.empty() && !top_contact_layer.layer->bridging) std::swap(base_layer, top_contact_layer); } } else { - loop_interface_processor.generate(top_contact_layer, m_support_material_interface_flow); + loop_interface_processor.generate(top_contact_layer, m_support_params.support_material_interface_flow); // If no loops are allowed, we treat the contact layer exactly as a generic interface layer. // Merge interface_layer into top_contact_layer, as the top_contact_layer is not synchronized and therefore it will be used // to trim other layers. if (top_contact_layer.could_merge(interface_layer)) top_contact_layer.merge(std::move(interface_layer)); } - if ((m_object_config->support_material_interface_layers == 0 || m_object_config->support_material_bottom_interface_layers == 0) && m_can_merge_support_regions) { + if ((m_object_config->support_material_interface_layers == 0 || m_object_config->support_material_bottom_interface_layers == 0) && m_support_params.can_merge_support_regions) { if (base_layer.could_merge(bottom_contact_layer)) base_layer.merge(std::move(bottom_contact_layer)); else if (base_layer.empty() && !bottom_contact_layer.empty() && !bottom_contact_layer.layer->bridging) @@ -3755,18 +3983,18 @@ void PrintObjectSupportMaterial::generate_toolpaths( MyLayerExtruded &layer_ex = (i == 0) ? top_contact_layer : (i == 1 ? bottom_contact_layer : interface_layer); if (layer_ex.empty() || layer_ex.polygons_to_extrude().empty()) continue; - bool interface_as_base = (&layer_ex == &interface_layer) && m_slicing_params.soluble_interface; + bool interface_as_base = (&layer_ex == &interface_layer) && m_object_config->support_material_interface_layers.value == 0; //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b) auto interface_flow = layer_ex.layer->bridging ? - Flow::bridging_flow(layer_ex.layer->height, m_support_material_bottom_interface_flow.nozzle_diameter()) : - (interface_as_base ? &m_support_material_flow : &m_support_material_interface_flow)->with_height(float(layer_ex.layer->height)); + Flow::bridging_flow(layer_ex.layer->height, m_support_params.support_material_bottom_interface_flow.nozzle_diameter()) : + (interface_as_base ? &m_support_params.support_material_flow : &m_support_params.support_material_interface_flow)->with_height(float(layer_ex.layer->height)); filler_interface->angle = interface_as_base ? // If zero interface layers are configured, use the same angle as for the base layers. angles[support_layer_id % angles.size()] : // Use interface angle for the interface layers. interface_angle; - filler_interface->spacing = m_support_material_interface_flow.spacing(); + filler_interface->spacing = m_support_params.support_material_interface_flow.spacing(); filler_interface->link_max_length = coord_t(scale_(filler_interface->spacing * link_max_length_factor / interface_density)); fill_expolygons_generate_paths( // Destination @@ -3785,9 +4013,9 @@ void PrintObjectSupportMaterial::generate_toolpaths( //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b) assert(! base_interface_layer.layer->bridging); - Flow interface_flow = m_support_material_flow.with_height(float(base_interface_layer.layer->height)); + Flow interface_flow = m_support_params.support_material_flow.with_height(float(base_interface_layer.layer->height)); filler->angle = interface_angle; - filler->spacing = m_support_material_interface_flow.spacing(); + filler->spacing = m_support_params.support_material_interface_flow.spacing(); filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / interface_density)); fill_expolygons_generate_paths( // Destination @@ -3808,8 +4036,8 @@ void PrintObjectSupportMaterial::generate_toolpaths( // We don't use $base_flow->spacing because we need a constant spacing // value that guarantees that all layers are correctly aligned. assert(! base_layer.layer->bridging); - auto flow = m_support_material_flow.with_height(float(base_layer.layer->height)); - filler->spacing = m_support_material_flow.spacing(); + auto flow = m_support_params.support_material_flow.with_height(float(base_layer.layer->height)); + filler->spacing = m_support_params.support_material_flow.spacing(); filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_density)); float density = float(support_density); bool sheath = with_sheath; @@ -3819,7 +4047,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( filler = filler_first_layer; filler->angle = Geometry::deg2rad(float(m_object_config->support_material_angle.value + 90.)); density = float(m_object_config->raft_first_layer_density.value * 0.01); - flow = m_first_layer_flow; + flow = m_support_params.first_layer_flow; // use the proper spacing for first layer as we don't need to align // its pattern to the other layers //FIXME When paralellizing, each thread shall have its own copy of the fillers. diff --git a/src/libslic3r/SupportMaterial.hpp b/src/libslic3r/SupportMaterial.hpp index da13768f6..a1bd81297 100644 --- a/src/libslic3r/SupportMaterial.hpp +++ b/src/libslic3r/SupportMaterial.hpp @@ -48,39 +48,8 @@ public: class MyLayer { public: - MyLayer() : - layer_type(sltUnknown), - print_z(0.), - bottom_z(0.), - height(0.), - idx_object_layer_above(size_t(-1)), - idx_object_layer_below(size_t(-1)), - bridging(false), - contact_polygons(nullptr), - overhang_polygons(nullptr) - {} - - ~MyLayer() - { - delete contact_polygons; - contact_polygons = nullptr; - delete overhang_polygons; - overhang_polygons = nullptr; - } - void reset() { - layer_type = sltUnknown; - print_z = 0.; - bottom_z = 0.; - height = 0.; - idx_object_layer_above = size_t(-1); - idx_object_layer_below = size_t(-1); - bridging = false; - polygons.clear(); - delete contact_polygons; - contact_polygons = nullptr; - delete overhang_polygons; - overhang_polygons = nullptr; + *this = MyLayer(); } bool operator==(const MyLayer &layer2) const { @@ -103,6 +72,21 @@ public: return false; } + void merge(MyLayer &&rhs) { + // The union_() does not support move semantic yet, but maybe one day it will. + this->polygons = union_(this->polygons, std::move(rhs.polygons)); + auto merge = [](std::unique_ptr &dst, std::unique_ptr &src) { + if (! dst || dst->empty()) + dst = std::move(src); + else if (src && ! src->empty()) + *dst = union_(*dst, std::move(*src)); + }; + merge(this->contact_polygons, rhs.contact_polygons); + merge(this->overhang_polygons, rhs.overhang_polygons); + merge(this->enforcer_polygons, rhs.enforcer_polygons); + rhs.reset(); + } + // For the bridging flow, bottom_print_z will be above bottom_z to account for the vertical separation. // For the non-bridging flow, bottom_print_z will be equal to bottom_z. coordf_t bottom_print_z() const { return print_z - height; } @@ -110,29 +94,44 @@ public: // To sort the extremes of top / bottom interface layers. coordf_t extreme_z() const { return (this->layer_type == sltTopContact) ? this->bottom_z : this->print_z; } - SupporLayerType layer_type; + SupporLayerType layer_type { sltUnknown }; // Z used for printing, in unscaled coordinates. - coordf_t print_z; + coordf_t print_z { 0 }; // Bottom Z of this layer. For soluble layers, bottom_z + height = print_z, // otherwise bottom_z + gap + height = print_z. - coordf_t bottom_z; + coordf_t bottom_z { 0 }; // Layer height in unscaled coordinates. - coordf_t height; + coordf_t height { 0 }; // Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers. // If this is not a contact layer, it will be set to size_t(-1). - size_t idx_object_layer_above; + size_t idx_object_layer_above { size_t(-1) }; // Index of a PrintObject layer_id, which supports this layer. This will be set for bottom contact layers. // If this is not a contact layer, it will be set to size_t(-1). - size_t idx_object_layer_below; + size_t idx_object_layer_below { size_t(-1) }; // Use a bridging flow when printing this support layer. - bool bridging; + bool bridging { false }; // Polygons to be filled by the support pattern. Polygons polygons; // Currently for the contact layers only. - // MyLayer owns the contact_polygons and overhang_polygons, they are freed by the destructor. - Polygons *contact_polygons; - Polygons *overhang_polygons; + std::unique_ptr contact_polygons; + std::unique_ptr overhang_polygons; + // Enforcers need to be propagated independently in case the "support on build plate only" option is enabled. + std::unique_ptr enforcer_polygons; + }; + + struct SupportParams { + Flow first_layer_flow; + Flow support_material_flow; + Flow support_material_interface_flow; + Flow support_material_bottom_interface_flow; + // Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder? + bool can_merge_support_regions; + + coordf_t support_layer_height_min; + // coordf_t support_layer_height_max; + + coordf_t gap_xy; }; // Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained @@ -159,17 +158,19 @@ public: void generate(PrintObject &object); private: + std::vector buildplate_covered(const PrintObject &object) const; + // Generate top contact layers supporting overhangs. // For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined. // If supports over bed surface only are requested, don't generate contact layers over an object. - MyLayersPtr top_contact_layers(const PrintObject &object, MyLayerStorage &layer_storage) const; + MyLayersPtr top_contact_layers(const PrintObject &object, const std::vector &buildplate_covered, MyLayerStorage &layer_storage) const; // Generate bottom contact layers supporting the top contact layers. // For a soluble interface material synchronize the layer heights with the object, // otherwise set the layer height to a bridging flow of a support interface nozzle. MyLayersPtr bottom_contact_layers_and_layer_support_areas( - const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage, - std::vector &layer_support_areas) const; + const PrintObject &object, const MyLayersPtr &top_contacts, std::vector &buildplate_covered, + MyLayerStorage &layer_storage, std::vector &layer_support_areas) const; // Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them. void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const MyLayersPtr &bottom_contacts, MyLayersPtr &top_contacts) const; @@ -240,18 +241,8 @@ private: // Pre-calculated parameters shared between the object slicer and the support generator, // carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc. SlicingParameters m_slicing_params; - - Flow m_first_layer_flow; - Flow m_support_material_flow; - Flow m_support_material_interface_flow; - Flow m_support_material_bottom_interface_flow; - // Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder? - bool m_can_merge_support_regions; - - coordf_t m_support_layer_height_min; -// coordf_t m_support_layer_height_max; - - coordf_t m_gap_xy; + // Various precomputed support parameters to be shared with external functions. + SupportParams m_support_params; }; } // namespace Slic3r From 4745948b84fc9f8a164867e9b1bc3de953c2d6c4 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 19 Mar 2021 11:35:09 +0100 Subject: [PATCH 29/32] Fixed compilation on Linux --- src/slic3r/GUI/Tab.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 6916c04a5..c68de4984 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3841,7 +3841,7 @@ bool Tab::validate_custom_gcodes() assert(opt_group->opt_map().size() == 1); std::string key = opt_group->opt_map().begin()->first; std::string value = boost::any_cast(opt_group->get_value(key)); - std::string config_value = m_type == Preset::TYPE_FILAMENT ? m_config->opt_string(key, unsigned int(0)) : m_config->opt_string(key); + std::string config_value = m_type == Preset::TYPE_FILAMENT ? m_config->opt_string(key, 0u) : m_config->opt_string(key); valid &= validate_custom_gcode(opt_group->title, value); Field* field = opt_group->get_field(key); TextCtrl* text_ctrl = dynamic_cast(field); From 6f3f3624c54e3cf2b508ca4558f2357604318d81 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 19 Mar 2021 13:39:20 +0100 Subject: [PATCH 30/32] Fixing FDM support bottom zero interface layers, where one dense interface layer was errorneously created. Follow-up to 00db3dc41937670e91c26170c9c168bf75a86aaf 73b88e6ce0fa21a031490f148751114af73d573c referencing the issues fixed by the above commits: Fixes Allow to disable raft under support structures. #3772 Fixes Separate counts for floor and roof support interface layers #4288 --- src/libslic3r/SupportMaterial.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index d721e12ca..c01da8f91 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -1388,7 +1388,7 @@ static inline std::tuple detect_overhangs( // Generate overhang / contact_polygons for non-raft layers. const Layer &lower_layer = *layer.lower_layer; const bool has_enforcer = ! annotations.enforcers_layers.empty() && ! annotations.enforcers_layers[layer_id].empty(); - float fw; + float fw = 0; for (LayerRegion *layerm : layer.regions()) { // Extrusion width accounts for the roundings of the extrudates. // It is the maximum widh of the extrudate. @@ -1849,8 +1849,6 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // Output layers, sorted by top Z. MyLayersPtr contact_out; - const bool support_auto = m_object_config->support_material.value && m_object_config->support_material_auto.value; - BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() in parallel - start"; // Determine top contact areas. // If generating raft only (no support), only calculate top contact areas for the 0th layer. @@ -1863,7 +1861,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ contact_out.assign(num_layers * 2, nullptr); tbb::spin_mutex layer_storage_mutex; tbb::parallel_for(tbb::blocked_range(this->has_raft() ? 0 : 1, num_layers), - [this, &object, &annotations, support_auto, &layer_storage, &layer_storage_mutex, &contact_out] + [this, &object, &annotations, &layer_storage, &layer_storage_mutex, &contact_out] (const tbb::blocked_range& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { @@ -3944,12 +3942,12 @@ void PrintObjectSupportMaterial::generate_toolpaths( base_layer.layer = intermediate_layers[idx_layer_intermediate]; if (m_object_config->support_material_interface_layers == 0) { - // If no interface layers were requested, we treat the contact layer exactly as a generic base layer. + // If no top interface layers were requested, we treat the contact layer exactly as a generic base layer. if (m_support_params.can_merge_support_regions) { if (base_layer.could_merge(top_contact_layer)) base_layer.merge(std::move(top_contact_layer)); - else if (base_layer.empty() && !top_contact_layer.empty() && !top_contact_layer.layer->bridging) - std::swap(base_layer, top_contact_layer); + else if (base_layer.empty()) + base_layer = std::move(top_contact_layer); } } else { loop_interface_processor.generate(top_contact_layer, m_support_params.support_material_interface_flow); @@ -3962,8 +3960,8 @@ void PrintObjectSupportMaterial::generate_toolpaths( if ((m_object_config->support_material_interface_layers == 0 || m_object_config->support_material_bottom_interface_layers == 0) && m_support_params.can_merge_support_regions) { if (base_layer.could_merge(bottom_contact_layer)) base_layer.merge(std::move(bottom_contact_layer)); - else if (base_layer.empty() && !bottom_contact_layer.empty() && !bottom_contact_layer.layer->bridging) - std::swap(base_layer, bottom_contact_layer); + else if (base_layer.empty() && ! bottom_contact_layer.empty() && ! bottom_contact_layer.layer->bridging) + base_layer = std::move(bottom_contact_layer); } #if 0 @@ -3983,7 +3981,8 @@ void PrintObjectSupportMaterial::generate_toolpaths( MyLayerExtruded &layer_ex = (i == 0) ? top_contact_layer : (i == 1 ? bottom_contact_layer : interface_layer); if (layer_ex.empty() || layer_ex.polygons_to_extrude().empty()) continue; - bool interface_as_base = (&layer_ex == &interface_layer) && m_object_config->support_material_interface_layers.value == 0; + bool interface_as_base = m_object_config->support_material_interface_layers.value == 0 || + (m_object_config->support_material_bottom_interface_layers == 0 && &layer_ex == &bottom_contact_layer); //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b) auto interface_flow = layer_ex.layer->bridging ? @@ -3994,21 +3993,22 @@ void PrintObjectSupportMaterial::generate_toolpaths( angles[support_layer_id % angles.size()] : // Use interface angle for the interface layers. interface_angle; - filler_interface->spacing = m_support_params.support_material_interface_flow.spacing(); - filler_interface->link_max_length = coord_t(scale_(filler_interface->spacing * link_max_length_factor / interface_density)); + double density = interface_as_base ? support_density : interface_density; + filler_interface->spacing = interface_as_base ? m_support_params.support_material_flow.spacing() : m_support_params.support_material_interface_flow.spacing(); + filler_interface->link_max_length = coord_t(scale_(filler_interface->spacing * link_max_length_factor / density)); fill_expolygons_generate_paths( // Destination layer_ex.extrusions, // Regions to fill union_ex(layer_ex.polygons_to_extrude(), true), // Filler and its parameters - filler_interface.get(), float(interface_density), + filler_interface.get(), float(density), // Extrusion parameters erSupportMaterialInterface, interface_flow); } // Base interface layers under soluble interfaces - if ( ! base_interface_layer.empty() && ! base_interface_layer.polygons_to_extrude().empty()){ + if ( ! base_interface_layer.empty() && ! base_interface_layer.polygons_to_extrude().empty()) { Fill *filler = filler_base_interface.get(); //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b) From 8295f81685f1c937af5bd4978da1e3dd5687b6db Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 19 Mar 2021 13:39:54 +0100 Subject: [PATCH 31/32] Fix mac warnings --- src/libslic3r/MeshBoolean.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index da5c95638..8a03310fe 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -155,7 +155,7 @@ template TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh) int i = 0; Vec3i facet; - for (const auto &v : vtc) { + for (auto v : vtc) { if (i > 2) { i = 0; break; } facet(i++) = v; } From aa2d8915ef9702eb3c1f33bb958cf19412ee9445 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 19 Mar 2021 14:02:20 +0100 Subject: [PATCH 32/32] Fix crash caused by boost log trivial on mac --- src/libslic3r/MeshBoolean.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index 8a03310fe..25ee3e4af 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -1,7 +1,6 @@ #include "Exception.hpp" #include "MeshBoolean.hpp" #include "libslic3r/TriangleMesh.hpp" -#include "boost/log/trivial.hpp" #undef PI // Include igl first. It defines "L" macro which then clashes with our localization @@ -160,11 +159,8 @@ template TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh) facet(i++) = v; } - if (i == 3) { + if (i == 3) facets.emplace_back(facet); - } else { - BOOST_LOG_TRIVIAL(error) << "CGAL face is not a triangle."; - } } TriangleMesh out{points, facets};