From e3c33844d5d89b2bf61aa781cca2cb963d9bef59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 19 Apr 2021 07:01:11 +0200 Subject: [PATCH 01/26] WIP: Duplicated the FDM support gizmo for the MMU segmentation --- src/libslic3r/Format/3mf.cpp | 18 +- src/libslic3r/Model.cpp | 13 + src/libslic3r/Model.hpp | 34 +- src/libslic3r/Print.cpp | 2 + src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/GLCanvas3D.cpp | 12 +- .../GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 358 ++++++++++++++++++ .../GUI/Gizmos/GLGizmoMmuSegmentation.hpp | 46 +++ src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmos.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 23 +- src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 1 + src/slic3r/GUI/Plater.cpp | 6 +- 14 files changed, 498 insertions(+), 25 deletions(-) create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 152d72079f..631f4769c4 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -95,6 +95,7 @@ static constexpr const char* PRINTABLE_ATTR = "printable"; static constexpr const char* INSTANCESCOUNT_ATTR = "instances_count"; static constexpr const char* CUSTOM_SUPPORTS_ATTR = "slic3rpe:custom_supports"; static constexpr const char* CUSTOM_SEAM_ATTR = "slic3rpe:custom_seam"; +static constexpr const char* MMU_SEGMENTATION_ATTR = "slic3rpe:mmu_segmentation"; static constexpr const char* KEY_ATTR = "key"; static constexpr const char* VALUE_ATTR = "value"; @@ -289,6 +290,7 @@ namespace Slic3r { std::vector triangles; std::vector custom_supports; std::vector custom_seam; + std::vector mmu_segmentation; bool empty() { return vertices.empty() || triangles.empty(); } @@ -297,6 +299,7 @@ namespace Slic3r { triangles.clear(); custom_supports.clear(); custom_seam.clear(); + mmu_segmentation.clear(); } }; @@ -1564,6 +1567,7 @@ namespace Slic3r { m_curr_object.geometry.custom_supports.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SUPPORTS_ATTR)); m_curr_object.geometry.custom_seam.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SEAM_ATTR)); + m_curr_object.geometry.mmu_segmentation.push_back(get_attribute_value_string(attributes, num_attributes, MMU_SEGMENTATION_ATTR)); return true; } @@ -1888,15 +1892,18 @@ namespace Slic3r { volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object); volume->calculate_convex_hull(); - // recreate custom supports and seam from previously loaded attribute + // recreate custom supports, seam and mmu segmentation from previously loaded attribute for (unsigned i=0; isupported_facets.set_triangle_from_string(i, geometry.custom_supports[index]); if (! geometry.custom_seam[index].empty()) volume->seam_facets.set_triangle_from_string(i, geometry.custom_seam[index]); + if (! geometry.mmu_segmentation[index].empty()) + volume->mmu_segmentation_facets.set_triangle_from_string(i, geometry.mmu_segmentation[index]); } @@ -2530,6 +2537,15 @@ namespace Slic3r { output_buffer += "\""; } + std::string mmu_painting_data_string = volume->mmu_segmentation_facets.get_triangle_as_string(i); + if (! mmu_painting_data_string.empty()) { + output_buffer += " "; + output_buffer += MMU_SEGMENTATION_ATTR; + output_buffer += "=\""; + output_buffer += mmu_painting_data_string; + output_buffer += "\""; + } + output_buffer += "/>\n"; if (! flush()) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index d48443181f..ddfa26552c 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1086,6 +1086,7 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con vol->supported_facets.assign(volume->supported_facets); vol->seam_facets.assign(volume->seam_facets); + vol->mmu_segmentation_facets.assign(volume->mmu_segmentation_facets); // Perform conversion only if the target "imperial" state is different from the current one. // This check supports conversion of "mixed" set of volumes, each with different "imperial" state. @@ -1187,6 +1188,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b volume->supported_facets.clear(); volume->seam_facets.clear(); + volume->mmu_segmentation_facets.clear(); if (! volume->is_model_part()) { // Modifiers are not cut, but we still need to add the instance transformation @@ -1780,6 +1782,7 @@ void ModelVolume::assign_new_unique_ids_recursive() config.set_new_unique_id(); supported_facets.set_new_unique_id(); seam_facets.set_new_unique_id(); + mmu_segmentation_facets.set_new_unique_id(); } void ModelVolume::rotate(double angle, Axis axis) @@ -2096,6 +2099,16 @@ bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo return false; } +bool model_mmu_segmentation_data_changed(const ModelObject& mo, const ModelObject& mo_new) { + assert(! model_volume_list_changed(mo, mo_new, ModelVolumeType::MODEL_PART)); + assert(mo.volumes.size() == mo_new.volumes.size()); + for (size_t i=0; immu_segmentation_facets.timestamp_matches(mo.volumes[i]->mmu_segmentation_facets)) + return true; + } + return false; +} + extern bool model_has_multi_part_objects(const Model &model) { for (const ModelObject *model_object : model.objects) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 868639ee80..0fce954ff3 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -588,6 +588,9 @@ public: // List of seam enforcers/blockers. FacetsAnnotation seam_facets; + // List of mesh facets painted for MMU segmentation. + FacetsAnnotation mmu_segmentation_facets; + // A parent object owning this modifier volume. ModelObject* get_object() const { return this->object; } ModelVolumeType type() const { return m_type; } @@ -675,6 +678,7 @@ public: this->config.set_new_unique_id(); this->supported_facets.set_new_unique_id(); this->seam_facets.set_new_unique_id(); + this->mmu_segmentation_facets.set_new_unique_id(); } protected: @@ -713,22 +717,26 @@ private: assert(this->id().valid()); assert(this->config.id().valid()); assert(this->supported_facets.id().valid()); - assert(this->seam_facets.id().valid()); + assert(this->seam_facets.id().valid()); + assert(this->mmu_segmentation_facets.id().valid()); assert(this->id() != this->config.id()); assert(this->id() != this->supported_facets.id()); assert(this->id() != this->seam_facets.id()); + assert(this->id() != this->mmu_segmentation_facets.id()); if (mesh.stl.stats.number_of_facets > 1) calculate_convex_hull(); } ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(ModelVolumeType::MODEL_PART), object(object) { assert(this->id().valid()); - assert(this->config.id().valid()); - assert(this->supported_facets.id().valid()); - assert(this->seam_facets.id().valid()); + assert(this->config.id().valid()); + assert(this->supported_facets.id().valid()); + assert(this->seam_facets.id().valid()); + assert(this->mmu_segmentation_facets.id().valid()); assert(this->id() != this->config.id()); assert(this->id() != this->supported_facets.id()); assert(this->id() != this->seam_facets.id()); + assert(this->id() != this->mmu_segmentation_facets.id()); } // Copying an existing volume, therefore this volume will get a copy of the ID assigned. @@ -736,19 +744,22 @@ private: ObjectBase(other), name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation), - supported_facets(other.supported_facets), seam_facets(other.seam_facets) + supported_facets(other.supported_facets), seam_facets(other.seam_facets), mmu_segmentation_facets(other.mmu_segmentation_facets) { assert(this->id().valid()); assert(this->config.id().valid()); assert(this->supported_facets.id().valid()); assert(this->seam_facets.id().valid()); + assert(this->mmu_segmentation_facets.id().valid()); assert(this->id() != this->config.id()); assert(this->id() != this->supported_facets.id()); assert(this->id() != this->seam_facets.id()); + assert(this->id() != this->mmu_segmentation_facets.id()); assert(this->id() == other.id()); assert(this->config.id() == other.config.id()); assert(this->supported_facets.id() == other.supported_facets.id()); assert(this->seam_facets.id() == other.seam_facets.id()); + assert(this->mmu_segmentation_facets.id() == other.mmu_segmentation_facets.id()); this->set_material_id(other.material_id()); } // Providing a new mesh, therefore this volume will get a new unique ID assigned. @@ -759,9 +770,11 @@ private: assert(this->config.id().valid()); assert(this->supported_facets.id().valid()); assert(this->seam_facets.id().valid()); + assert(this->mmu_segmentation_facets.id().valid()); assert(this->id() != this->config.id()); assert(this->id() != this->supported_facets.id()); assert(this->id() != this->seam_facets.id()); + assert(this->id() != this->mmu_segmentation_facets.id()); assert(this->id() != other.id()); assert(this->config.id() == other.config.id()); this->set_material_id(other.material_id()); @@ -772,9 +785,11 @@ private: assert(this->config.id() != other.config.id()); assert(this->supported_facets.id() != other.supported_facets.id()); assert(this->seam_facets.id() != other.seam_facets.id()); + assert(this->mmu_segmentation_facets.id() != other.mmu_segmentation_facets.id()); assert(this->id() != this->config.id()); assert(this->supported_facets.empty()); assert(this->seam_facets.empty()); + assert(this->mmu_segmentation_facets.empty()); } ModelVolume& operator=(ModelVolume &rhs) = delete; @@ -782,17 +797,19 @@ private: friend class cereal::access; friend class UndoRedo::StackImpl; // Used for deserialization, therefore no IDs are allocated. - ModelVolume() : ObjectBase(-1), config(-1), supported_facets(-1), seam_facets(-1), object(nullptr) { + ModelVolume() : ObjectBase(-1), config(-1), supported_facets(-1), seam_facets(-1), mmu_segmentation_facets(-1), object(nullptr) { assert(this->id().invalid()); assert(this->config.id().invalid()); assert(this->supported_facets.id().invalid()); assert(this->seam_facets.id().invalid()); + assert(this->mmu_segmentation_facets.id().invalid()); } template void load(Archive &ar) { bool has_convex_hull; ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); cereal::load_by_value(ar, supported_facets); cereal::load_by_value(ar, seam_facets); + cereal::load_by_value(ar, mmu_segmentation_facets); cereal::load_by_value(ar, config); assert(m_mesh); if (has_convex_hull) { @@ -808,6 +825,7 @@ private: ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); cereal::save_by_value(ar, supported_facets); cereal::save_by_value(ar, seam_facets); + cereal::save_by_value(ar, mmu_segmentation_facets); cereal::save_by_value(ar, config); if (has_convex_hull) cereal::save_optional(ar, m_convex_hull); @@ -1078,6 +1096,10 @@ extern bool model_custom_supports_data_changed(const ModelObject& mo, const Mode // The function assumes that volumes list is synchronized. extern bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo_new); +// Test whether the now ModelObject has newer MMU segmentation data than the old one. +// The function assumes that volumes list is synchronized. +extern bool model_mmu_segmentation_data_changed(const ModelObject& mo, const ModelObject& mo_new); + // If the model has multi-part objects, then it is currently not supported by the SLA mode. // Either the model cannot be loaded, or a SLA printer has to be activated. extern bool model_has_multi_part_objects(const Model &model); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index ce5bf1b294..fbbacad1c1 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -412,6 +412,8 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, mv_dst.supported_facets.assign(mv_src.supported_facets); assert(mv_dst.seam_facets.id() == mv_src.seam_facets.id()); mv_dst.seam_facets.assign(mv_src.seam_facets); + assert(mv_dst.mmu_segmentation_facets.id() == mv_src.mmu_segmentation_facets.id()); + mv_dst.mmu_segmentation_facets.assign(mv_src.mmu_segmentation_facets); //FIXME what to do with the materials? // mv_dst.m_material_id = mv_src.m_material_id; ++ i_src; diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 4b3a1c6ca9..62c8172b0a 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -57,6 +57,8 @@ set(SLIC3R_GUI_SOURCES GUI/Gizmos/GLGizmoPainterBase.hpp GUI/Gizmos/GLGizmoSeam.cpp GUI/Gizmos/GLGizmoSeam.hpp + GUI/Gizmos/GLGizmoMmuSegmentation.cpp + GUI/Gizmos/GLGizmoMmuSegmentation.hpp GUI/GLSelectionRectangle.cpp GUI/GLSelectionRectangle.hpp GUI/GLModel.hpp diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7bbdc72b11..025b940043 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1347,7 +1347,8 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject const GLGizmosManager& gm = get_gizmos_manager(); auto gizmo_type = gm.get_current_type(); if ( (gizmo_type == GLGizmosManager::FdmSupports - || gizmo_type == GLGizmosManager::Seam) + || gizmo_type == GLGizmosManager::Seam + || gizmo_type == GLGizmosManager::MmuSegmentation) && ! vol->is_modifier) vol->force_neutral_color = true; else @@ -3227,7 +3228,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled) { if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports && m_gizmos.get_current_type() != GLGizmosManager::FdmSupports - && m_gizmos.get_current_type() != GLGizmosManager::Seam) { + && m_gizmos.get_current_type() != GLGizmosManager::Seam + && m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation) { m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect); m_dirty = true; } @@ -5046,7 +5048,8 @@ void GLCanvas3D::_render_bed(bool bottom, bool show_axes) const (m_gizmos.get_current_type() != GLGizmosManager::FdmSupports && m_gizmos.get_current_type() != GLGizmosManager::SlaSupports && m_gizmos.get_current_type() != GLGizmosManager::Hollow - && m_gizmos.get_current_type() != GLGizmosManager::Seam); + && m_gizmos.get_current_type() != GLGizmosManager::Seam + && m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation); wxGetApp().plater()->get_bed().render(const_cast(*this), bottom, scale_factor, show_axes, show_texture); } @@ -5104,7 +5107,8 @@ void GLCanvas3D::_render_objects() const const GLGizmosManager& gm = get_gizmos_manager(); GLGizmosManager::EType type = gm.get_current_type(); if (type == GLGizmosManager::FdmSupports - || type == GLGizmosManager::Seam) { + || type == GLGizmosManager::Seam + || type == GLGizmosManager::MmuSegmentation) { shader->stop_using(); gm.render_painter_gizmo(); shader->start_using(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp new file mode 100644 index 0000000000..e26b0341da --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -0,0 +1,358 @@ +#include "GLGizmoMmuSegmentation.hpp" + +#include "libslic3r/Model.hpp" + +//#include "slic3r/GUI/3DScene.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/ImGuiWrapper.hpp" +#include "slic3r/GUI/Plater.hpp" + + +#include + + +namespace Slic3r { + +namespace GUI { + + + +void GLGizmoMmuSegmentation::on_shutdown() +{ + m_angle_threshold_deg = 0.f; + m_parent.use_slope(false); +} + + + +std::string GLGizmoMmuSegmentation::on_get_name() const +{ + // FIXME Lukas H.: Discuss and change shortcut + return (_L("MMU painting") + " [N]").ToUTF8().data(); +} + + + +bool GLGizmoMmuSegmentation::on_init() +{ + // FIXME Lukas H.: Discuss and change shortcut + m_shortcut_key = WXK_CONTROL_N; + + m_desc["clipping_of_view"] = _L("Clipping of view") + ": "; + m_desc["reset_direction"] = _L("Reset direction"); + m_desc["cursor_size"] = _L("Brush size") + ": "; + m_desc["cursor_type"] = _L("Brush shape") + ": "; + m_desc["enforce_caption"] = _L("Left mouse button") + ": "; + m_desc["enforce"] = _L("Enforce supports"); + m_desc["block_caption"] = _L("Right mouse button") + ": "; + m_desc["block"] = _L("Block supports"); + m_desc["remove_caption"] = _L("Shift + Left mouse button") + ": "; + m_desc["remove"] = _L("Remove selection"); + m_desc["remove_all"] = _L("Remove all selection"); + m_desc["circle"] = _L("Circle"); + m_desc["sphere"] = _L("Sphere"); + m_desc["highlight_by_angle"] = _L("Highlight by angle"); + m_desc["enforce_button"] = _L("Enforce"); + m_desc["cancel"] = _L("Cancel"); + + return true; +} + + + +void GLGizmoMmuSegmentation::render_painter_gizmo() const +{ + const Selection& selection = m_parent.get_selection(); + + glsafe(::glEnable(GL_BLEND)); + glsafe(::glEnable(GL_DEPTH_TEST)); + + render_triangles(selection); + + m_c->object_clipper()->render_cut(); + render_cursor(); + + glsafe(::glDisable(GL_BLEND)); +} + + + +void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bottom_limit) +{ + if (! m_c->selection_info()->model_object()) + return; + + const float approx_height = m_imgui->scaled(17.0f); + y = std::min(y, bottom_limit - approx_height); + m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); + + m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + + // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: + const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, + m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + + m_imgui->scaled(1.5f); + const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f); + const float autoset_slider_left = m_imgui->calc_text_size(m_desc.at("highlight_by_angle")).x + m_imgui->scaled(1.f); + const float cursor_type_radio_left = m_imgui->calc_text_size(m_desc.at("cursor_type")).x + m_imgui->scaled(1.f); + const float cursor_type_radio_width1 = m_imgui->calc_text_size(m_desc["circle"]).x + + m_imgui->scaled(2.5f); + const float cursor_type_radio_width2 = m_imgui->calc_text_size(m_desc["sphere"]).x + + m_imgui->scaled(2.5f); + const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f); + const float button_enforce_width = m_imgui->calc_text_size(m_desc.at("enforce_button")).x; + const float button_cancel_width = m_imgui->calc_text_size(m_desc.at("cancel")).x; + const float buttons_width = std::max(button_enforce_width, button_cancel_width) + m_imgui->scaled(0.5f); + const float minimal_slider_width = m_imgui->scaled(4.f); + + float caption_max = 0.f; + float total_text_max = 0.; + for (const std::string& t : {"enforce", "block", "remove"}) { + caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t+"_caption")).x); + total_text_max = std::max(total_text_max, caption_max + m_imgui->calc_text_size(m_desc.at(t)).x); + } + caption_max += m_imgui->scaled(1.f); + total_text_max += m_imgui->scaled(1.f); + + float window_width = minimal_slider_width + std::max(autoset_slider_left, std::max(cursor_slider_left, clipping_slider_left)); + window_width = std::max(window_width, total_text_max); + window_width = std::max(window_width, button_width); + window_width = std::max(window_width, cursor_type_radio_left + cursor_type_radio_width1 + cursor_type_radio_width2); + window_width = std::max(window_width, 2.f * buttons_width + m_imgui->scaled(1.f)); + + auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) { + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, caption); + ImGui::SameLine(caption_max); + m_imgui->text(text); + }; + + for (const std::string& t : {"enforce", "block", "remove"}) + draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t)); + + m_imgui->text(""); + ImGui::Separator(); + + m_imgui->text(m_desc["highlight_by_angle"] + ":"); + ImGui::AlignTextToFramePadding(); + std::string format_str = std::string("%.f") + I18N::translate_utf8("°", + "Degree sign to use in the respective slider in FDM supports gizmo," + "placed after the number with no whitespace in between."); + ImGui::SameLine(autoset_slider_left); + ImGui::PushItemWidth(window_width - autoset_slider_left); + if (m_imgui->slider_float("", &m_angle_threshold_deg, 0.f, 90.f, format_str.data())) { + m_parent.set_slope_normal_angle(90.f - m_angle_threshold_deg); + if (! m_parent.is_using_slope()) { + m_parent.use_slope(true); + m_parent.set_as_dirty(); + } + } + + m_imgui->disabled_begin(m_angle_threshold_deg == 0.f); + ImGui::NewLine(); + ImGui::SameLine(window_width - 2.f*buttons_width - m_imgui->scaled(0.5f)); + if (m_imgui->button(m_desc["enforce_button"], buttons_width, 0.f)) { + select_facets_by_angle(m_angle_threshold_deg, false); + m_angle_threshold_deg = 0.f; + } + ImGui::SameLine(window_width - buttons_width); + if (m_imgui->button(m_desc["cancel"], buttons_width, 0.f)) { + m_angle_threshold_deg = 0.f; + m_parent.use_slope(false); + } + m_imgui->disabled_end(); + + ImGui::Separator(); + + if (m_imgui->button(m_desc.at("remove_all"))) { + Plater::TakeSnapshot(wxGetApp().plater(), wxString(_L("Reset selection"))); + ModelObject* mo = m_c->selection_info()->model_object(); + int idx = -1; + for (ModelVolume* mv : mo->volumes) { + if (mv->is_model_part()) { + ++idx; + m_triangle_selectors[idx]->reset(); + } + } + + update_model_object(); + m_parent.set_as_dirty(); + } + + + const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; + + ImGui::AlignTextToFramePadding(); + m_imgui->text(m_desc.at("cursor_size")); + ImGui::SameLine(cursor_slider_left); + ImGui::PushItemWidth(window_width - cursor_slider_left); + ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f"); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(max_tooltip_width); + ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + + + ImGui::AlignTextToFramePadding(); + m_imgui->text(m_desc.at("cursor_type")); + ImGui::SameLine(cursor_type_radio_left + m_imgui->scaled(0.f)); + ImGui::PushItemWidth(cursor_type_radio_width1); + + bool sphere_sel = m_cursor_type == TriangleSelector::CursorType::SPHERE; + if (m_imgui->radio_button(m_desc["sphere"], sphere_sel)) + sphere_sel = true; + + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(max_tooltip_width); + ImGui::TextUnformatted(_L("Paints all facets inside, regardless of their orientation.").ToUTF8().data()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + + ImGui::SameLine(cursor_type_radio_left + cursor_type_radio_width2 + m_imgui->scaled(0.f)); + ImGui::PushItemWidth(cursor_type_radio_width2); + + if (m_imgui->radio_button(m_desc["circle"], ! sphere_sel)) + sphere_sel = false; + + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(max_tooltip_width); + ImGui::TextUnformatted(_L("Ignores facets facing away from the camera.").ToUTF8().data()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + + m_cursor_type = sphere_sel + ? TriangleSelector::CursorType::SPHERE + : TriangleSelector::CursorType::CIRCLE; + + + + + ImGui::Separator(); + if (m_c->object_clipper()->get_position() == 0.f) { + ImGui::AlignTextToFramePadding(); + m_imgui->text(m_desc.at("clipping_of_view")); + } + else { + if (m_imgui->button(m_desc.at("reset_direction"))) { + wxGetApp().CallAfter([this](){ + m_c->object_clipper()->set_position(-1., false); + }); + } + } + + ImGui::SameLine(clipping_slider_left); + ImGui::PushItemWidth(window_width - clipping_slider_left); + float clp_dist = m_c->object_clipper()->get_position(); + if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f")) + m_c->object_clipper()->set_position(clp_dist, true); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(max_tooltip_width); + ImGui::TextUnformatted(_L("Ctrl + Mouse wheel").ToUTF8().data()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + m_imgui->end(); +} + + + +void GLGizmoMmuSegmentation::select_facets_by_angle(float threshold_deg, bool block) +{ + float threshold = (M_PI/180.)*threshold_deg; + const Selection& selection = m_parent.get_selection(); + const ModelObject* mo = m_c->selection_info()->model_object(); + const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; + + int mesh_id = -1; + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + + ++mesh_id; + + const Transform3d trafo_matrix = mi->get_matrix(true) * mv->get_matrix(true); + Vec3f down = (trafo_matrix.inverse() * (-Vec3d::UnitZ())).cast().normalized(); + Vec3f limit = (trafo_matrix.inverse() * Vec3d(std::sin(threshold), 0, -std::cos(threshold))).cast().normalized(); + + float dot_limit = limit.dot(down); + + // Now calculate dot product of vert_direction and facets' normals. + int idx = -1; + for (const stl_facet& facet : mv->mesh().stl.facet_start) { + ++idx; + if (facet.normal.dot(down) > dot_limit) + m_triangle_selectors[mesh_id]->set_facet(idx, + block + ? EnforcerBlockerType::BLOCKER + : EnforcerBlockerType::ENFORCER); + } + } + + activate_internal_undo_redo_stack(true); + + Plater::TakeSnapshot(wxGetApp().plater(), block ? _L("Block supports by angle") + : _L("Add supports by angle")); + update_model_object(); + m_parent.set_as_dirty(); +} + + + +void GLGizmoMmuSegmentation::update_model_object() const +{ + bool updated = false; + ModelObject* mo = m_c->selection_info()->model_object(); + int idx = -1; + for (ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + ++idx; + updated |= mv->mmu_segmentation_facets.set(*m_triangle_selectors[idx].get()); + } + + if (updated) + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +} + + + +void GLGizmoMmuSegmentation::update_from_model_object() +{ + wxBusyCursor wait; + + const ModelObject* mo = m_c->selection_info()->model_object(); + m_triangle_selectors.clear(); + + int volume_id = -1; + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + + ++volume_id; + + // This mesh does not account for the possible Z up SLA offset. + const TriangleMesh* mesh = &mv->mesh(); + + m_triangle_selectors.emplace_back(std::make_unique(*mesh)); + m_triangle_selectors.back()->deserialize(mv->mmu_segmentation_facets.get_data()); + } +} + + + +PainterGizmoType GLGizmoMmuSegmentation::get_painter_type() const +{ + return PainterGizmoType::MMU_SEGMENTATION; +} + + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp new file mode 100644 index 0000000000..9511b56a4a --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp @@ -0,0 +1,46 @@ +#ifndef slic3r_GLGizmoMmuSegmentation_hpp_ +#define slic3r_GLGizmoMmuSegmentation_hpp_ + +#include "GLGizmoPainterBase.hpp" + +namespace Slic3r { + +namespace GUI { + +class GLGizmoMmuSegmentation : public GLGizmoPainterBase +{ +public: + GLGizmoMmuSegmentation(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : GLGizmoPainterBase(parent, icon_filename, sprite_id) {} + + void render_painter_gizmo() const override; + +protected: + void on_render_input_window(float x, float y, float bottom_limit) override; + std::string on_get_name() const override; + +private: + bool on_init() override; + + void update_model_object() const override; + void update_from_model_object() override; + + void on_opening() override {} + void on_shutdown() override; + PainterGizmoType get_painter_type() const override; + + void select_facets_by_angle(float threshold, bool block); + float m_angle_threshold_deg = 0.f; + + // This map holds all translated description texts, so they can be easily referenced during layout calculations + // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. + std::map m_desc; +}; + + + +} // namespace GUI +} // namespace Slic3r + + +#endif // slic3r_GLGizmoMmuSegmentation_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 91aef75d9a..c6b5bbc8e6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -37,7 +37,7 @@ void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate) if (activate && ! m_internal_stack_active) { wxString str = get_painter_type() == PainterGizmoType::FDM_SUPPORTS ? _L("Entering Paint-on supports") - : _L("Entering Seam painting"); + : (get_painter_type() == PainterGizmoType::MMU_SEGMENTATION ? _L("Entering MMU segmentation") : _L("Entering Seam painting")); Plater::TakeSnapshot(wxGetApp().plater(), str); wxGetApp().plater()->enter_gizmos_stack(); m_internal_stack_active = true; @@ -45,7 +45,7 @@ void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate) if (! activate && m_internal_stack_active) { wxString str = get_painter_type() == PainterGizmoType::SEAM ? _L("Leaving Seam painting") - : _L("Leaving Paint-on supports"); + : (get_painter_type() == PainterGizmoType::MMU_SEGMENTATION ? _L("Leaving MMU segmentation") : _L("Leaving Paint-on supports")); wxGetApp().plater()->leave_gizmos_stack(); Plater::TakeSnapshot(wxGetApp().plater(), str); m_internal_stack_active = false; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index da415ce09a..400934ca34 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -25,7 +25,8 @@ struct Camera; enum class PainterGizmoType { FDM_SUPPORTS, - SEAM + SEAM, + MMU_SEGMENTATION }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmos.hpp b/src/slic3r/GUI/Gizmos/GLGizmos.hpp index e8e73959cc..0252dee15e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmos.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmos.hpp @@ -32,6 +32,7 @@ enum class SLAGizmoEventType : unsigned char { #include "slic3r/GUI/Gizmos/GLGizmoFlatten.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp" #include "slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp" #include "slic3r/GUI/Gizmos/GLGizmoCut.hpp" #include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp" diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 9dc785b3fa..453b98dad3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -18,6 +18,7 @@ #include "slic3r/GUI/Gizmos/GLGizmoCut.hpp" #include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/PresetBundle.hpp" @@ -107,6 +108,7 @@ bool GLGizmosManager::init() m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 6)); m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, "fdm_supports.svg", 7)); m_gizmos.emplace_back(new GLGizmoSeam(m_parent, "seam.svg", 8)); + m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "fdm_supports.svg", 9)); m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent)); @@ -392,6 +394,7 @@ void GLGizmosManager::set_painter_gizmo_data() dynamic_cast(m_gizmos[FdmSupports].get())->set_painter_gizmo_data(m_parent.get_selection()); dynamic_cast(m_gizmos[Seam].get())->set_painter_gizmo_data(m_parent.get_selection()); + dynamic_cast(m_gizmos[MmuSegmentation].get())->set_painter_gizmo_data(m_parent.get_selection()); } // Returns true if the gizmo used the event to do something, false otherwise. @@ -408,6 +411,8 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p return dynamic_cast(m_gizmos[FdmSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == Seam) return dynamic_cast(m_gizmos[Seam].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + else if (m_current == MmuSegmentation) + return dynamic_cast(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else return false; } @@ -484,7 +489,7 @@ bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt) { bool processed = false; - if (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam) { + if (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) { float rot = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta(); if (gizmo_event((rot > 0.f ? SLAGizmoEventType::MouseWheelUp : SLAGizmoEventType::MouseWheelDown), Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) processed = true; @@ -617,7 +622,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) m_tooltip = ""; if (evt.LeftDown() && (!control_down || grabber_contains_mouse())) { - if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam) + if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown())) // the gizmo got the event and took some action, there is no need to do anything more processed = true; @@ -643,16 +648,16 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) // event was taken care of by the SlaSupports gizmo processed = true; } - else if (evt.RightDown() && !control_down && selected_object_idx != -1 && (m_current == FdmSupports || m_current == Seam) + else if (evt.RightDown() && !control_down && selected_object_idx != -1 && (m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && gizmo_event(SLAGizmoEventType::RightDown, mouse_pos)) { - // event was taken care of by the FdmSupports / Seam gizmo + // event was taken care of by the FdmSupports / Seam / MMUPainting gizmo processed = true; } else if (evt.Dragging() && m_parent.get_move_volume_id() != -1 - && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam)) + && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation)) // don't allow dragging objects with the Sla gizmo on processed = true; - else if (evt.Dragging() && !control_down && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam) + else if (evt.Dragging() && !control_down && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown())) { // the gizmo got the event and took some action, no need to do anything more here m_parent.set_as_dirty(); @@ -665,7 +670,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) else if (evt.RightIsDown()) gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), true); } - else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam) && !m_parent.is_mouse_dragging()) { + else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && !m_parent.is_mouse_dragging()) { // in case SLA/FDM gizmo is selected, we just pass the LeftUp event and stop processing - neither // object moving or selecting is suppressed in that case gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down); @@ -675,7 +680,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) // to avoid to loose the selection when user clicks an the white faces of a different object while the Flatten gizmo is active processed = true; } - else if (evt.RightUp() && (m_current == FdmSupports || m_current == Seam) && !m_parent.is_mouse_dragging()) { + else if (evt.RightUp() && (m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && !m_parent.is_mouse_dragging()) { gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down); processed = true; } @@ -759,7 +764,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) case 'r' : case 'R' : { - if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam) && gizmo_event(SLAGizmoEventType::ResetClippingPlane)) + if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && gizmo_event(SLAGizmoEventType::ResetClippingPlane)) processed = true; break; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 917a5830c2..02fcc4ed88 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -68,6 +68,7 @@ public: SlaSupports, FdmSupports, Seam, + MmuSegmentation, Undefined }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 50adb89e41..2c3fbe7af7 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3233,6 +3233,7 @@ void Plater::priv::reload_from_disk() new_volume->convert_from_meters(); new_volume->supported_facets.assign(old_volume->supported_facets); new_volume->seam_facets.assign(old_volume->seam_facets); + new_volume->mmu_segmentation_facets.assign(old_volume->mmu_segmentation_facets); std::swap(old_model_object->volumes[sel_v.volume_idx], old_model_object->volumes.back()); old_model_object->delete_volume(old_model_object->volumes.size() - 1); old_model_object->ensure_on_bed(); @@ -3306,13 +3307,14 @@ void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = ModelObject* mo = model.objects[obj_idx]; - // If there are custom supports/seams, remove them. Fixed mesh + // If there are custom supports/seams/mmu segmentation, remove them. Fixed mesh // may be different and they would make no sense. bool paint_removed = false; for (ModelVolume* mv : mo->volumes) { - paint_removed |= ! mv->supported_facets.empty() || ! mv->seam_facets.empty(); + paint_removed |= ! mv->supported_facets.empty() || ! mv->seam_facets.empty() || ! mv->mmu_segmentation_facets.empty(); mv->supported_facets.clear(); mv->seam_facets.clear(); + mv->mmu_segmentation_facets.clear(); } if (paint_removed) { // snapshot_time is captured by copy so the lambda knows where to undo/redo to. From f49ceb1e0f8450f54a84a71c97cf478a69af5723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 19 Apr 2021 07:04:50 +0200 Subject: [PATCH 02/26] WIP: MMU segmentation without top and bottom layers --- src/libslic3r/Format/3mf.cpp | 7 +- src/libslic3r/Model.cpp | 18 +- src/libslic3r/Model.hpp | 10 +- src/libslic3r/Print.cpp | 9 + src/libslic3r/Print.hpp | 2 + src/libslic3r/PrintObject.cpp | 1287 ++++++++++++++++++++++++++++++++- 6 files changed, 1322 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 631f4769c4..33e5042a3d 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1567,7 +1567,12 @@ namespace Slic3r { m_curr_object.geometry.custom_supports.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SUPPORTS_ATTR)); m_curr_object.geometry.custom_seam.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SEAM_ATTR)); - m_curr_object.geometry.mmu_segmentation.push_back(get_attribute_value_string(attributes, num_attributes, MMU_SEGMENTATION_ATTR)); +// m_curr_object.geometry.mmu_segmentation.push_back(get_attribute_value_string(attributes, num_attributes, MMU_SEGMENTATION_ATTR)); + // FIXME Lukas H.: This is only for backward compatibility with older 3MF test files. Removes this when it is not necessary. + if(get_attribute_value_string(attributes, num_attributes, MMU_SEGMENTATION_ATTR) != "") + m_curr_object.geometry.mmu_segmentation.push_back(get_attribute_value_string(attributes, num_attributes, MMU_SEGMENTATION_ATTR)); + else + m_curr_object.geometry.mmu_segmentation.push_back(get_attribute_value_string(attributes, num_attributes, "slic3rpe:mmu_painting")); return true; } diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index ddfa26552c..30e2436fdc 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -2081,8 +2081,10 @@ bool model_volume_list_changed(const ModelObject &model_object_old, const ModelO bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject& mo_new) { assert(! model_volume_list_changed(mo, mo_new, ModelVolumeType::MODEL_PART)); - assert(mo.volumes.size() == mo_new.volumes.size()); - for (size_t i=0; isupported_facets.timestamp_matches(mo.volumes[i]->supported_facets)) return true; } @@ -2091,8 +2093,10 @@ bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo_new) { assert(! model_volume_list_changed(mo, mo_new, ModelVolumeType::MODEL_PART)); - assert(mo.volumes.size() == mo_new.volumes.size()); - for (size_t i=0; iseam_facets.timestamp_matches(mo.volumes[i]->seam_facets)) return true; } @@ -2101,8 +2105,10 @@ bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo bool model_mmu_segmentation_data_changed(const ModelObject& mo, const ModelObject& mo_new) { assert(! model_volume_list_changed(mo, mo_new, ModelVolumeType::MODEL_PART)); - assert(mo.volumes.size() == mo_new.volumes.size()); - for (size_t i=0; immu_segmentation_facets.timestamp_matches(mo.volumes[i]->mmu_segmentation_facets)) return true; } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 0fce954ff3..6b0bd4ed8a 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -487,6 +487,7 @@ enum class ModelVolumeType : int { PARAMETER_MODIFIER, SUPPORT_ENFORCER, SUPPORT_BLOCKER, + MMU_SEGMENTATION }; enum class EnforcerBlockerType : int8_t { @@ -600,6 +601,7 @@ public: bool is_support_enforcer() const { return m_type == ModelVolumeType::SUPPORT_ENFORCER; } bool is_support_blocker() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER; } bool is_support_modifier() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER || m_type == ModelVolumeType::SUPPORT_ENFORCER; } + bool is_mmu_segmentation() const { return m_type == ModelVolumeType::MMU_SEGMENTATION; } t_model_material_id material_id() const { return m_material_id; } void set_material_id(t_model_material_id material_id); ModelMaterial* material() const; @@ -681,6 +683,9 @@ public: this->mmu_segmentation_facets.set_new_unique_id(); } + const std::vector &get_mmu_segmentation_expolygons() const { return m_mmu_segmentation_expolygons; } + void set_mmu_segmentation_expolygons(const std::vector &expoly) { m_mmu_segmentation_expolygons = expoly; } + protected: friend class Print; friend class SLAPrint; @@ -705,6 +710,8 @@ private: // The convex hull of this model's mesh. std::shared_ptr m_convex_hull; Geometry::Transformation m_transformation; + // List of segmented regions (ExPolygons) indexed by extruder index + std::vector m_mmu_segmentation_expolygons; // flag to optimize the checking if the volume is splittable // -1 -> is unknown value (before first cheking) @@ -744,7 +751,8 @@ private: ObjectBase(other), name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation), - supported_facets(other.supported_facets), seam_facets(other.seam_facets), mmu_segmentation_facets(other.mmu_segmentation_facets) + supported_facets(other.supported_facets), seam_facets(other.seam_facets), mmu_segmentation_facets(other.mmu_segmentation_facets), + m_mmu_segmentation_expolygons(other.m_mmu_segmentation_expolygons) { assert(this->id().valid()); assert(this->config.id().valid()); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index fbbacad1c1..8e23e27675 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -892,6 +892,15 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ } } else if (model_custom_seam_data_changed(model_object, model_object_new)) { update_apply_status(this->invalidate_step(psGCodeExport)); + } else if (!print_diff.empty() || model_mmu_segmentation_data_changed(model_object, model_object_new)) { + this->call_cancel_callback(); + update_apply_status(false); + update_apply_status(this->invalidate_all_steps()); + auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); + for (auto it = range.first; it != range.second; ++ it) + update_apply_status(it->print_object->invalidate_all_steps()); + // FIXME Lukas H.: Temporary solution to force update regions after change regions size or repainting. + model_volume_list_update_supports(model_object, model_object_new); } if (! model_parts_differ && ! modifiers_differ) { // Synchronize Object's config. diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 91f86d0105..5414497f86 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -240,6 +240,8 @@ public: // Helpers to project custom facets on slices void project_and_append_custom_facets(bool seam, EnforcerBlockerType type, std::vector& expolys) const; + // Returns MMU segmentation based on painting in MMU segmentation gizmo + std::vector>> mmu_segmentation_by_painting(); private: // to be called from Print only. friend class Print; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index cbf3e71ab7..e6abfe5a24 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -13,16 +13,25 @@ #include "Utils.hpp" #include "Fill/FillAdaptive.hpp" #include "Format/STL.hpp" +#include "EdgeGrid.hpp" +#include "VoronoiVisualUtils.hpp" +#include "VoronoiOffset.hpp" #include #include #include +#include #include #include #include +#include +#include +#include +#include + //! macro used to mark string used at localization, //! return same string #define L(s) Slic3r::I18N::translate(s) @@ -43,6 +52,31 @@ #include #endif +namespace Slic3r { +struct ColoredLine { + Line line; + int color; + int poly_idx = -1; + int local_line_idx = -1; +}; +} + +#include +namespace boost { namespace polygon { +template <> +struct geometry_concept { typedef segment_concept type; }; + +template <> +struct segment_traits { + typedef coord_t coordinate_type; + typedef Slic3r::Point point_type; + + static inline point_type get(const Slic3r::ColoredLine& line, direction_1d dir) { + return dir.to_int() ? line.line.b : line.line.a; + } +}; +} } + namespace Slic3r { // Constructor is called from the main thread, therefore all Model / ModelObject / ModelIntance data are valid. @@ -1903,6 +1937,52 @@ void PrintObject::_slice(const std::vector &layer_height_profile) upscaled = m_config.xy_size_compensation.value > 0 && num_modifiers == 0; } + // --------------------MMU_SEGMENTATION_BEGIN---------------------- + + // Temporary fix for not assigned lslices + for(size_t layer_idx = 0; layer_idx < m_layers.size(); layer_idx += 1) { + ExPolygons ex_polygons; + for (LayerRegion *region : this->m_layers[layer_idx]->regions()) + for (const Surface &surface : region->slices.surfaces) + ex_polygons.emplace_back(surface.expolygon); + this->m_layers[layer_idx]->lslices = union_ex(ex_polygons); + } + + size_t region_count_before_change = this->region_volumes.size(); + std::vector>> segmented_regions = this->mmu_segmentation_by_painting(); + // Skip region with default extruder + for (size_t region_idx = 1; region_idx < 3; ++region_idx) { + std::vector c_layers(m_layers.size()); + for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++layer_idx) { + for(const std::pair &colored_polygon : segmented_regions[layer_idx]) { + if(colored_polygon.second != region_idx) + continue; + c_layers[layer_idx].emplace_back(colored_polygon.first); + } + } + + ModelVolume *model_volume = this->model_object()->add_volume(TriangleMesh()); + model_volume->set_mmu_segmentation_expolygons(c_layers); + model_volume->set_type(ModelVolumeType::MMU_SEGMENTATION); + model_volume->config.clear(); + model_volume->config.set_key_value("extruder", new ConfigOptionInt(int(region_idx) + 1)); + this->print()->add_region(); + this->add_region_volume(this->print()->regions().size() - 1, this->model_object()->volumes.size()-1, std::make_pair(0, std::numeric_limits::max())); + + size_t num_extruders = this->print()->config().nozzle_diameter.size(); + PrintRegionConfig config = PrintObject::region_config_from_model_volume(this->print()->default_region_config(), nullptr, *model_volume, num_extruders); + this->print()->get_region(m_print->regions().size() - 1)->set_config(std::move(config)); + } + + for (size_t i_layer = 0; i_layer < m_layers.size(); i_layer += 1) { + Layer *layer = m_layers[i_layer]; + // Make sure all layers contain layer region objects for all regions. + for (size_t region_id = region_count_before_change; region_id < this->region_volumes.size(); ++ region_id) + layer->add_region(this->print()->get_region(region_id)); + } + + // --------------------MMU_SEGMENTATION_END---------------------- + // Slice all modifier volumes. if (this->region_volumes.size() > 1) { for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { @@ -2081,7 +2161,7 @@ std::vector PrintObject::slice_modifiers(size_t region_id, const std for (size_t i = 0; i < volumes_and_ranges.size(); ) { int volume_id = volumes_and_ranges[i].second; const ModelVolume *model_volume = this->model_object()->volumes[volume_id]; - if (model_volume->is_modifier()) { + if (model_volume->is_modifier() || model_volume->is_mmu_segmentation()) { std::vector ranges; ranges.emplace_back(volumes_and_ranges[i].first); size_t j = i + 1; @@ -2108,7 +2188,8 @@ std::vector PrintObject::slice_modifiers(size_t region_id, const std } } - if (equal_ranges && volume_ranges.front().size() == 1 && volume_ranges.front().front() == t_layer_height_range(0, DBL_MAX)) { + if (equal_ranges && volume_ranges.front().size() == 1 && volume_ranges.front().front() == t_layer_height_range(0, DBL_MAX) + && (this->region_volumes[region_id].size() != 1 || !this->model_object()->volumes[this->region_volumes[region_id].front().second]->is_mmu_segmentation())) { // No modifier in this region was split to layer spans. std::vector volumes; for (const std::pair &volume_and_range : this->region_volumes[region_id]) { @@ -2117,7 +2198,13 @@ std::vector PrintObject::slice_modifiers(size_t region_id, const std volumes.emplace_back(volume); } out = this->slice_volumes(slice_zs, SlicingMode::Regular, volumes); - } else { + } else if(equal_ranges && volume_ranges.front().size() == 1 && volume_ranges.front().front() == t_layer_height_range(0, DBL_MAX) + && this->region_volumes[region_id].size() == 1 && this->model_object()->volumes[this->region_volumes[region_id].front().second]->is_mmu_segmentation()) { + int volume_id = this->region_volumes[region_id].front().second; + const ModelVolume *model_volume = this->model_object()->volumes[volume_id]; + t_layer_height_range range = volume_ranges.front().front(); + out = this->slice_volume(slice_zs, {range}, SlicingMode::Regular, *model_volume); + } else { // Some modifier in this region was split to layer spans. std::vector merge; for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { @@ -2230,6 +2317,9 @@ std::vector PrintObject::slice_volume(const std::vector &z, S { std::vector layers; if (! z.empty()) { + if(volume.is_mmu_segmentation()) { + return volume.get_mmu_segmentation_expolygons(); + } // Compose mesh. //FIXME better to split the mesh into separate shells, perform slicing over each shell separately and then to use a Boolean operation to merge them. TriangleMesh mesh(volume.mesh()); @@ -3032,4 +3122,1195 @@ const Layer *PrintObject::get_first_layer_bellow_printz(coordf_t print_z, coordf return (it == m_layers.begin()) ? nullptr : *(--it); } +// --------------------MMU_START---------------------- +// Assumes that is at most same projected_l length or below than projection_l +static bool project_line_on_line(const Line &projection_l, const Line &projected_l, Line *new_projected) +{ + const Vec2d v1 = (projection_l.b - projection_l.a).cast(); + const Vec2d va = (projected_l.a - projection_l.a).cast(); + const Vec2d vb = (projected_l.b - projection_l.a).cast(); + const double l2 = v1.squaredNorm(); // avoid a sqrt + if (l2 == 0.0) + return false; + double t1 = va.dot(v1) / l2; + double t2 = vb.dot(v1) / l2; + t1 = std::clamp(t1, 0., 1.); + t2 = std::clamp(t2, 0., 1.); + assert(t1 >= 0.); + assert(t2 >= 0.); + assert(t1 <= 1.); + assert(t2 <= 1.); + + Point p1 = (projection_l.a.cast() + t1 * v1).cast(); + Point p2 = (projection_l.a.cast() + t2 * v1).cast(); + *new_projected = Line(p1, p2); + return true; +} + +struct PaintedLine +{ + size_t contour_idx; + size_t line_idx; + Line projected_line; + int color = 1; +}; + +struct PaintedLineVisitor +{ + PaintedLineVisitor(const EdgeGrid::Grid &grid, std::vector &painted_lines) : grid(grid), painted_lines(painted_lines) + { + painted_lines_set.reserve(painted_lines.capacity()); + } + + void reset() { painted_lines_set.clear(); } + + bool operator()(coord_t iy, coord_t ix) + { + // Called with a row and column of the grid cell, which is intersected by a line. + auto cell_data_range = grid.cell_data_range(iy, ix); + for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++it_contour_and_segment) { + Line grid_line = grid.line(*it_contour_and_segment); + + const Vec2d v1 = (line_to_test.b - line_to_test.a).cast().normalized(); + const Vec2d v2 = (grid_line.b - grid_line.a).cast().normalized(); + double angle = ::acos(clamp(-1.0, 1.0, v1.dot(v2))); + double angle_deg = Geometry::rad2deg(angle); + // When lines have too different length, it is necessary to normalize them + if ((angle_deg >= 0 && angle_deg <= 30) || (angle_deg >= 150)) { + Line line_to_test_projected; + project_line_on_line(grid_line, line_to_test, &line_to_test_projected); + + if (painted_lines_set.find(*it_contour_and_segment) == painted_lines_set.end()) { + if (Line(grid_line.a, line_to_test_projected.a).length() > Line(grid_line.a, line_to_test_projected.b).length()) { + line_to_test_projected.reverse(); + } + + double dist_1 = grid_line.distance_to(line_to_test.a); + double dist_2 = grid_line.distance_to(line_to_test.b); + double dist_3 = line_to_test.distance_to(grid_line.a); + double dist_4 = line_to_test.distance_to(grid_line.b); + double total_dist = std::min(std::min(dist_1, dist_2), std::min(dist_3, dist_4)); + + if (total_dist > 50 * SCALED_EPSILON) + continue; + + painted_lines.push_back({it_contour_and_segment->first, it_contour_and_segment->second, line_to_test_projected, this->color}); + painted_lines_set.insert(*it_contour_and_segment); + } + } + } + // Continue traversing the grid along the edge. + return true; + } + + const EdgeGrid::Grid &grid; + std::vector &painted_lines; + Line line_to_test; + std::unordered_set, boost::hash>> painted_lines_set; + int color = -1; +}; + +static std::vector to_colored_lines(const Polygon &polygon, int color) +{ + std::vector lines; + lines.reserve(polygon.points.size()); + if (polygon.points.size() > 2) { + for (auto it = polygon.points.begin(); it != polygon.points.end() - 1; ++it) + lines.push_back({Line(*it, *(it + 1)), color}); + lines.push_back({Line(polygon.points.back(), polygon.points.front()), color}); + } + return lines; +} + +static Polygon colored_points_to_polygon(const std::vector &lines) +{ + Points out; + for (const ColoredLine &l : lines) + out.emplace_back(l.line.a); + return Polygon(out); +} + +static Polygons colored_points_to_polygon(const std::vector> &lines) +{ + Polygons out; + for (const std::vector &l : lines) + out.emplace_back(colored_points_to_polygon(l)); + return out; +} + +inline std::vector to_lines(const std::vector> &c_lines) +{ + size_t n_lines = 0; + for (const auto &c_line : c_lines) + n_lines += c_line.size(); + std::vector lines; + lines.reserve(n_lines); + for (const auto &c_line : c_lines) + lines.insert(lines.end(), c_line.begin(), c_line.end()); + return lines; +} + +// Double vertex equal to a coord_t point after conversion to double. +template +inline bool vertex_equal_to_point(const VertexType &vertex, const Point &ipt) +{ + // Convert ipt to doubles, force the 80bit FPU temporary to 64bit and then compare. + // This should work with any settings of math compiler switches and the C++ compiler + // shall understand the memcpies as type punning and it shall optimize them out. + using ulp_cmp_type = boost::polygon::detail::ulp_comparison; + ulp_cmp_type ulp_cmp; + static constexpr int ULPS = boost::polygon::voronoi_diagram_traits::vertex_equality_predicate_type::ULPS; + return ulp_cmp(vertex.x(), double(ipt.x()), ULPS) == ulp_cmp_type::EQUAL && + ulp_cmp(vertex.y(), double(ipt.y()), ULPS) == ulp_cmp_type::EQUAL; +} + +bool vertex_equal_to_point(const Voronoi::VD::vertex_type *vertex, const Point &ipt) { + return vertex_equal_to_point(*vertex, ipt); +} + +static std::vector> get_segments(const std::vector &polygon) +{ + std::vector> segments; + + size_t segment_end = 0; + while (segment_end + 1 < polygon.size() && polygon[segment_end].color == polygon[segment_end + 1].color) + segment_end++; + + if (segment_end == polygon.size() - 1) + return {std::make_pair(0, polygon.size() - 1)}; + + size_t first_different_color = (segment_end + 1) % polygon.size(); + for (size_t line_offset_idx = 0; line_offset_idx < polygon.size(); ++line_offset_idx) { + size_t start_s = (first_different_color + line_offset_idx) % polygon.size(); + size_t end_s = start_s; + + while (line_offset_idx + 1 < polygon.size() && polygon[start_s].color == polygon[(first_different_color + line_offset_idx + 1) % polygon.size()].color) { + end_s = (first_different_color + line_offset_idx + 1) % polygon.size(); + line_offset_idx++; + } + segments.emplace_back(start_s, end_s); + } + return segments; +} + +static std::vector>> get_all_segments(const std::vector> &color_poly) +{ + std::vector>> all_segments(color_poly.size()); + for (size_t poly_idx = 0; poly_idx < color_poly.size(); ++poly_idx) { + const std::vector &c_polygon = color_poly[poly_idx]; + all_segments[poly_idx] = get_segments(c_polygon); + } + return all_segments; +} + +static std::vector colorize_line(const Line & line_to_process, + const size_t start_idx, + const size_t end_idx, + std::vector &painted_lines) +{ + std::vector internal_painted; + for (size_t line_idx = start_idx; line_idx <= end_idx; ++line_idx) { internal_painted.emplace_back(painted_lines[line_idx]); } + const int filter_eps_value = scale_(0.1f); + std::vector filtered_lines; + filtered_lines.emplace_back(internal_painted.front()); + for (size_t line_idx = 1; line_idx < internal_painted.size(); ++line_idx) { + PaintedLine &prev = filtered_lines.back(); + PaintedLine &curr = internal_painted[line_idx]; + + double prev_length = prev.projected_line.length(); + double curr_dist_start = (curr.projected_line.a - prev.projected_line.a).cast().norm(); + double dist_between_lines = curr_dist_start - prev_length; + + if (dist_between_lines >= 0) { + if (prev.color == curr.color) { + if (dist_between_lines <= filter_eps_value) { + prev.projected_line.b = curr.projected_line.b; + } else { + filtered_lines.emplace_back(curr); + } + } else { + filtered_lines.emplace_back(curr); + } + } else { + double curr_dist_end = (curr.projected_line.b - prev.projected_line.a).cast().norm(); + if (curr_dist_end <= prev_length) { + } else { + if (prev.color == curr.color) { + prev.projected_line.b = curr.projected_line.b; + } else { + curr.projected_line.a = prev.projected_line.b; + filtered_lines.emplace_back(curr); + } + } + } + } + + std::vector final_lines; + double dist_to_start = (filtered_lines.front().projected_line.a - line_to_process.a).cast().norm(); + if (dist_to_start <= filter_eps_value) { + filtered_lines.front().projected_line.a = line_to_process.a; + final_lines.push_back({filtered_lines.front().projected_line, filtered_lines.front().color}); + } else { + final_lines.push_back({Line(line_to_process.a, filtered_lines.front().projected_line.a), 0}); + final_lines.push_back({filtered_lines.front().projected_line, filtered_lines.front().color}); + } + + for (size_t line_idx = 1; line_idx < filtered_lines.size(); ++line_idx) { + ColoredLine &prev = final_lines.back(); + PaintedLine &curr = filtered_lines[line_idx]; + + double line_dist = (curr.projected_line.a - prev.line.b).cast().norm(); + if (line_dist <= filter_eps_value) { + if (prev.color == curr.color) { + prev.line.b = curr.projected_line.b; + } else { + prev.line.b = curr.projected_line.a; + final_lines.push_back({curr.projected_line, curr.color}); + } + } else { + final_lines.push_back({Line(prev.line.b, curr.projected_line.a), 0}); + final_lines.push_back({curr.projected_line, curr.color}); + } + } + + double dist_to_end = (final_lines.back().line.b - line_to_process.b).cast().norm(); + if (dist_to_end <= filter_eps_value) + final_lines.back().line.b = line_to_process.b; + else + final_lines.push_back({Line(final_lines.back().line.b, line_to_process.b), 0}); + + for (size_t line_idx = 1; line_idx < final_lines.size(); ++line_idx) + assert(final_lines[line_idx - 1].line.b == final_lines[line_idx].line.a); + + for (size_t line_idx = 2; line_idx < final_lines.size(); ++line_idx) { + const ColoredLine &line_0 = final_lines[line_idx - 2]; + ColoredLine & line_1 = final_lines[line_idx - 1]; + const ColoredLine &line_2 = final_lines[line_idx - 0]; + + if (line_0.color == line_2.color && line_0.color != line_1.color) + if (line_1.line.length() <= scale_(0.2)) line_1.color = line_0.color; + } + + std::vector colored_lines_simpl; + colored_lines_simpl.emplace_back(final_lines.front()); + for (size_t line_idx = 1; line_idx < final_lines.size(); ++line_idx) { + const ColoredLine &line_0 = final_lines[line_idx]; + + if (colored_lines_simpl.back().color == line_0.color) + colored_lines_simpl.back().line.b = line_0.line.b; + else + colored_lines_simpl.emplace_back(line_0); + } + + final_lines = colored_lines_simpl; + + if (final_lines.size() > 1) { + if (final_lines.front().color != final_lines[1].color && final_lines.front().line.length() <= scale_(0.2)) { + final_lines[1].line.a = final_lines.front().line.a; + final_lines.erase(final_lines.begin()); + } + } + + if (final_lines.size() > 1) { + if (final_lines.back().color != final_lines[final_lines.size() - 2].color && final_lines.back().line.length() <= scale_(0.2)) { + final_lines[final_lines.size() - 2].line.b = final_lines.back().line.b; + final_lines.pop_back(); + } + } + + return final_lines; +} + +static std::vector colorize_polygon(const Polygon &poly, const size_t start_idx, const size_t end_idx, std::vector &painted_lines) +{ + std::vector new_lines; + Lines lines = poly.lines(); + + for (size_t idx = 0; idx < painted_lines[start_idx].line_idx; ++idx) + new_lines.emplace_back(ColoredLine{lines[idx], 0}); + + for (size_t first_idx = start_idx; first_idx <= end_idx; ++first_idx) { + size_t second_idx = first_idx; + while (second_idx <= end_idx && painted_lines[first_idx].line_idx == painted_lines[second_idx].line_idx) ++second_idx; + --second_idx; + + assert(painted_lines[first_idx].line_idx == painted_lines[second_idx].line_idx); + std::vector lines_c_line = colorize_line(lines[painted_lines[first_idx].line_idx], first_idx, second_idx, painted_lines); + new_lines.insert(new_lines.end(), lines_c_line.begin(), lines_c_line.end()); + + if (second_idx + 1 <= end_idx) + for (size_t idx = painted_lines[second_idx].line_idx + 1; idx < painted_lines[second_idx + 1].line_idx; ++idx) + new_lines.emplace_back(ColoredLine{lines[idx], 0}); + + first_idx = second_idx; + } + + for (size_t idx = painted_lines[end_idx].line_idx + 1; idx < poly.size(); ++idx) + new_lines.emplace_back(ColoredLine{lines[idx], 0}); + + for (size_t line_idx = 2; line_idx < new_lines.size(); ++line_idx) { + const ColoredLine &line_0 = new_lines[line_idx - 2]; + ColoredLine & line_1 = new_lines[line_idx - 1]; + const ColoredLine &line_2 = new_lines[line_idx - 0]; + + if (line_0.color == line_2.color && line_0.color != line_1.color && line_0.color >= 1) { + if (line_1.line.length() <= scale_(0.5)) line_1.color = line_0.color; + } + } + + for (size_t line_idx = 3; line_idx < new_lines.size(); ++line_idx) { + const ColoredLine &line_0 = new_lines[line_idx - 3]; + ColoredLine & line_1 = new_lines[line_idx - 2]; + ColoredLine & line_2 = new_lines[line_idx - 1]; + const ColoredLine &line_3 = new_lines[line_idx - 0]; + + if (line_0.color == line_3.color && (line_0.color != line_1.color || line_0.color != line_2.color) && line_0.color >= 1 && line_3.color >= 1) { + if ((line_1.line.length() + line_2.line.length()) <= scale_(0.5)) { + line_1.color = line_0.color; + line_2.color = line_0.color; + } + } + } + + std::vector> segments = get_segments(new_lines); + auto segment_length = [&new_lines](const std::pair &segment) { + double total_length = 0; + for (size_t seg_start_idx = segment.first; seg_start_idx != segment.second; seg_start_idx = (seg_start_idx + 1 < new_lines.size()) ? seg_start_idx + 1 : 0) + total_length += new_lines[seg_start_idx].line.length(); + total_length += new_lines[segment.second].line.length(); + return total_length; + }; + + for (size_t pair_idx = 1; pair_idx < segments.size(); ++pair_idx) { + int color0 = new_lines[segments[pair_idx - 1].first].color; + int color1 = new_lines[segments[pair_idx - 0].first].color; + + double seg0l = segment_length(segments[pair_idx - 1]); + double seg1l = segment_length(segments[pair_idx - 0]); + + if (color0 != color1 && seg0l >= scale_(0.1) && seg1l <= scale_(0.2)) { + for (size_t seg_start_idx = segments[pair_idx].first; seg_start_idx != segments[pair_idx].second; seg_start_idx = (seg_start_idx + 1 < new_lines.size()) ? seg_start_idx + 1 : 0) + new_lines[seg_start_idx].color = color0; + new_lines[segments[pair_idx].second].color = color0; + } + } + + segments = get_segments(new_lines); + for (size_t pair_idx = 1; pair_idx < segments.size(); ++pair_idx) { + int color0 = new_lines[segments[pair_idx - 1].first].color; + int color1 = new_lines[segments[pair_idx - 0].first].color; + double seg1l = segment_length(segments[pair_idx - 0]); + + if (color0 >= 1 && color0 != color1 && seg1l <= scale_(0.2)) { + for (size_t seg_start_idx = segments[pair_idx].first; seg_start_idx != segments[pair_idx].second; seg_start_idx = (seg_start_idx + 1 < new_lines.size()) ? seg_start_idx + 1 : 0) + new_lines[seg_start_idx].color = color0; + new_lines[segments[pair_idx].second].color = color0; + } + } + + for (size_t pair_idx = 2; pair_idx < segments.size(); ++pair_idx) { + int color0 = new_lines[segments[pair_idx - 2].first].color; + int color1 = new_lines[segments[pair_idx - 1].first].color; + int color2 = new_lines[segments[pair_idx - 0].first].color; + + if (color0 > 0 && color0 == color2 && color0 != color1 && segment_length(segments[pair_idx - 1]) <= scale_(0.5)) { + for (size_t seg_start_idx = segments[pair_idx].first; seg_start_idx != segments[pair_idx].second; seg_start_idx = (seg_start_idx + 1 < new_lines.size()) ? seg_start_idx + 1 : 0) + new_lines[seg_start_idx].color = color0; + new_lines[segments[pair_idx].second].color = color0; + } + } + + return new_lines; +} + +static std::vector> colorize_polygons(const Polygons &polygons, std::vector &painted_lines) +{ + const size_t start_idx = 0; + const size_t end_idx = painted_lines.size() - 1; + + std::vector> new_polygons; + + for (size_t idx = 0; idx < painted_lines[start_idx].contour_idx; ++idx) + new_polygons.emplace_back(to_colored_lines(polygons[idx], 0)); + + for (size_t first_idx = start_idx; first_idx <= end_idx; ++first_idx) { + size_t second_idx = first_idx; + while (second_idx <= end_idx && painted_lines[first_idx].contour_idx == painted_lines[second_idx].contour_idx) + ++second_idx; + --second_idx; + + assert(painted_lines[first_idx].contour_idx == painted_lines[second_idx].contour_idx); + std::vector polygon_c = colorize_polygon(polygons[painted_lines[first_idx].contour_idx], first_idx, second_idx, painted_lines); + new_polygons.emplace_back(polygon_c); + + if (second_idx + 1 <= end_idx) + for (size_t idx = painted_lines[second_idx].contour_idx + 1; idx < painted_lines[second_idx + 1].contour_idx; ++idx) + new_polygons.emplace_back(to_colored_lines(polygons[idx], 0)); + + first_idx = second_idx; + } + + for (size_t idx = painted_lines[end_idx].contour_idx + 1; idx < polygons.size(); ++idx) + new_polygons.emplace_back(to_colored_lines(polygons[idx], 0)); + + return new_polygons; +} + +using boost::polygon::voronoi_diagram; + +struct MMU_Graph +{ + enum class ARC_TYPE { BORDER, NON_BORDER }; + + struct Arc + { + size_t from_idx; + size_t to_idx; + int color; + ARC_TYPE type; + bool used{false}; + + bool operator==(const Arc &rhs) const { return (from_idx == rhs.from_idx) && (to_idx == rhs.to_idx) && (color == rhs.color) && (type == rhs.type); } + bool operator!=(const Arc &rhs) const { return !operator==(rhs); } + }; + + struct Node + { + Point point; + std::list neighbours; + + void remove_edge(const size_t to_idx) + { + for (auto arc_it = this->neighbours.begin(); arc_it != this->neighbours.end(); ++arc_it) { + if (arc_it->to_idx == to_idx) { + assert(arc_it->type != ARC_TYPE::BORDER); + this->neighbours.erase(arc_it); + break; + } + } + } + }; + + std::vector nodes; + std::vector arcs; + size_t all_border_points{}; + + std::vector polygon_idx_offset; + std::vector polygon_sizes; + + void remove_edge(const size_t from_idx, const size_t to_idx) + { + nodes[from_idx].remove_edge(to_idx); + nodes[to_idx].remove_edge(from_idx); + } + + size_t get_global_index(const size_t poly_idx, const size_t point_idx) const { return polygon_idx_offset[poly_idx] + point_idx; } + + void append_edge(const size_t &from_idx, const size_t &to_idx, int color = -1, ARC_TYPE type = ARC_TYPE::NON_BORDER) + { + // Don't append duplicate edges between the same nodes. + for (const MMU_Graph::Arc &arc : this->nodes[from_idx].neighbours) + if (arc.to_idx == to_idx) + return; + for (const MMU_Graph::Arc &arc : this->nodes[to_idx].neighbours) + if (arc.to_idx == to_idx) + return; + + this->nodes[from_idx].neighbours.push_back({from_idx, to_idx, color, type}); + this->nodes[to_idx].neighbours.push_back({to_idx, from_idx, color, type}); + this->arcs.push_back({from_idx, to_idx, color, type}); + this->arcs.push_back({to_idx, from_idx, color, type}); + } + + // Ignoring arcs in the opposite direction + MMU_Graph::Arc get_arc(size_t idx) { return this->arcs[idx * 2]; } + + size_t nodes_count() const { return this->nodes.size(); } + + void remove_nodes_with_one_arc() + { + std::queue update_queue; + for (const MMU_Graph::Node &node : this->nodes) + if (node.neighbours.size() == 1) update_queue.emplace(&node - &this->nodes.front()); + + while (!update_queue.empty()) { + size_t node_from_idx = update_queue.front(); + MMU_Graph::Node &node_from = this->nodes[update_queue.front()]; + update_queue.pop(); + if (node_from.neighbours.empty()) + continue; + + assert(node_from.neighbours.size() == 1); + size_t node_to_idx = node_from.neighbours.front().to_idx; + MMU_Graph::Node &node_to = this->nodes[node_to_idx]; + this->remove_edge(node_from_idx, node_to_idx); + if (node_to.neighbours.size() == 1) + update_queue.emplace(node_to_idx); + } + } + + void add_contours(const std::vector> &color_poly) + { + this->all_border_points = nodes.size(); + this->polygon_sizes = std::vector(color_poly.size()); + for (size_t polygon_idx = 0; polygon_idx < color_poly.size(); ++polygon_idx) + this->polygon_sizes[polygon_idx] = color_poly[polygon_idx].size(); + this->polygon_idx_offset = std::vector(color_poly.size()); + this->polygon_idx_offset[0] = 0; + for (size_t polygon_idx = 1; polygon_idx < color_poly.size(); ++polygon_idx) { + this->polygon_idx_offset[polygon_idx] = this->polygon_idx_offset[polygon_idx - 1] + color_poly[polygon_idx - 1].size(); + } + + size_t poly_idx = 0; + for (const std::vector &color_lines : color_poly) { + size_t line_idx = 0; + for (const ColoredLine &color_line : color_lines) { + size_t from_idx = this->get_global_index(poly_idx, line_idx); + size_t to_idx = this->get_global_index(poly_idx, (line_idx + 1) % color_lines.size()); + this->append_edge(from_idx, to_idx, color_line.color, ARC_TYPE::BORDER); + ++line_idx; + } + ++poly_idx; + } + } + + // Nodes 0..all_border_points are only one with are on countour. Other vertexis are consider as not on coouter. So we check if base on attach index + inline bool is_vertex_on_contour(const Voronoi::VD::vertex_type *vertex) const + { + assert(vertex != nullptr); + return vertex->color() < this->all_border_points; + } + + inline bool is_edge_attach_to_contour(const voronoi_diagram::const_edge_iterator &edge_iterator) const + { + return this->is_vertex_on_contour(edge_iterator->vertex0()) || this->is_vertex_on_contour(edge_iterator->vertex1()); + } + + inline bool is_edge_connecting_two_contour_vertices(const voronoi_diagram::const_edge_iterator &edge_iterator) const + { + return this->is_vertex_on_contour(edge_iterator->vertex0()) && this->is_vertex_on_contour(edge_iterator->vertex1()); + } +}; + +namespace bg = boost::geometry; +namespace bgm = boost::geometry::model; +namespace bgi = boost::geometry::index; + +// float is needed because for coord_t bgi::intersects throws "bad numeric conversion: positive overflow" +using rtree_point_t = bgm::point; +using rtree_t = bgi::rtree, bgi::rstar<16, 4>>; + +static inline rtree_point_t mk_rtree_point(const Point &pt) { return rtree_point_t(float(pt.x()), float(pt.y())); } + +static inline Point mk_point(const Voronoi::VD::vertex_type *point) { return Point(coord_t(point->x()), coord_t(point->y())); } + +static inline Point mk_point(const Voronoi::Internal::point_type &point) { return Point(coord_t(point.x()), coord_t(point.y())); } + +static inline Point mk_point(const voronoi_diagram::vertex_type &point) { return Point(coord_t(point.x()), coord_t(point.y())); } + +static inline void mark_processed(const voronoi_diagram::const_edge_iterator &edge_iterator) +{ + edge_iterator->color(true); + edge_iterator->twin()->color(true); +} + +// Return true, if "p" is closer to line.a, then line.b +static inline bool is_point_closer_to_beginning_of_line(const Line &line, const Point &p) +{ + return (p - line.a).cast().squaredNorm() < (p - line.b).cast().squaredNorm(); +} + +static inline bool has_same_color(const ColoredLine &cl1, const ColoredLine &cl2) { return cl1.color == cl2.color; } + +// Determines if the line points from the point between two contour lines is pointing inside polygon or outside. +static inline bool points_inside(const Line &contour_first, const Line &contour_second, const Point &new_point) +{ + // Used in points_inside for decision if line leading thought the common point of two lines is pointing inside polygon or outside + auto three_points_inward_normal = [](const Point &left, const Point &middle, const Point &right) -> Vec2d { + assert(left != middle); + assert(middle != right); + return (perp(Point(middle - left)).cast().normalized() + perp(Point(right - middle)).cast().normalized()).normalized(); + }; + + assert(contour_first.b == contour_second.a); + Vec2d inward_normal = three_points_inward_normal(contour_first.a, contour_first.b, contour_second.b); + Vec2d edge_norm = (new_point - contour_first.b).cast().normalized(); + double side = inward_normal.dot(edge_norm); + // assert(side != 0.); + return side > 0.; +} + +static inline bool line_intersection_with_epsilon(const Line &line_to_extend, const Line &other, Point *intersection) +{ + Line extended_line = line_to_extend; + extended_line.extend(15 * SCALED_EPSILON); + return extended_line.intersection(other, intersection); +} + +// For every ColoredLine in lines_colored_out, assign the index of the polygon to which belongs and also the index of this line inside of the polygon. +static inline void init_polygon_indices(const MMU_Graph &graph, + const std::vector> &color_poly, + std::vector &lines_colored_out) +{ + size_t poly_idx = 0; + for (const std::vector &color_lines : color_poly) { + size_t line_idx = 0; + for (size_t color_line_idx = 0; color_line_idx < color_lines.size(); ++color_line_idx) { + size_t from_idx = graph.get_global_index(poly_idx, line_idx); + lines_colored_out[from_idx].poly_idx = int(poly_idx); + lines_colored_out[from_idx].local_line_idx = int(line_idx); + ++line_idx; + } + ++poly_idx; + } +} + +static MMU_Graph build_graph(size_t layer_idx, const std::vector> &color_poly) +{ + Geometry::VoronoiDiagram vd; + std::vector lines_colored = to_lines(color_poly); + Polygons color_poly_tmp = colored_points_to_polygon(color_poly); + const Points points = to_points(color_poly_tmp); + const Lines lines = to_lines(color_poly_tmp); + + boost::polygon::construct_voronoi(lines_colored.begin(), lines_colored.end(), &vd); + MMU_Graph graph; + for (const Point &point : points) + graph.nodes.push_back({point}); + + graph.add_contours(color_poly); + init_polygon_indices(graph, color_poly, lines_colored); + + assert(graph.nodes.size() == lines_colored.size()); + + // All Voronoi vertices are post-processes to merge very close vertices to single. Witch Eliminates issues with intersection edges. + // Also, Voronoi vertices outside of the bounding of input polygons are throw away by marking them. + auto append_voronoi_vertices_to_graph = [&graph, &color_poly_tmp, &vd]() -> void { + auto is_equal_points = [](const Point &p1, const Point &p2) { return p1 == p2 || (p1 - p2).cast().norm() <= 3 * SCALED_EPSILON; }; + + BoundingBox bbox = get_extents(color_poly_tmp); + bbox.offset(SCALED_EPSILON); + // EdgeGrid is used for vertices near to contour and rtree for other vertices + // FIXME Lukas H.: Get rid of EdgeGrid and rtree. Use only one structure for both cases. + EdgeGrid::Grid grid; + grid.set_bbox(bbox); + grid.create(color_poly_tmp, coord_t(scale_(10.))); + rtree_t rtree; + for (const voronoi_diagram::vertex_type &vertex : vd.vertices()) { + vertex.color(-1); + Point vertex_point = mk_point(vertex); + + const Point &first_point = graph.nodes[graph.get_arc(vertex.incident_edge()->cell()->source_index()).from_idx].point; + const Point &second_point = graph.nodes[graph.get_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx].point; + + if (vertex_equal_to_point(&vertex, first_point)) { + assert(vertex.color() != vertex.incident_edge()->cell()->source_index()); + assert(vertex.color() != vertex.incident_edge()->twin()->cell()->source_index()); + vertex.color(graph.get_arc(vertex.incident_edge()->cell()->source_index()).from_idx); + } else if (vertex_equal_to_point(&vertex, second_point)) { + assert(vertex.color() != vertex.incident_edge()->cell()->source_index()); + assert(vertex.color() != vertex.incident_edge()->twin()->cell()->source_index()); + vertex.color(graph.get_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx); + } else if (bbox.contains(vertex_point)) { + EdgeGrid::Grid::ClosestPointResult cp = grid.closest_point_signed_distance(vertex_point, coord_t(3 * SCALED_EPSILON)); + if (cp.valid()) { + size_t global_idx = graph.get_global_index(cp.contour_idx, cp.start_point_idx); + size_t global_idx_next = graph.get_global_index(cp.contour_idx, (cp.start_point_idx + 1) % color_poly_tmp[cp.contour_idx].points.size()); + vertex.color(is_equal_points(vertex_point, graph.nodes[global_idx].point) ? global_idx : global_idx_next); + } else { + if (rtree.empty()) { + rtree.insert(std::make_pair(mk_rtree_point(vertex_point), graph.nodes_count())); + vertex.color(graph.nodes_count()); + graph.nodes.push_back({vertex_point}); + } else { + std::vector> closest; + rtree.query(bgi::nearest(mk_rtree_point(vertex_point), 1), std::back_inserter(closest)); + assert(!closest.empty()); + rtree_point_t r_point = closest.front().first; + Point closest_p(bg::get<0>(r_point), bg::get<1>(r_point)); + if (Line(vertex_point, closest_p).length() > 3 * SCALED_EPSILON) { + rtree.insert(std::make_pair(mk_rtree_point(vertex_point), graph.nodes_count())); + vertex.color(graph.nodes_count()); + graph.nodes.push_back({vertex_point}); + } else { + vertex.color(closest.front().second); + } + } + } + } + } + }; + + append_voronoi_vertices_to_graph(); + + auto get_prev_contour_line = [&lines_colored, &color_poly, &graph](const voronoi_diagram::const_edge_iterator &edge_it) -> ColoredLine { + size_t contour_line_local_idx = lines_colored[edge_it->cell()->source_index()].local_line_idx; + size_t contour_line_size = color_poly[lines_colored[edge_it->cell()->source_index()].poly_idx].size(); + size_t contour_prev_idx = graph.get_global_index(lines_colored[edge_it->cell()->source_index()].poly_idx, + (contour_line_local_idx > 0) ? contour_line_local_idx - 1 : contour_line_size - 1); + return lines_colored[contour_prev_idx]; + }; + + auto get_next_contour_line = [&lines_colored, &color_poly, &graph](const voronoi_diagram::const_edge_iterator &edge_it) -> ColoredLine { + size_t contour_line_local_idx = lines_colored[edge_it->cell()->source_index()].local_line_idx; + size_t contour_line_size = color_poly[lines_colored[edge_it->cell()->source_index()].poly_idx].size(); + size_t contour_next_idx = graph.get_global_index(lines_colored[edge_it->cell()->source_index()].poly_idx, + (contour_line_local_idx + 1) % contour_line_size); + return lines_colored[contour_next_idx]; + }; + + BoundingBox bbox = get_extents(color_poly_tmp); + bbox.offset(scale_(10.)); + const double bbox_dim_max = double(std::max(bbox.size().x(), bbox.size().y())); + + // Make a copy of the input segments with the double type. + std::vector segments; + for (const Line &line : lines) + segments.emplace_back(Voronoi::Internal::point_type(double(line.a(0)), double(line.a(1))), + Voronoi::Internal::point_type(double(line.b(0)), double(line.b(1)))); + + for (auto edge_it = vd.edges().begin(); edge_it != vd.edges().end(); ++edge_it) { + // Skip second half-edge + if (edge_it->cell()->source_index() > edge_it->twin()->cell()->source_index() || edge_it->color()) + continue; + + if (edge_it->is_infinite()) { + // Infinite edge is leading through a point on the counter, but there are no Voronoi vertices. + // So we could fix this case by computing the intersection between the contour line and infinity edge. + std::vector samples; + Voronoi::Internal::clip_infinite_edge(points, segments, *edge_it, bbox_dim_max, &samples); + if (samples.empty()) + continue; + + const Line edge_line(mk_point(samples[0]), mk_point(samples[1])); + const ColoredLine &contour_line = lines_colored[edge_it->cell()->source_index()]; + Point contour_intersection; + + if (line_intersection_with_epsilon(contour_line.line, edge_line, &contour_intersection)) { + const MMU_Graph::Arc &graph_arc = graph.get_arc(edge_it->cell()->source_index()); + const size_t from_idx = (edge_it->vertex1() != nullptr) ? edge_it->vertex1()->color() : edge_it->vertex0()->color(); + size_t to_idx = ((contour_line.line.a - contour_intersection).cast().squaredNorm() < + (contour_line.line.b - contour_intersection).cast().squaredNorm()) ? + graph_arc.from_idx : + graph_arc.to_idx; + if (from_idx != to_idx && from_idx < graph.nodes_count() && to_idx < graph.nodes_count()) { + graph.append_edge(from_idx, to_idx); + mark_processed(edge_it); + } + } + } else if (edge_it->is_finite()) { + const Point v0 = mk_point(edge_it->vertex0()); + const Point v1 = mk_point(edge_it->vertex1()); + const size_t from_idx = edge_it->vertex0()->color(); + const size_t to_idx = edge_it->vertex1()->color(); + + // Both points are on contour, so skip them. In cases of duplicate Voronoi vertices, skip edges between the same two points. + if (graph.is_edge_connecting_two_contour_vertices(edge_it) || (edge_it->vertex0()->color() == edge_it->vertex1()->color())) continue; + + const Line edge_line(v0, v1); + const Line contour_line = lines_colored[edge_it->cell()->source_index()].line; + const ColoredLine colored_line = lines_colored[edge_it->cell()->source_index()]; + const ColoredLine contour_line_prev = get_prev_contour_line(edge_it); + const ColoredLine contour_line_next = get_next_contour_line(edge_it); + + Point intersection; + if (edge_it->vertex0()->color() >= graph.nodes_count() || edge_it->vertex1()->color() >= graph.nodes_count()) { +// if(edge_it->vertex0()->color() < graph.nodes_count() && !graph.is_vertex_on_contour(edge_it->vertex0())) { +// +// } + if (edge_it->vertex1()->color() < graph.nodes_count() && !graph.is_vertex_on_contour(edge_it->vertex1())) { + Line contour_line_twin = lines_colored[edge_it->twin()->cell()->source_index()].line; + if (line_intersection_with_epsilon(contour_line_twin, edge_line, &intersection)) { + const MMU_Graph::Arc &graph_arc = graph.get_arc(edge_it->twin()->cell()->source_index()); + const size_t to_idx_l = is_point_closer_to_beginning_of_line(contour_line_twin, intersection) ? graph_arc.from_idx : + graph_arc.to_idx; + graph.append_edge(edge_it->vertex1()->color(), to_idx_l); + } else if (line_intersection_with_epsilon(contour_line, edge_line, &intersection)) { + const MMU_Graph::Arc &graph_arc = graph.get_arc(edge_it->cell()->source_index()); + const size_t to_idx_l = is_point_closer_to_beginning_of_line(contour_line, intersection) ? graph_arc.from_idx : graph_arc.to_idx; + graph.append_edge(edge_it->vertex1()->color(), to_idx_l); + } + mark_processed(edge_it); + } + } else if (graph.is_edge_attach_to_contour(edge_it)) { + mark_processed(edge_it); + // Skip edges witch connection two points on a contour + if (graph.is_edge_connecting_two_contour_vertices(edge_it)) + continue; + + if (graph.is_vertex_on_contour(edge_it->vertex0())) { + if (is_point_closer_to_beginning_of_line(contour_line, v0)) { + if (!has_same_color(contour_line_prev, colored_line) && points_inside(contour_line_prev.line, contour_line, v1)) { + graph.append_edge(from_idx, to_idx); + } + } else { + if (!has_same_color(contour_line_next, colored_line) && points_inside(contour_line, contour_line_next.line, v1)) { + graph.append_edge(from_idx, to_idx); + } + } + } else { + assert(graph.is_vertex_on_contour(edge_it->vertex1())); + if (is_point_closer_to_beginning_of_line(contour_line, v1)) { + if (!has_same_color(contour_line_prev, colored_line) && points_inside(contour_line_prev.line, contour_line, v0)) { + graph.append_edge(from_idx, to_idx); + } + } else { + if (!has_same_color(contour_line_next, colored_line) && points_inside(contour_line, contour_line_next.line, v0)) { + graph.append_edge(from_idx, to_idx); + } + } + } + } else if (line_intersection_with_epsilon(contour_line, edge_line, &intersection)) { + mark_processed(edge_it); + Point real_v0 = graph.nodes[edge_it->vertex0()->color()].point; + Point real_v1 = graph.nodes[edge_it->vertex1()->color()].point; + + if (is_point_closer_to_beginning_of_line(contour_line, intersection)) { + Line first_part(intersection, real_v0); + Line second_part(intersection, real_v1); + + if (!has_same_color(contour_line_prev, colored_line)) { + if (points_inside(contour_line_prev.line, contour_line, first_part.b)) { + graph.append_edge(edge_it->vertex0()->color(), graph.get_arc(edge_it->cell()->source_index()).from_idx); + } + if (points_inside(contour_line_prev.line, contour_line, second_part.b)) { + graph.append_edge(edge_it->vertex1()->color(), graph.get_arc(edge_it->cell()->source_index()).from_idx); + } + } + } else { + const size_t int_point_idx = graph.get_arc(edge_it->cell()->source_index()).to_idx; + const Point int_point = graph.nodes[int_point_idx].point; + + const Line first_part(int_point, real_v0); + const Line second_part(int_point, real_v1); + + if (!has_same_color(contour_line_next, colored_line)) { + if (points_inside(contour_line, contour_line_next.line, first_part.b)) { + graph.append_edge(edge_it->vertex0()->color(), int_point_idx); + } + if (points_inside(contour_line, contour_line_next.line, second_part.b)) { + graph.append_edge(edge_it->vertex1()->color(), int_point_idx); + } + } + } + } + } + } + + for (auto edge_it = vd.edges().begin(); edge_it != vd.edges().end(); ++edge_it) { + // Skip second half-edge and processed edges + if (edge_it->cell()->source_index() > edge_it->twin()->cell()->source_index() || edge_it->color()) + continue; + + if (edge_it->is_finite() && !bool(edge_it->color()) && edge_it->vertex0()->color() < graph.nodes_count() && + edge_it->vertex1()->color() < graph.nodes_count()) { + // Skip cases, when the edge is between two same vertices, which is in cases two near vertices were merged together. + if (edge_it->vertex0()->color() == edge_it->vertex1()->color()) + continue; + + size_t from_idx = edge_it->vertex0()->color(); + size_t to_idx = edge_it->vertex1()->color(); + graph.append_edge(from_idx, to_idx); + } + mark_processed(edge_it); + } + + graph.remove_nodes_with_one_arc(); + return graph; +} + +static inline Polygon to_polygon(const Lines &lines) +{ + Polygon poly_out; + poly_out.points.reserve(lines.size()); + for (const Line &line : lines) + poly_out.points.emplace_back(line.a); + return poly_out; +} + +// Returns list of polygons and assigned colors. +// It iterates through all nodes on the border between two different colors, and from this point, +// start selection always left most edges for every node to construct CCW polygons. +// Assumes that graph is planar (without self-intersection edges) +static std::vector> extract_colored_segments(MMU_Graph &graph) +{ + // When there is no next arc, then is returned original_arc or edge with is marked as used + auto get_next = [&graph](const Line &process_line, MMU_Graph::Arc &original_arc) -> MMU_Graph::Arc & { + std::vector> sorted_arcs; + for (MMU_Graph::Arc &arc : graph.nodes[original_arc.to_idx].neighbours) { + if (graph.nodes[arc.to_idx].point == process_line.a || arc.used) + continue; + + assert(original_arc.to_idx == arc.from_idx); + Vec2d process_line_vec_n = (process_line.a - process_line.b).cast().normalized(); + Vec2d neighbour_line_vec_n = (graph.nodes[arc.to_idx].point - graph.nodes[arc.from_idx].point).cast().normalized(); + + double angle = ::acos(clamp(-1.0, 1.0, neighbour_line_vec_n.dot(process_line_vec_n))); + if (Slic3r::cross2(neighbour_line_vec_n, process_line_vec_n) < 0.0) + angle = 2.0 * (double) PI - angle; + + sorted_arcs.emplace_back(&arc, angle); + } + + std::sort(sorted_arcs.begin(), sorted_arcs.end(), + [](std::pair &l, std::pair &r) -> bool { return l.second < r.second; }); + + // Try to return left most edge witch is unused + for (auto &sorted_arc : sorted_arcs) + if (!sorted_arc.first->used) + return *sorted_arc.first; + + if (sorted_arcs.empty()) + return original_arc; + + return *(sorted_arcs.front().first); + }; + + std::vector> polygons_segments; + for (MMU_Graph::Node &node : graph.nodes) + for (MMU_Graph::Arc &arc : node.neighbours) + arc.used = false; + + for (size_t node_idx = 0; node_idx < graph.all_border_points; ++node_idx) { + MMU_Graph::Node &node = graph.nodes[node_idx]; + + for (MMU_Graph::Arc &arc : node.neighbours) { + if (arc.type == MMU_Graph::ARC_TYPE::NON_BORDER || arc.used) continue; + + Line process_line(node.point, graph.nodes[arc.to_idx].point); + arc.used = true; + + Lines face_lines; + face_lines.emplace_back(process_line); + Point start_p = process_line.a; + + Line p_vec = process_line; + MMU_Graph::Arc *p_arc = &arc; + do { + MMU_Graph::Arc &next = get_next(p_vec, *p_arc); + face_lines.emplace_back(Line(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point)); + if (next.used) break; + + next.used = true; + p_vec = Line(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point); + p_arc = &next; + } while (graph.nodes[p_arc->to_idx].point != start_p); + + Polygon poly = to_polygon(face_lines); + if (poly.is_counter_clockwise() && poly.is_valid()) + polygons_segments.emplace_back(poly, arc.color); + } + } + return polygons_segments; +} + +// Used in remove_multiple_edges_in_vertices() +// Returns length of edge with is connected to contour. To this length is include other edges with follows it if they are almost straight (with the +// tolerance of 15) And also if node between two subsequent edges is connected only to these two edges. +static inline double compute_edge_length(MMU_Graph &graph, size_t start_idx, MMU_Graph::Arc &start_edge) +{ + for (MMU_Graph::Node &node : graph.nodes) + for (MMU_Graph::Arc &arc : node.neighbours) + arc.used = false; + + start_edge.used = true; + MMU_Graph::Arc *arc = &start_edge; + size_t idx = start_idx; + double line_total_length = Line(graph.nodes[idx].point, graph.nodes[arc->to_idx].point).length(); + while (graph.nodes[arc->to_idx].neighbours.size() == 2) { + bool found = false; + for (MMU_Graph::Arc &arc_n : graph.nodes[arc->to_idx].neighbours) { + if (arc_n.type == MMU_Graph::ARC_TYPE::NON_BORDER && !arc_n.used && arc_n.to_idx != idx) { + Line first_line(graph.nodes[idx].point, graph.nodes[arc->to_idx].point); + Line second_line(graph.nodes[arc->to_idx].point, graph.nodes[arc_n.to_idx].point); + + Vec2d first_line_vec = (first_line.a - first_line.b).cast(); + Vec2d second_line_vec = (second_line.b - second_line.a).cast(); + Vec2d first_line_vec_n = first_line_vec.normalized(); + Vec2d second_line_vec_n = second_line_vec.normalized(); + double angle = ::acos(clamp(-1.0, 1.0, first_line_vec_n.dot(second_line_vec_n))); + if (Slic3r::cross2(first_line_vec_n, second_line_vec_n) < 0.0) + angle = 2.0 * (double) PI - angle; + + if (std::abs(angle - PI) >= (PI / 12)) + continue; + + idx = arc->to_idx; + arc = &arc_n; + + line_total_length += Line(graph.nodes[idx].point, graph.nodes[arc->to_idx].point).length(); + arc_n.used = true; + found = true; + break; + } + } + if (!found) + break; + } + + return line_total_length; +} + +// Used for fixing double Voronoi edges for concave parts of the polygon. +static void remove_multiple_edges_in_vertices(MMU_Graph &graph, const std::vector> &color_poly) +{ + std::vector>> colored_segments = get_all_segments(color_poly); + for (const std::vector> &colored_segment_p : colored_segments) { + size_t poly_idx = &colored_segment_p - &colored_segments.front(); + for (const std::pair &colored_segment : colored_segment_p) { + size_t first_idx = graph.get_global_index(poly_idx, colored_segment.first); + size_t second_idx = graph.get_global_index(poly_idx, (colored_segment.second + 1) % graph.polygon_sizes[poly_idx]); + Line seg_line(graph.nodes[first_idx].point, graph.nodes[second_idx].point); + + if (graph.nodes[first_idx].neighbours.size() >= 3) { + std::vector> arc_to_check; + for (MMU_Graph::Arc &n_arc : graph.nodes[first_idx].neighbours) { + if (n_arc.type == MMU_Graph::ARC_TYPE::NON_BORDER) { + double total_len = compute_edge_length(graph, first_idx, n_arc); + arc_to_check.emplace_back(&n_arc, total_len); + } + } + std::sort(arc_to_check.begin(), arc_to_check.end(), + [](std::pair &l, std::pair &r) -> bool { return l.second > r.second; }); + + while (arc_to_check.size() > 1) { + graph.remove_edge(first_idx, arc_to_check.back().first->to_idx); + arc_to_check.pop_back(); + } + } + } + } +} + +std::vector>> PrintObject::mmu_segmentation_by_painting() +{ + std::vector>> segmented_regions(this->layers().size()); + std::vector> painted_lines(this->layers().size()); + std::vector edge_grids(this->layers().size()); + + for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++layer_idx) { + const Layer *layer = m_layers[layer_idx]; + BoundingBox bbox(get_extents(layer->lslices)); + bbox.offset(SCALED_EPSILON); + edge_grids[layer_idx].set_bbox(bbox); + edge_grids[layer_idx].create(layer->lslices, coord_t(scale_(10.))); + } + + for (const ModelVolume *mv : this->model_object()->volumes) { + for (const auto ¶ms : {std::make_pair(EnforcerBlockerType::ENFORCER, 1), std::make_pair(EnforcerBlockerType::BLOCKER, 2)}) { + const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, params.first); + if (!mv->is_model_part() || custom_facets.indices.empty()) + continue; + + const Transform3f tr = this->trafo().cast() * mv->get_matrix().cast(); + for (size_t facet_idx = 0; facet_idx < custom_facets.indices.size(); ++facet_idx) { + float min_z = std::numeric_limits::max(); + float max_z = std::numeric_limits::lowest(); + + std::array facet; + Points projected_facet(3); + for (int p_idx = 0; p_idx < 3; ++p_idx) { + facet[p_idx] = tr * custom_facets.vertices[custom_facets.indices[facet_idx](p_idx)]; + max_z = std::max(max_z, facet[p_idx].z()); + min_z = std::min(min_z, facet[p_idx].z()); + } + + // Sort the vertices by z-axis for simplification of projected_facet on slices + std::sort(facet.begin(), facet.end(), [](const Vec3f &p1, const Vec3f &p2) { return p1.z() < p2.z(); }); + + for (int p_idx = 0; p_idx < 3; ++p_idx) { + projected_facet[p_idx] = Point(scale_(facet[p_idx].x()), scale_(facet[p_idx].y())); + projected_facet[p_idx] = projected_facet[p_idx] - this->center_offset(); + } + + ExPolygon triangle = ExPolygon(projected_facet); + + // Find lowest slice not below the triangle. + auto first_layer = std::upper_bound(this->layers().begin(), this->layers().end(), float(min_z - EPSILON), + [](float z, const Layer *l1) { return z < l1->slice_z; }); + auto last_layer = std::upper_bound(this->layers().begin(), this->layers().end(), float(max_z + EPSILON), + [](float z, const Layer *l1) { return z < l1->slice_z; }); + --last_layer; + + for (auto layer_it = first_layer; layer_it != (last_layer + 1); ++layer_it) { + const Layer *layer = *layer_it; + size_t layer_idx = layer_it - this->layers().begin(); + if (facet[0].z() > layer->slice_z || layer->slice_z > facet[2].z()) + continue; + + // https://kandepet.com/3d-printing-slicing-3d-objects/ + float t = (float(layer->slice_z) - facet[0].z()) / (facet[2].z() - facet[0].z()); + Vec3f line_start_f = facet[0] + t * (facet[2] - facet[0]); + Vec3f line_end_f; + + if (facet[1].z() > layer->slice_z) { + // [P0, P2] a [P0, P1] + float t1 = (float(layer->slice_z) - facet[0].z()) / (facet[1].z() - facet[0].z()); + line_end_f = facet[0] + t1 * (facet[1] - facet[0]); + } else if (facet[1].z() <= layer->slice_z) { + // [P0, P2] a [P1, P2] + float t2 = (float(layer->slice_z) - facet[1].z()) / (facet[2].z() - facet[1].z()); + line_end_f = facet[1] + t2 * (facet[2] - facet[1]); + } + + Point line_start(scale_(line_start_f.x()), scale_(line_start_f.y())); + Point line_end(scale_(line_end_f.x()), scale_(line_end_f.y())); + line_start -= this->center_offset(); + line_end -= this->center_offset(); + + std::vector painted_line_tmp; + PaintedLineVisitor visitor(edge_grids[layer_idx], painted_line_tmp); + visitor.reset(); + visitor.line_to_test.a = line_start; + visitor.line_to_test.b = line_end; + visitor.color = params.second; + edge_grids[layer_idx].visit_cells_intersecting_line(line_start, line_end, visitor); + + if (!painted_line_tmp.empty()) + painted_lines[layer_idx].insert(painted_lines[layer_idx].end(), painted_line_tmp.begin(), painted_line_tmp.end()); + } + } + } + } + + tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [&](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + // for(size_t layer_idx = 0; layer_idx < this->layers().size(); ++layer_idx) { + BOOST_LOG_TRIVIAL(debug) << "MMU segmentation of layer: " << layer_idx; + auto comp = [&edge_grids, &layer_idx](const PaintedLine &first, const PaintedLine &second) { + Point first_start_p = *(edge_grids[layer_idx].contours()[first.contour_idx].begin() + first.line_idx); + + return first.contour_idx < second.contour_idx || + (first.contour_idx == second.contour_idx && + (first.line_idx < second.line_idx || + (first.line_idx == second.line_idx && + Line(first_start_p, first.projected_line.a).length() < Line(first_start_p, second.projected_line.a).length()))); + }; + + std::sort(painted_lines[layer_idx].begin(), painted_lines[layer_idx].end(), comp); + std::vector &painted_lines_single = painted_lines[layer_idx]; + + if (!painted_lines_single.empty()) { + Polygons original_polygons; + for (const Slic3r::EdgeGrid::Contour &contour : edge_grids[layer_idx].contours()) { + Points points; + for (const Point &point : contour) points.emplace_back(point); + original_polygons.emplace_back(points); + } + + std::vector> color_poly = colorize_polygons(original_polygons, painted_lines_single); + MMU_Graph graph = build_graph(layer_idx, color_poly); + remove_multiple_edges_in_vertices(graph, color_poly); + graph.remove_nodes_with_one_arc(); + std::vector> segmentation = extract_colored_segments(graph); + for (const std::pair ®ion : segmentation) + segmented_regions[layer_idx].emplace_back(region); + } + } + }); // end of parallel_for + + return segmented_regions; +} +// --------------------MMU_END---------------------- + } // namespace Slic3r From 368b48b0a0b738145d67f633379ff8cdf20cbb9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 19 Apr 2021 07:12:42 +0200 Subject: [PATCH 03/26] WIP: Allows regions of MMU segmentation to be trimmed by chosen width. --- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/Print.cpp | 2 ++ src/libslic3r/PrintConfig.cpp | 8 ++++++++ src/libslic3r/PrintConfig.hpp | 2 ++ src/libslic3r/PrintObject.cpp | 18 ++++++++++++++++++ src/slic3r/GUI/Tab.cpp | 1 + 6 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 7db61a20f1..5f0e1f99e3 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -438,7 +438,7 @@ const std::vector& Preset::print_options() "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", "clip_multipart_objects", "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", - "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", + "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width", "wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits" }; return s_opts; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 8e23e27675..c8ab9b2c27 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -227,6 +227,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n osteps.emplace_back(posSupportMaterial); steps.emplace_back(psSkirt); steps.emplace_back(psBrim); + } else if (opt_key == "mmu_segmented_region_max_width") { + invalidated |= this->invalidate_all_steps(); } else { // for legacy, if we can't handle this option let's invalidate all steps //FIXME invalidate all steps of all objects as well? diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 5516b298d3..3280fe0a9d 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1288,6 +1288,14 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionBool(false)); + def = this->add("mmu_segmented_region_max_width", coFloat); + def->label = L("Maximum width of a segmented region"); + def->tooltip = L("Maximum width of a segmented region. Zero disables this feature."); + def->sidetext = L("mm (zero to disable)"); + def->min = 0; + def->mode = comExpert; + def->set_default_value(new ConfigOptionFloat(0.f)); + def = this->add("ironing", coBool); def->label = L("Enable ironing"); def->tooltip = L("Enable ironing of the top layers with the hot print head for smooth surface"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index aab5096624..7c50827ff9 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -962,6 +962,7 @@ public: ConfigOptionFloat max_print_height; ConfigOptionFloats min_print_speed; ConfigOptionFloat min_skirt_length; + ConfigOptionFloat mmu_segmented_region_max_width; ConfigOptionString notes; ConfigOptionFloats nozzle_diameter; ConfigOptionBool only_retract_when_crossing_perimeters; @@ -1037,6 +1038,7 @@ protected: OPT_PTR(max_print_height); OPT_PTR(min_print_speed); OPT_PTR(min_skirt_length); + OPT_PTR(mmu_segmented_region_max_width); OPT_PTR(notes); OPT_PTR(nozzle_diameter); OPT_PTR(only_retract_when_crossing_perimeters); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index e6abfe5a24..6dc87499c3 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -4182,6 +4182,21 @@ static void remove_multiple_edges_in_vertices(MMU_Graph &graph, const std::vecto } } +static void cut_segmented_layers(const LayerPtrs &layers, std::vector>> &segmented_regions, const float cut_width) { + tbb::parallel_for(tbb::blocked_range(0, segmented_regions.size()),[&](const tbb::blocked_range& range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + std::vector> segmented_regions_cuts; + for (const std::pair &colored_expoly : segmented_regions[layer_idx]) { + ExPolygons cut_colored_expoly = diff_ex({colored_expoly.first}, offset_ex(layers[layer_idx]->lslices, cut_width)); + for (const ExPolygon &expoly : cut_colored_expoly) { + segmented_regions_cuts.emplace_back(expoly, colored_expoly.second); + } + } + segmented_regions[layer_idx] = segmented_regions_cuts; + } + }); // end of parallel_for +} + std::vector>> PrintObject::mmu_segmentation_by_painting() { std::vector>> segmented_regions(this->layers().size()); @@ -4309,6 +4324,9 @@ std::vector>> PrintObject::mmu_segmenta } }); // end of parallel_for + if(m_print->config().mmu_segmented_region_max_width > 0.f) + cut_segmented_layers(m_layers, segmented_regions, float(-scale_(m_print->config().mmu_segmented_region_max_width))); + return segmented_regions; } // --------------------MMU_END---------------------- diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 0e954a9064..78ecc25c1c 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1586,6 +1586,7 @@ void TabPrint::build() optgroup = page->new_optgroup(L("Advanced")); optgroup->append_single_option_line("interface_shells"); + optgroup->append_single_option_line("mmu_segmented_region_max_width"); page = add_options_page(L("Advanced"), "wrench"); optgroup = page->new_optgroup(L("Extrusion width")); From 40d9e51b5ed59ca1cbfddab6416714b2985ebc4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 19 Apr 2021 19:21:06 +0200 Subject: [PATCH 04/26] WIP: Added support for top and bottom layers for MMU segmentation. --- src/libslic3r/Print.hpp | 3 + src/libslic3r/PrintObject.cpp | 243 +++++++++++++++++++++++++++++++++- 2 files changed, 245 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 5414497f86..6903fc6087 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -242,6 +242,9 @@ public: // Returns MMU segmentation based on painting in MMU segmentation gizmo std::vector>> mmu_segmentation_by_painting(); + // Returns MMU segmentation of top and bottom layers based on painting in MMU segmentation gizmo + std::vector> mmu_segmentation_top_and_bottom_layers(); + private: // to be called from Print only. friend class Print; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 6dc87499c3..b59db7733f 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -4197,6 +4197,244 @@ static void cut_segmented_layers(const LayerPtrs &layers, std::vector> PrintObject::mmu_segmentation_top_and_bottom_layers() +{ + std::vector> triangles_by_color(3); + triangles_by_color.assign(3, std::vector(m_layers.size())); + for (const ModelVolume *mv : this->model_object()->volumes) { + for (const auto ¶ms : {std::make_pair(EnforcerBlockerType::NONE, 0), std::make_pair(EnforcerBlockerType::ENFORCER, 1), + std::make_pair(EnforcerBlockerType::BLOCKER, 2)}) { + const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, params.first); + if (!mv->is_model_part() || custom_facets.indices.empty()) + continue; + + const Transform3f tr = this->trafo().cast() * mv->get_matrix().cast(); + for (size_t facet_idx = 0; facet_idx < custom_facets.indices.size(); ++facet_idx) { + float min_z = std::numeric_limits::max(); + float max_z = std::numeric_limits::lowest(); + + std::array facet; + Points projected_facet(3); + for (int p_idx = 0; p_idx < 3; ++p_idx) { + facet[p_idx] = tr * custom_facets.vertices[custom_facets.indices[facet_idx](p_idx)]; + max_z = std::max(max_z, facet[p_idx].z()); + min_z = std::min(min_z, facet[p_idx].z()); + } + + // Sort the vertices by z-axis for simplification of projected_facet on slices + std::sort(facet.begin(), facet.end(), [](const Vec3f &p1, const Vec3f &p2) { return p1.z() < p2.z(); }); + + for (int p_idx = 0; p_idx < 3; ++p_idx) { + projected_facet[p_idx] = Point(scale_(facet[p_idx].x()), scale_(facet[p_idx].y())); + projected_facet[p_idx] = projected_facet[p_idx] - this->center_offset(); + } + + ExPolygon triangle = ExPolygon(projected_facet); + + // Find lowest slice not below the triangle. + auto first_layer = std::upper_bound(m_layers.begin(), m_layers.end(), float(min_z - EPSILON), + [](float z, const Layer *l1) { return z < l1->slice_z + l1->height * 0.5; }); + auto last_layer = std::upper_bound(m_layers.begin(), m_layers.end(), float(max_z - EPSILON), + [](float z, const Layer *l1) { return z < l1->slice_z + l1->height * 0.5; }); + + if (last_layer == m_layers.end()) + --last_layer; + + if (first_layer == m_layers.end() || (first_layer != m_layers.begin() && facet[0].z() < (*first_layer)->print_z - EPSILON)) + --first_layer; + + for (auto layer_it = first_layer; (layer_it != (last_layer + 1) && layer_it != m_layers.end()); ++layer_it) { + size_t layer_idx = layer_it - m_layers.begin(); + triangles_by_color[params.second][layer_idx].emplace_back(triangle); + } + } + } + } + + auto get_extrusion_width = [&m_layers = std::as_const(m_layers)](const size_t layer_idx) -> float { + auto extrusion_width_it = std::max_element(m_layers[layer_idx]->m_regions.begin(), m_layers[layer_idx]->m_regions.end(), + [](const LayerRegion *l1, const LayerRegion *l2) { + return l1->region()->config().perimeter_extrusion_width < + l2->region()->config().perimeter_extrusion_width; + }); + assert(extrusion_width_it != m_layers[layer_idx]->m_regions.end()); + return float((*extrusion_width_it)->region()->config().perimeter_extrusion_width); + }; + + auto get_top_solid_layers = [&m_layers = std::as_const(m_layers)](const size_t layer_idx) -> int { + auto top_solid_layer_it = std::max_element(m_layers[layer_idx]->m_regions.begin(), m_layers[layer_idx]->m_regions.end(), + [](const LayerRegion *l1, const LayerRegion *l2) { + return l1->region()->config().top_solid_layers < l2->region()->config().top_solid_layers; + }); + assert(top_solid_layer_it != m_layers[layer_idx]->m_regions.end()); + return (*top_solid_layer_it)->region()->config().top_solid_layers; + }; + + auto get_bottom_solid_layers = [&m_layers = std::as_const(m_layers)](const size_t layer_idx) -> int { + auto top_bottom_layer_it = std::max_element(m_layers[layer_idx]->m_regions.begin(), m_layers[layer_idx]->m_regions.end(), + [](const LayerRegion *l1, const LayerRegion *l2) { + return l1->region()->config().bottom_solid_layers < l2->region()->config().bottom_solid_layers; + }); + assert(top_bottom_layer_it != m_layers[layer_idx]->m_regions.end()); + return (*top_bottom_layer_it)->region()->config().bottom_solid_layers; + }; + + std::vector top_layers(m_layers.size()); + top_layers.back() = m_layers.back()->lslices; + tbb::parallel_for(tbb::blocked_range(1, m_layers.size()), [&](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + float extrusion_width = 0.1f * float(scale_(get_extrusion_width(layer_idx))); + top_layers[layer_idx - 1] = diff_ex(m_layers[layer_idx - 1]->lslices, offset_ex(m_layers[layer_idx]->lslices, extrusion_width)); + } + }); // end of parallel_for + + std::vector bottom_layers(m_layers.size()); + bottom_layers.front() = m_layers.front()->lslices; + tbb::parallel_for(tbb::blocked_range(0, m_layers.size() - 1), [&](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + float extrusion_width = 0.1f * float(scale_(get_extrusion_width(layer_idx))); + bottom_layers[layer_idx + 1] = diff_ex(m_layers[layer_idx + 1]->lslices, offset_ex(m_layers[layer_idx]->lslices, extrusion_width)); + } + }); // end of parallel_for + + tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [&](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + float extrusion_width = 0.1f * float(scale_(get_extrusion_width(layer_idx))); + for (std::vector &triangles : triangles_by_color) { + if (!triangles[layer_idx].empty() && (!top_layers[layer_idx].empty() || !bottom_layers[layer_idx].empty())) { + ExPolygons connected = union_ex(offset_ex(triangles[layer_idx], float(10 * SCALED_EPSILON))); + triangles[layer_idx] = union_ex(offset_ex(offset_ex(connected, -extrusion_width / 1), extrusion_width / 1)); + } else { + triangles[layer_idx].clear(); + } + } + } + }); // end of parallel_for + + std::vector> triangles_by_color_bottom(3); + std::vector> triangles_by_color_top(3); + triangles_by_color_bottom.assign(3, std::vector(m_layers.size())); + triangles_by_color_top.assign(3, std::vector(m_layers.size())); + + for (size_t layer_idx = 0; layer_idx < this->layers().size(); ++layer_idx) { + BOOST_LOG_TRIVIAL(debug) << "MMU segmentation of top layer: " << layer_idx; + float extrusion_width = scale_(get_extrusion_width(layer_idx)); + int top_solid_layers = get_top_solid_layers(layer_idx); + ExPolygons top_expolygon = top_layers[layer_idx]; + if (top_expolygon.empty()) + continue; + + for (size_t color_idx = 0; color_idx < triangles_by_color.size(); ++color_idx) { + if (triangles_by_color[color_idx][layer_idx].empty()) + continue; + ExPolygons intersection_poly = intersection_ex(triangles_by_color[color_idx][layer_idx], top_expolygon); + if (!intersection_poly.empty()) { + triangles_by_color_top[color_idx][layer_idx].insert(triangles_by_color_top[color_idx][layer_idx].end(), intersection_poly.begin(), + intersection_poly.end()); + for (int last_idx = int(layer_idx) - 1; last_idx >= std::max(int(layer_idx - top_solid_layers), int(0)); --last_idx) { + float offset_value = float(layer_idx - last_idx) * (-1.0f) * extrusion_width; + if (offset_ex(top_expolygon, offset_value).empty()) + continue; + ExPolygons layer_slices_trimmed = m_layers[last_idx]->lslices; + + for (int last_idx_1 = last_idx; last_idx_1 < int(layer_idx); ++last_idx_1) { + layer_slices_trimmed = intersection_ex(layer_slices_trimmed, m_layers[last_idx_1 + 1]->lslices); + } + + ExPolygons offset_e = offset_ex(layer_slices_trimmed, offset_value); + ExPolygons intersection_poly_2 = intersection_ex(triangles_by_color_top[color_idx][layer_idx], offset_e); + triangles_by_color_top[color_idx][last_idx].insert(triangles_by_color_top[color_idx][last_idx].end(), intersection_poly_2.begin(), + intersection_poly_2.end()); + } + } + } + } + + for (size_t layer_idx = 0; layer_idx < this->layers().size(); ++layer_idx) { + BOOST_LOG_TRIVIAL(debug) << "MMU segmentation of bottom layer: " << layer_idx; + float extrusion_width = scale_(get_extrusion_width(layer_idx)); + int bottom_solid_layers = get_bottom_solid_layers(layer_idx); + ExPolygons bottom_expolygon = bottom_layers[layer_idx]; + if (bottom_expolygon.empty()) + continue; + + for (size_t color_idx = 0; color_idx < triangles_by_color.size(); ++color_idx) { + if (triangles_by_color[color_idx][layer_idx].empty()) + continue; + + ExPolygons intersection_poly = intersection_ex(triangles_by_color[color_idx][layer_idx], bottom_expolygon); + if (!intersection_poly.empty()) { + triangles_by_color_bottom[color_idx][layer_idx].insert(triangles_by_color_bottom[color_idx][layer_idx].end(), intersection_poly.begin(), + intersection_poly.end()); + for (size_t last_idx = layer_idx + 1; last_idx < std::min(layer_idx + bottom_solid_layers, m_layers.size()); ++last_idx) { + float offset_value = float(last_idx - layer_idx) * (-1.0f) * extrusion_width; + if (offset_ex(bottom_expolygon, offset_value).empty()) + continue; + ExPolygons layer_slices_trimmed = m_layers[last_idx]->lslices; + + for (int last_idx_1 = int(last_idx); last_idx_1 > int(layer_idx); --last_idx_1) { + layer_slices_trimmed = intersection_ex(layer_slices_trimmed, offset_ex(m_layers[last_idx_1 - 1]->lslices, offset_value)); + } + + ExPolygons offset_e = offset_ex(layer_slices_trimmed, offset_value); + ExPolygons intersection_poly_2 = intersection_ex(triangles_by_color_bottom[color_idx][layer_idx], offset_e); + triangles_by_color_bottom[color_idx][last_idx].insert(triangles_by_color_bottom[color_idx][last_idx].end(), intersection_poly_2.begin(), + intersection_poly_2.end()); + } + } + } + } + + std::vector> triangles_by_color_merged(3); + triangles_by_color_merged.assign(3, std::vector(m_layers.size())); + for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++layer_idx) { + for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) { + triangles_by_color_merged[color_idx][layer_idx].insert(triangles_by_color_merged[color_idx][layer_idx].end(), + triangles_by_color_bottom[color_idx][layer_idx].begin(), + triangles_by_color_bottom[color_idx][layer_idx].end()); + triangles_by_color_merged[color_idx][layer_idx].insert(triangles_by_color_merged[color_idx][layer_idx].end(), + triangles_by_color_top[color_idx][layer_idx].begin(), + triangles_by_color_top[color_idx][layer_idx].end()); + triangles_by_color_merged[color_idx][layer_idx] = union_ex(triangles_by_color_merged[color_idx][layer_idx]); + } + + // Cut all colors for cases when two colors are overlapping + for (size_t color_idx = 1; color_idx < triangles_by_color_merged.size(); ++color_idx) { + triangles_by_color_merged[color_idx][layer_idx] = diff_ex(triangles_by_color_merged[color_idx][layer_idx], + triangles_by_color_merged[color_idx - 1][layer_idx]); + } + } + + return triangles_by_color_merged; +} + +static std::vector>> merge_segmented_layers( + const std::vector>> &segmented_regions, const std::vector> &top_and_bottom_layers) +{ + std::vector>> segmented_regions_merged(segmented_regions.size()); + + tbb::parallel_for(tbb::blocked_range(0, segmented_regions.size()), [&](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - merging region: " << layer_idx; + for (const std::pair &colored_expoly : segmented_regions[layer_idx]) { + ExPolygons cut_colored_expoly = {colored_expoly.first}; + for (const std::vector &top_and_bottom_layer : top_and_bottom_layers) + cut_colored_expoly = diff_ex(cut_colored_expoly, top_and_bottom_layer[layer_idx]); + for (ExPolygon &ex_poly : cut_colored_expoly) + segmented_regions_merged[layer_idx].emplace_back(ex_poly, colored_expoly.second); + + } + + for (size_t color_idx = 0; color_idx < top_and_bottom_layers.size(); ++color_idx) { + ExPolygons top_and_bottom_expoly = top_and_bottom_layers[color_idx][layer_idx]; + for (const ExPolygon &expoly : top_and_bottom_expoly) { segmented_regions_merged[layer_idx].emplace_back(expoly, color_idx); } + } + } + }); // end of parallel_for + + return segmented_regions_merged; +} + std::vector>> PrintObject::mmu_segmentation_by_painting() { std::vector>> segmented_regions(this->layers().size()); @@ -4327,7 +4565,10 @@ std::vector>> PrintObject::mmu_segmenta if(m_print->config().mmu_segmented_region_max_width > 0.f) cut_segmented_layers(m_layers, segmented_regions, float(-scale_(m_print->config().mmu_segmented_region_max_width))); - return segmented_regions; +// return segmented_regions; + std::vector> top_and_bottom_layers = mmu_segmentation_top_and_bottom_layers(); + std::vector>> segmented_regions_merged = merge_segmented_layers(segmented_regions, top_and_bottom_layers); + return segmented_regions_merged; } // --------------------MMU_END---------------------- From 9153874041054cd881d9973f154ead1dd51dac00 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Mon, 19 Apr 2021 15:48:07 +0200 Subject: [PATCH 05/26] Disabled thick bridges, updated support settings. Bundle refactoring. --- resources/profiles/PrusaResearch.idx | 320 ++-- resources/profiles/PrusaResearch.ini | 2072 ++++++++++---------------- 2 files changed, 964 insertions(+), 1428 deletions(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index 8c14026ce1..dc3c79cd55 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,159 +1,161 @@ -min_slic3r_version = 2.3.0-rc1 -1.2.4 Updated cost/density values in filament settings. Various changes in print settings. -1.2.3 Updated firmware version. Updated end g-code in MMU2 printer profiles. -1.2.2 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles. -1.2.1 Updated FW version for MK2.5 family printers. -1.2.0 Added full_fan_speed_layer value for PETG. Increased support interface spacing for 0.6mm nozzle profiles. Updated firmware version. -min_slic3r_version = 2.3.0-beta2 -1.2.0-beta1 Updated end g-code. Added full_fan_speed_layer values. -min_slic3r_version = 2.3.0-beta0 -1.2.0-beta0 Adjusted infill anchor limits. Added filament spool weights. -min_slic3r_version = 2.3.0-alpha4 -1.2.0-alpha1 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles. -1.2.0-alpha0 Added filament spool weights -min_slic3r_version = 2.2.0-alpha3 -1.1.13 Updated firmware version. Updated end g-code in MMU2 printer profiles. -1.1.12 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles. -1.1.11 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles. -1.1.10 Updated firmware version. -1.1.9 Updated K values in filament profiles (linear advance). Added new filament profiles and SLA materials. -1.1.8 Updated start/end g-code scripts for MK3 family printer profiles (reduced extruder motor current for some print profiles). Added new filament and SLA material profiles. -1.1.7 Updated end g-code for MMU2 Single printer profiles. Added/updated filament and SLA material profiles. -1.1.6 Updated firmware version for MK2.5/S and MK3/S. -1.1.5 Updated MMU1 specific retraction settings for Prusament PC Blend -1.1.4 Added Prusament PC Blend filament profile. -1.1.3 Added SLA material and filament profile -1.1.2 Added renamed_from fields for PETG filaments to indicate that they were renamed from PET. -1.1.1 Added Verbatim and Fiberlogy PETG filament profiles. Updated auto cooling settings for ABS. -1.1.1-beta Updated for PrusaSlicer 2.2.0-beta -1.1.1-alpha4 Extended list of default filaments to be installed, top/bottom_solid_min_thickness defined, infill_acceleration changed etc -1.1.1-alpha3 Print bed textures are now configurable from the Preset Bundle. Requires PrusaSlicer 2.2.0-alpha3 and newer. -# The following line (max_slic3r_version) forces the users of PrusaSlicer 2.2.0-alpha3 and newer to update the profiles to 1.1.1-alpha3 and newer, -# so they will see the print bed. -max_slic3r_version = 2.2.0-alpha2 -min_slic3r_version = 2.2.0-alpha0 -1.1.1-alpha2 Bumped up config version, so our in house customer will get updated profiles. -1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0 -min_slic3r_version = 2.1.1-beta0 -1.0.11 Updated firmware version. -1.0.10 Updated firmware version for MK2.5/S and MK3/S. -1.0.9 Updated firmware version for MK2.5/S and MK3/S. -1.0.8 Various changes in FFF profiles, new filaments/materials added. See changelog. -1.0.7 Updated layer height limits for MINI -1.0.6 Added Prusa MINI profiles -min_slic3r_version = 2.1.0-alpha0 -1.0.5 Added SLA materials -1.0.4 Updated firmware version and 0.25mm nozzle profiles -1.0.3 Added filament profiles -1.0.2 Added SLA materials -1.0.1 Updated MK3 firmware version check to 3.8.0, new soluble support profiles for 0.6mm nozzle diameter MMU2S printers. -1.0.0 Updated end G-code for the MMU2 profiles to lift the extruder at the end of print. Wipe tower bridging distance was made smaller for soluble supports. -1.0.0-beta1 Updated color for the ASA filaments to differ from the other filaments. Single extruder printers now have no extruder color assigned, obects and toolpaths will be colored with the color of the active filament. -1.0.0-beta0 Printer model checks in start G-codes, ASA filament profiles, limits on min / max SL1 exposition times -1.0.0-alpha2 Printer model and nozzle diameter check -1.0.0-alpha1 Added Prusament ASA profile -1.0.0-alpha0 Filament specific retract for PET and similar copolymers, and for FLEX -min_slic3r_version = 1.42.0-alpha6 -0.8.10 Updated firmware version. -0.8.9 Updated firmware version for MK2.5/S and MK3/S. -0.8.8 Updated firmware version for MK2.5/S and MK3/S. -0.8.7 Updated firmware version -0.8.6 Updated firmware version for MK2.5/S and MK3/S -0.8.5 Updated SL1 printer and material settings -0.8.4 Added Prusament ASA profile -0.8.3 FW version and SL1 materials update -0.8.2 FFF and SL1 settings update -0.8.1 Output settings and SLA materials update -0.8.0 Updated for the PrusaSlicer 2.0.0 final release -0.8.0-rc2 Updated firmware versions for MK2.5/S and MK3/S -0.8.0-rc1 Updated SLA profiles -0.8.0-rc Updated for the PrusaSlicer 2.0.0-rc release -0.8.0-beta4 Updated SLA profiles -0.8.0-beta3 Updated SLA profiles -0.8.0-beta2 Updated SLA profiles -0.8.0-beta1 Updated SLA profiles -0.8.0-beta Updated SLA profiles -0.8.0-alpha9 Updated SLA and FFF profiles -0.8.0-alpha8 Updated SLA profiles -0.8.0-alpha7 Updated SLA profiles -0.8.0-alpha6 Updated SLA profiles -min_slic3r_version = 1.42.0-alpha -0.8.0-alpha Updated SLA profiles -0.4.0-alpha4 Updated SLA profiles -0.4.0-alpha3 Update of SLA profiles -0.4.0-alpha2 First SLA profiles -min_slic3r_version = 1.41.3-alpha -0.4.12 Updated firmware version for MK2.5/S and MK3/S. -0.4.11 Updated firmware version for MK2.5/S and MK3/S. -0.4.10 Updated firmware version -0.4.9 Updated firmware version for MK2.5/S and MK3/S -0.4.8 MK2.5/3/S FW update -0.4.7 MK2/S/MMU FW update -0.4.6 Updated firmware versions for MK2.5/S and MK3/S -0.4.5 Enabled remaining time support for MK2/S/MMU1 -0.4.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt -0.4.3 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt -0.4.2 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt -0.4.1 New MK2.5S and MK3S FW versions -0.4.0 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt -min_slic3r_version = 1.41.1 -0.3.11 Updated firmware version for MK2.5/S and MK3/S. -0.3.10 Updated firmware version -0.3.9 Updated firmware version for MK2.5/S and MK3/S -0.3.8 MK2.5/3/S FW update -0.3.7 MK2/S/MMU FW update -0.3.6 Updated firmware versions for MK2.5 and MK3 -0.3.5 New MK2.5 and MK3 FW versions -0.3.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt -0.3.3 Prusament PETG released -0.3.2 New MK2.5 and MK3 FW versions -0.3.1 New MK2.5 and MK3 FW versions -0.3.0 New MK2.5 and MK3 FW version -min_slic3r_version = 1.41.0-alpha -0.2.9 New MK2.5 and MK3 FW versions -0.2.8 New MK2.5 and MK3 FW version -min_slic3r_version = 1.41.1 -0.2.7 New MK2.5 and MK3 FW version -0.2.6 Added MMU2 MK2.5 settings -min_slic3r_version = 1.41.0-alpha -0.2.5 Prusament is out - added prusament settings -0.2.4 Added soluble support profiles for MMU2 -0.2.3 Added materials for MMU2 single mode, edited MK3 xy stealth feedrate limit -0.2.2 Edited MMU2 Single mode purge line -0.2.1 Added PET and BVOH settings for MMU2 -0.2.0-beta5 Fixed MMU1 ramming parameters -0.2.0-beta4 Added filament loading speed at start, increased minimal purge on wipe tower -0.2.0-beta3 Edited ramming parameters and filament cooling moves for MMU2 -0.2.0-beta2 Edited first layer speed and wipe tower position -0.2.0-beta Removed limit on the MK3MMU2 height, added legacy M204 S T format to the MK2 profiles -0.2.0-alpha8 Added filament_load/unload_time for the PLA/ABS MMU2 filament presets. -0.2.0-alpha7 Vojtech's fix the incorrect *MK3* references -0.2.0-alpha6 Jindra's way to fix the 0.2.0-alpha5 version -0.2.0-alpha5 Bumped up firmware versions for MK2.5/MK3 to 3.3.1, disabled priming areas for MK3MMU2 -0.2.0-alpha4 Extended the custom start/end G-codes of the MMU2.0 printers for no priming towers. -0.2.0-alpha3 Adjusted machine limits for time estimates, added filament density and cost -0.2.0-alpha2 Renamed the key MK3SMMU to MK3MMU2, added a generic PLA MMU2 material -0.2.0-alpha1 added initial profiles for the i3 MK3 Multi Material Upgrade 2.0 -0.2.0-alpha moved machine limits from the start G-code to the new print profile parameters -min_slic3r_version = 1.40.0 -0.1.18 Updated firmware version -0.1.17 Updated firmware version for MK2.5/S and MK3/S -0.1.16 MK2.5/3/S FW update -0.1.15 MK2/S/MMU FW update -0.1.14 Updated firmware versions for MK2.5 and MK3 -0.1.13 New MK2.5 and MK3 FW versions -0.1.12 New MK2.5 and MK3 FW versions -0.1.11 fw version changed to 3.3.1 -0.1.10 MK3 jerk and acceleration update -0.1.9 edited support extrusion width for 0.25 and 0.6 nozzles -0.1.8 extrusion width for 0,25, 0.6 and variable layer height fixes -0.1.7 Fixed errors in 0.25mm and 0.6mm profiles -0.1.6 Split the MK2.5 profile from the MK2S -min_slic3r_version = 1.40.0-beta -0.1.5 fixed printer_variant fields for the i3 MK3 0.25 and 0.6mm nozzles -0.1.4 edited fw version, added z-raise after print -min_slic3r_version = 1.40.0-alpha -0.1.3 Fixed an incorrect position of the max_print_height parameter -0.1.2 Wipe tower changes -0.1.1 Minor print speed adjustments -0.1.0 Initial +min_slic3r_version = 2.4.0-alpha0 +1.3.0-alpha0 Disabled thick bridges, updated support settings. +min_slic3r_version = 2.3.0-rc1 +1.2.4 Updated cost/density values in filament settings. Various changes in print settings. +1.2.3 Updated firmware version. Updated end g-code in MMU2 printer profiles. +1.2.2 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles. +1.2.1 Updated FW version for MK2.5 family printers. +1.2.0 Added full_fan_speed_layer value for PETG. Increased support interface spacing for 0.6mm nozzle profiles. Updated firmware version. +min_slic3r_version = 2.3.0-beta2 +1.2.0-beta1 Updated end g-code. Added full_fan_speed_layer values. +min_slic3r_version = 2.3.0-beta0 +1.2.0-beta0 Adjusted infill anchor limits. Added filament spool weights. +min_slic3r_version = 2.3.0-alpha4 +1.2.0-alpha1 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles. +1.2.0-alpha0 Added filament spool weights +min_slic3r_version = 2.2.0-alpha3 +1.1.13 Updated firmware version. Updated end g-code in MMU2 printer profiles. +1.1.12 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles. +1.1.11 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles. +1.1.10 Updated firmware version. +1.1.9 Updated K values in filament profiles (linear advance). Added new filament profiles and SLA materials. +1.1.8 Updated start/end g-code scripts for MK3 family printer profiles (reduced extruder motor current for some print profiles). Added new filament and SLA material profiles. +1.1.7 Updated end g-code for MMU2 Single printer profiles. Added/updated filament and SLA material profiles. +1.1.6 Updated firmware version for MK2.5/S and MK3/S. +1.1.5 Updated MMU1 specific retraction settings for Prusament PC Blend +1.1.4 Added Prusament PC Blend filament profile. +1.1.3 Added SLA material and filament profile +1.1.2 Added renamed_from fields for PETG filaments to indicate that they were renamed from PET. +1.1.1 Added Verbatim and Fiberlogy PETG filament profiles. Updated auto cooling settings for ABS. +1.1.1-beta Updated for PrusaSlicer 2.2.0-beta +1.1.1-alpha4 Extended list of default filaments to be installed, top/bottom_solid_min_thickness defined, infill_acceleration changed etc +1.1.1-alpha3 Print bed textures are now configurable from the Preset Bundle. Requires PrusaSlicer 2.2.0-alpha3 and newer. +# The following line (max_slic3r_version) forces the users of PrusaSlicer 2.2.0-alpha3 and newer to update the profiles to 1.1.1-alpha3 and newer, +# so they will see the print bed. +max_slic3r_version = 2.2.0-alpha2 +min_slic3r_version = 2.2.0-alpha0 +1.1.1-alpha2 Bumped up config version, so our in house customer will get updated profiles. +1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0 +min_slic3r_version = 2.1.1-beta0 +1.0.11 Updated firmware version. +1.0.10 Updated firmware version for MK2.5/S and MK3/S. +1.0.9 Updated firmware version for MK2.5/S and MK3/S. +1.0.8 Various changes in FFF profiles, new filaments/materials added. See changelog. +1.0.7 Updated layer height limits for MINI +1.0.6 Added Prusa MINI profiles +min_slic3r_version = 2.1.0-alpha0 +1.0.5 Added SLA materials +1.0.4 Updated firmware version and 0.25mm nozzle profiles +1.0.3 Added filament profiles +1.0.2 Added SLA materials +1.0.1 Updated MK3 firmware version check to 3.8.0, new soluble support profiles for 0.6mm nozzle diameter MMU2S printers. +1.0.0 Updated end G-code for the MMU2 profiles to lift the extruder at the end of print. Wipe tower bridging distance was made smaller for soluble supports. +1.0.0-beta1 Updated color for the ASA filaments to differ from the other filaments. Single extruder printers now have no extruder color assigned, obects and toolpaths will be colored with the color of the active filament. +1.0.0-beta0 Printer model checks in start G-codes, ASA filament profiles, limits on min / max SL1 exposition times +1.0.0-alpha2 Printer model and nozzle diameter check +1.0.0-alpha1 Added Prusament ASA profile +1.0.0-alpha0 Filament specific retract for PET and similar copolymers, and for FLEX +min_slic3r_version = 1.42.0-alpha6 +0.8.10 Updated firmware version. +0.8.9 Updated firmware version for MK2.5/S and MK3/S. +0.8.8 Updated firmware version for MK2.5/S and MK3/S. +0.8.7 Updated firmware version +0.8.6 Updated firmware version for MK2.5/S and MK3/S +0.8.5 Updated SL1 printer and material settings +0.8.4 Added Prusament ASA profile +0.8.3 FW version and SL1 materials update +0.8.2 FFF and SL1 settings update +0.8.1 Output settings and SLA materials update +0.8.0 Updated for the PrusaSlicer 2.0.0 final release +0.8.0-rc2 Updated firmware versions for MK2.5/S and MK3/S +0.8.0-rc1 Updated SLA profiles +0.8.0-rc Updated for the PrusaSlicer 2.0.0-rc release +0.8.0-beta4 Updated SLA profiles +0.8.0-beta3 Updated SLA profiles +0.8.0-beta2 Updated SLA profiles +0.8.0-beta1 Updated SLA profiles +0.8.0-beta Updated SLA profiles +0.8.0-alpha9 Updated SLA and FFF profiles +0.8.0-alpha8 Updated SLA profiles +0.8.0-alpha7 Updated SLA profiles +0.8.0-alpha6 Updated SLA profiles +min_slic3r_version = 1.42.0-alpha +0.8.0-alpha Updated SLA profiles +0.4.0-alpha4 Updated SLA profiles +0.4.0-alpha3 Update of SLA profiles +0.4.0-alpha2 First SLA profiles +min_slic3r_version = 1.41.3-alpha +0.4.12 Updated firmware version for MK2.5/S and MK3/S. +0.4.11 Updated firmware version for MK2.5/S and MK3/S. +0.4.10 Updated firmware version +0.4.9 Updated firmware version for MK2.5/S and MK3/S +0.4.8 MK2.5/3/S FW update +0.4.7 MK2/S/MMU FW update +0.4.6 Updated firmware versions for MK2.5/S and MK3/S +0.4.5 Enabled remaining time support for MK2/S/MMU1 +0.4.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt +0.4.3 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt +0.4.2 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt +0.4.1 New MK2.5S and MK3S FW versions +0.4.0 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt +min_slic3r_version = 1.41.1 +0.3.11 Updated firmware version for MK2.5/S and MK3/S. +0.3.10 Updated firmware version +0.3.9 Updated firmware version for MK2.5/S and MK3/S +0.3.8 MK2.5/3/S FW update +0.3.7 MK2/S/MMU FW update +0.3.6 Updated firmware versions for MK2.5 and MK3 +0.3.5 New MK2.5 and MK3 FW versions +0.3.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt +0.3.3 Prusament PETG released +0.3.2 New MK2.5 and MK3 FW versions +0.3.1 New MK2.5 and MK3 FW versions +0.3.0 New MK2.5 and MK3 FW version +min_slic3r_version = 1.41.0-alpha +0.2.9 New MK2.5 and MK3 FW versions +0.2.8 New MK2.5 and MK3 FW version +min_slic3r_version = 1.41.1 +0.2.7 New MK2.5 and MK3 FW version +0.2.6 Added MMU2 MK2.5 settings +min_slic3r_version = 1.41.0-alpha +0.2.5 Prusament is out - added prusament settings +0.2.4 Added soluble support profiles for MMU2 +0.2.3 Added materials for MMU2 single mode, edited MK3 xy stealth feedrate limit +0.2.2 Edited MMU2 Single mode purge line +0.2.1 Added PET and BVOH settings for MMU2 +0.2.0-beta5 Fixed MMU1 ramming parameters +0.2.0-beta4 Added filament loading speed at start, increased minimal purge on wipe tower +0.2.0-beta3 Edited ramming parameters and filament cooling moves for MMU2 +0.2.0-beta2 Edited first layer speed and wipe tower position +0.2.0-beta Removed limit on the MK3MMU2 height, added legacy M204 S T format to the MK2 profiles +0.2.0-alpha8 Added filament_load/unload_time for the PLA/ABS MMU2 filament presets. +0.2.0-alpha7 Vojtech's fix the incorrect *MK3* references +0.2.0-alpha6 Jindra's way to fix the 0.2.0-alpha5 version +0.2.0-alpha5 Bumped up firmware versions for MK2.5/MK3 to 3.3.1, disabled priming areas for MK3MMU2 +0.2.0-alpha4 Extended the custom start/end G-codes of the MMU2.0 printers for no priming towers. +0.2.0-alpha3 Adjusted machine limits for time estimates, added filament density and cost +0.2.0-alpha2 Renamed the key MK3SMMU to MK3MMU2, added a generic PLA MMU2 material +0.2.0-alpha1 added initial profiles for the i3 MK3 Multi Material Upgrade 2.0 +0.2.0-alpha moved machine limits from the start G-code to the new print profile parameters +min_slic3r_version = 1.40.0 +0.1.18 Updated firmware version +0.1.17 Updated firmware version for MK2.5/S and MK3/S +0.1.16 MK2.5/3/S FW update +0.1.15 MK2/S/MMU FW update +0.1.14 Updated firmware versions for MK2.5 and MK3 +0.1.13 New MK2.5 and MK3 FW versions +0.1.12 New MK2.5 and MK3 FW versions +0.1.11 fw version changed to 3.3.1 +0.1.10 MK3 jerk and acceleration update +0.1.9 edited support extrusion width for 0.25 and 0.6 nozzles +0.1.8 extrusion width for 0,25, 0.6 and variable layer height fixes +0.1.7 Fixed errors in 0.25mm and 0.6mm profiles +0.1.6 Split the MK2.5 profile from the MK2S +min_slic3r_version = 1.40.0-beta +0.1.5 fixed printer_variant fields for the i3 MK3 0.25 and 0.6mm nozzles +0.1.4 edited fw version, added z-raise after print +min_slic3r_version = 1.40.0-alpha +0.1.3 Fixed an incorrect position of the max_print_height parameter +0.1.2 Wipe tower changes +0.1.1 Minor print speed adjustments +0.1.0 Initial diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 796d578226..16737d1563 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,18 +5,15 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 1.2.4 +config_version = 1.3.0-alpha0 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% # The printer models will be shown by the Configuration Wizard in this order, # also the first model installed & the first nozzle installed will be activated after install. -#TODO: One day we may differentiate variants of the nozzles / hot ends, -#for example by the melt zone size, or whether the nozzle is hardened. # Printer model name will be shown by the installation wizard. - [printer_model:MINI] name = Original Prusa MINI && MINI+ variants = 0.4; 0.25; 0.6; 0.8 @@ -128,20 +125,21 @@ default_materials = Prusa Orange Tough @0.05 # All presets starting with asterisk, for example *common*, are intermediate and they will # not make it into the user interface. -# Common print preset, mostly derived from MK2 single material with a 0.4mm nozzle. -# All other print presets will derive from the *common* print preset. +# Common print presets + [print:*common*] avoid_crossing_perimeters = 0 +thick_bridges = 0 bridge_acceleration = 1000 bridge_angle = 0 -bridge_flow_ratio = 0.8 -bridge_speed = 20 +bridge_flow_ratio = 1 +bridge_speed = 25 brim_width = 0 clip_multipart_objects = 1 compatible_printers = complete_objects = 0 default_acceleration = 1000 -dont_support_bridges = 1 +dont_support_bridges = 0 elefant_foot_compensation = 0.2 ensure_vertical_shell_thickness = 1 external_fill_pattern = rectilinear @@ -154,7 +152,7 @@ extrusion_width = 0.45 fill_angle = 45 fill_density = 20% fill_pattern = cubic -first_layer_acceleration = 1000 +first_layer_acceleration = 800 first_layer_extrusion_width = 0.42 first_layer_height = 0.2 first_layer_speed = 20 @@ -183,6 +181,7 @@ perimeter_extrusion_width = 0.45 post_process = print_settings_id = raft_layers = 0 +raft_first_layer_density = 90% resolution = 0 seam_position = nearest single_extruder_multi_material_priming = 1 @@ -203,7 +202,7 @@ support_material_interface_extruder = 0 support_material_angle = 0 support_material_buildplate_only = 0 support_material_enforce_layers = 0 -support_material_contact_distance = 0.1 +support_material_contact_distance = 0.2 support_material_interface_contact_loops = 0 support_material_interface_layers = 2 support_material_interface_spacing = 0.2 @@ -212,9 +211,10 @@ support_material_pattern = rectilinear support_material_spacing = 2 support_material_speed = 50 support_material_synchronize_layers = 0 -support_material_threshold = 55 +support_material_threshold = 50 support_material_with_sheath = 0 -support_material_xy_spacing = 50% +support_material_xy_spacing = 60% +support_material_bottom_interface_layers = 0 thin_walls = 0 top_infill_extrusion_width = 0.45 top_solid_infill_speed = 40 @@ -240,20 +240,15 @@ wipe_tower_x = 170 wipe_tower_y = 125 [print:*MK306*] +inherits = *MK3* fill_pattern = gyroid fill_density = 15% -single_extruder_multi_material_priming = 0 -travel_speed = 180 -wipe_tower_x = 170 -wipe_tower_y = 125 - -## MINI [print:*MINI*] fill_pattern = grid travel_speed = 150 wipe_tower = 0 -default_acceleration = 1250 +default_acceleration = 1000 first_layer_acceleration = 800 infill_acceleration = 1000 bridge_acceleration = 1000 @@ -262,7 +257,6 @@ max_print_speed = 150 extruder_clearance_height = 20 extruder_clearance_radius = 35 -# Print parameters common to a 0.25mm diameter nozzle. [print:*0.25nozzle*] elefant_foot_compensation = 0 external_perimeter_extrusion_width = 0.25 @@ -277,7 +271,11 @@ support_material_interface_layers = 0 support_material_interface_spacing = 0.15 support_material_spacing = 1 support_material_xy_spacing = 150% +support_material_contact_distance = 0.1 output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode +thick_bridges = 0 +bridge_flow_ratio = 1 +bridge_speed = 20 [print:*0.25nozzleMK3*] inherits = *0.25nozzle* @@ -288,7 +286,6 @@ infill_speed = 45 solid_infill_speed = 45 top_solid_infill_speed = 30 support_material_speed = 40 -bridge_speed = 20 gap_fill_speed = 30 perimeter_acceleration = 500 infill_acceleration = 1000 @@ -307,7 +304,6 @@ solid_infill_speed = 40 infill_acceleration = 800 first_layer_acceleration = 500 -# Print parameters common to a 0.6mm diameter nozzle. [print:*0.6nozzle*] external_perimeter_extrusion_width = 0.61 extrusion_width = 0.67 @@ -324,6 +320,31 @@ output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_heig infill_anchor_max = 15 top_solid_min_thickness = 0.9 bottom_solid_min_thickness = 0.6 +thick_bridges = 1 +bridge_flow_ratio = 0.95 +bridge_speed = 25 + +[print:*0.6nozzleMK3*] +inherits = *0.6nozzle* +external_perimeter_extrusion_width = 0.65 +extrusion_width = 0.65 +infill_extrusion_width = 0.65 +thick_bridges = 0 + +[print:*0.6nozzleMINI*] +inherits = *0.6nozzleMK3* +infill_extrusion_width = 0.68 +solid_infill_extrusion_width = 0.68 +fill_pattern = gyroid +fill_density = 15% +travel_speed = 150 +perimeter_acceleration = 800 +infill_acceleration = 1000 +bridge_acceleration = 1000 +first_layer_acceleration = 800 +default_acceleration = 1250 +support_material_speed = 40 +support_material_interface_speed = 100% [print:*0.8nozzle*] external_perimeter_extrusion_width = 0.9 @@ -356,34 +377,12 @@ bridge_flow_ratio = 0.9 perimeter_acceleration = 800 infill_acceleration = 1000 bridge_acceleration = 1000 -first_layer_acceleration = 1000 +first_layer_acceleration = 800 default_acceleration = 1000 top_solid_min_thickness = 1.2 bottom_solid_min_thickness = 0.8 single_extruder_multi_material_priming = 0 - -[print:*0.6nozzleMK3*] -inherits = *0.6nozzle* -external_perimeter_extrusion_width = 0.65 -extrusion_width = 0.65 -infill_extrusion_width = 0.65 -bridge_flow_ratio = 0.95 -bridge_speed = 25 - -[print:*0.6nozzleMINI*] -inherits = *0.6nozzleMK3* -infill_extrusion_width = 0.68 -solid_infill_extrusion_width = 0.68 -fill_pattern = gyroid -fill_density = 15% -travel_speed = 150 -perimeter_acceleration = 800 -infill_acceleration = 1000 -bridge_acceleration = 1000 -first_layer_acceleration = 1000 -default_acceleration = 1250 -support_material_speed = 40 -support_material_interface_speed = 100% +thick_bridges = 1 [print:*soluble_support*] overhangs = 1 @@ -399,29 +398,29 @@ support_material_threshold = 80 support_material_with_sheath = 1 wipe_tower_bridging = 6 support_material_interface_speed = 80% - -# XXXXXXXXXXXXXXXXXXXX -# XXX--- 0.05mm ---XXX -# XXXXXXXXXXXXXXXXXXXX +support_material_bottom_interface_layers = -1 +thick_bridges = 1 [print:*0.05mm*] inherits = *common* +layer_height = 0.05 bottom_solid_layers = 10 bridge_acceleration = 300 -bridge_flow_ratio = 0.7 +bridge_flow_ratio = 1.15 +bridge_speed = 15 default_acceleration = 1000 external_perimeter_speed = 20 fill_density = 20% -first_layer_acceleration = 500 +first_layer_acceleration = 800 gap_fill_speed = 20 infill_acceleration = 800 infill_speed = 30 max_print_speed = 80 small_perimeter_speed = 20 solid_infill_speed = 30 -support_material_extrusion_width = 0.3 +support_material_extrusion_width = 0.33 support_material_spacing = 1.5 -layer_height = 0.05 +support_material_contact_distance = 0.15 perimeter_acceleration = 300 perimeter_speed = 30 perimeters = 3 @@ -429,151 +428,28 @@ support_material_speed = 30 top_solid_infill_speed = 20 top_solid_layers = 15 -[print:0.05mm ULTRADETAIL] -inherits = *0.05mm* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1 -infill_extrusion_width = 0.5 - -# MK3 # -[print:0.05mm ULTRADETAIL @MK3] -inherits = *0.05mm*; *MK3* -fill_pattern = gyroid -fill_density = 15% -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material -top_infill_extrusion_width = 0.4 - -# MK2 # -[print:0.05mm ULTRADETAIL @0.25 nozzle] -inherits = *0.05mm*; *0.25nozzle* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.25 and num_extruders==1 -fill_density = 20% -infill_speed = 20 -max_print_speed = 100 -perimeter_speed = 20 -small_perimeter_speed = 15 -solid_infill_speed = 20 -support_material_speed = 20 - -# MK3 # -[print:0.05mm ULTRADETAIL @0.25 nozzle MK3] -inherits = *0.05mm*; *0.25nozzle*; *MK3* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 and num_extruders==1 -fill_pattern = grid -fill_density = 20% - -# XXXXXXXXXXXXXXXXXXXX -# XXX--- 0.07mm ---XXX -# XXXXXXXXXXXXXXXXXXXX - [print:*0.07mm*] -inherits = *common* -bottom_solid_layers = 8 -bridge_acceleration = 300 -bridge_flow_ratio = 0.7 -bridge_speed = 20 -default_acceleration = 1000 -external_perimeter_speed = 20 -fill_density = 15% -first_layer_acceleration = 500 -gap_fill_speed = 20 -infill_acceleration = 800 -infill_speed = 40 -max_print_speed = 80 -small_perimeter_speed = 20 -solid_infill_speed = 40 -support_material_extrusion_width = 0.3 -support_material_spacing = 1.5 +inherits = *0.05mm* layer_height = 0.07 -perimeter_acceleration = 300 -perimeter_speed = 30 -perimeters = 3 +bottom_solid_layers = 8 +bridge_flow_ratio = 1 +fill_density = 15% +infill_speed = 40 +solid_infill_speed = 40 support_material_speed = 40 top_solid_infill_speed = 30 top_solid_layers = 11 -# MK3 # -[print:0.07mm ULTRADETAIL @MK3] -inherits = *0.07mm*; *MK3* -fill_pattern = gyroid -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material -top_infill_extrusion_width = 0.4 - -[print:0.07mm ULTRADETAIL @0.25 nozzle MK3] -inherits = *0.07mm*; *0.25nozzle*; *MK3* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 and num_extruders==1 -infill_speed = 30 -solid_infill_speed = 30 -support_material_speed = 30 -top_solid_infill_speed = 20 -fill_pattern = grid -fill_density = 20% - -# XXXXXXXXXXXXXXXXXXXX -# XXX--- 0.10mm ---XXX -# XXXXXXXXXXXXXXXXXXXX - -# MK2 # [print:*0.10mm*] inherits = *common* bottom_solid_layers = 7 -bridge_flow_ratio = 0.7 +bridge_flow_ratio = 1 +bridge_speed = 20 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1 layer_height = 0.1 perimeter_acceleration = 800 top_solid_layers = 9 - -# MK2 # -[print:0.10mm DETAIL] -inherits = *0.10mm* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1 -external_perimeter_speed = 40 -infill_acceleration = 2000 -infill_speed = 60 -perimeter_speed = 50 -solid_infill_speed = 50 -perimeters = 3 - -# MK3 # -[print:0.10mm DETAIL @MK3] -inherits = *0.10mm*; *MK3* -bridge_speed = 30 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 -external_perimeter_speed = 25 -infill_acceleration = 1000 -infill_speed = 80 -max_print_speed = 200 -perimeter_speed = 45 -solid_infill_speed = 80 -top_infill_extrusion_width = 0.4 -top_solid_infill_speed = 40 -fill_pattern = gyroid -fill_density = 15% -perimeters = 3 - -# MK2 # -[print:0.10mm DETAIL @0.25 nozzle] -inherits = *0.10mm*; *0.25nozzle* -bridge_acceleration = 600 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.25 -external_perimeter_speed = 20 -infill_acceleration = 1000 -infill_speed = 40 -perimeter_acceleration = 600 -perimeter_speed = 25 -small_perimeter_speed = 15 -solid_infill_speed = 40 -top_solid_infill_speed = 30 - -# MK3 # -[print:0.10mm DETAIL @0.25 nozzle MK3] -inherits = *0.10mm*; *0.25nozzleMK3*; *MK3* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 -fill_pattern = grid -fill_density = 20% - -# XXXXXXXXXXXXXXXXXXXX -# XXX--- 0.15mm ---XXX -# XXXXXXXXXXXXXXXXXXXX +support_material_contact_distance = 0.17 [print:*0.15mm*] inherits = *common* @@ -587,138 +463,8 @@ perimeter_speed = 50 solid_infill_speed = 50 top_infill_extrusion_width = 0.4 top_solid_layers = 7 - -# MK2 # -[print:0.15mm 100mms Linear Advance] -inherits = *0.15mm* -bridge_flow_ratio = 0.95 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 -external_perimeter_speed = 50 -infill_speed = 100 -max_print_speed = 150 -perimeter_speed = 60 -small_perimeter_speed = 30 -solid_infill_speed = 100 -support_material_speed = 60 -top_solid_infill_speed = 70 - -# MK2 # -[print:0.15mm OPTIMAL] -inherits = *0.15mm* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 -top_infill_extrusion_width = 0.45 - -# MK2 # -[print:0.15mm OPTIMAL @0.25 nozzle] -inherits = *0.15mm*; *0.25nozzle* -bridge_acceleration = 600 -bridge_flow_ratio = 0.7 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.25 -external_perimeter_speed = 20 -infill_acceleration = 1000 -infill_speed = 40 -perimeter_acceleration = 600 -perimeter_speed = 25 -small_perimeter_speed = 15 -solid_infill_speed = 40 -top_solid_infill_speed = 30 - -# MK2 # -[print:0.15mm OPTIMAL @0.6 nozzle] -inherits = *0.15mm*; *0.6nozzle* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 - -# MK3 # -[print:0.15mm QUALITY @MK3] -inherits = *0.15mm*; *MK3* -bridge_speed = 30 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 -external_perimeter_speed = 25 -infill_acceleration = 1000 -infill_speed = 80 -max_print_speed = 200 -perimeter_speed = 45 -solid_infill_speed = 80 -top_solid_infill_speed = 40 -fill_pattern = gyroid -fill_density = 15% - -[print:0.15mm SPEED @MK3] -inherits = *0.15mm*; *MK3* -bridge_speed = 30 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 -external_perimeter_speed = 35 -infill_acceleration = 1000 -infill_speed = 200 -max_print_speed = 200 -perimeter_speed = 60 -solid_infill_speed = 200 -top_solid_infill_speed = 50 - -# MK3 MMU # -[print:0.15mm SOLUBLE FULL @MK3] -inherits = 0.15mm SPEED @MK3; *soluble_support* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 -notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder -support_material_extruder = 5 -support_material_interface_extruder = 5 -perimeter_speed = 40 -solid_infill_speed = 40 -infill_speed = 80 -top_infill_extrusion_width = 0.45 -top_solid_infill_speed = 30 -support_material_speed = 45 - -# MK3 MMU # -[print:0.15mm SOLUBLE INTERFACE @MK3] -inherits = 0.15mm SOLUBLE FULL @MK3 -notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder -support_material_extruder = 0 -support_material_interface_layers = 3 -support_material_with_sheath = 0 - -# MK2 MMU # -[print:0.15mm OPTIMAL SOLUBLE FULL] -inherits = *0.15mm*; *soluble_support* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 and num_extruders>1 -external_perimeter_speed = 25 -notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder -perimeter_speed = 40 -solid_infill_speed = 40 -top_infill_extrusion_width = 0.45 -top_solid_infill_speed = 30 - -# MK2 MMU # -[print:0.15mm OPTIMAL SOLUBLE INTERFACE] -inherits = 0.15mm OPTIMAL SOLUBLE FULL -notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder -support_material_extruder = 0 -support_material_interface_layers = 3 -support_material_with_sheath = 0 -support_material_xy_spacing = 80% - -# MK3 # -[print:0.15mm QUALITY @0.25 nozzle MK3] -inherits = *0.15mm*; *0.25nozzleMK3*; *MK3* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 -fill_pattern = grid -fill_density = 20% - -# MK3 # -[print:0.15mm DETAIL @0.6 nozzle MK3] -inherits = *0.15mm*; *0.6nozzleMK3*; *MK306* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 -external_perimeter_speed = 35 -infill_acceleration = 1000 -infill_speed = 70 -max_print_speed = 100 -perimeter_speed = 45 -solid_infill_speed = 70 -top_solid_infill_speed = 45 - -# XXXXXXXXXXXXXXXXXXXX -# XXX--- 0.20mm ---XXX -# XXXXXXXXXXXXXXXXXXXX +bridge_flow_ratio = 1 +bridge_speed = 25 [print:*0.20mm*] inherits = *common* @@ -734,114 +480,6 @@ solid_infill_speed = 50 top_infill_extrusion_width = 0.4 top_solid_layers = 5 -# MK2 # -[print:0.20mm 100mms Linear Advance] -inherits = *0.20mm* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 -external_perimeter_speed = 50 -infill_speed = 100 -max_print_speed = 150 -perimeter_speed = 60 -small_perimeter_speed = 30 -solid_infill_speed = 100 -support_material_speed = 60 -top_solid_infill_speed = 70 - -# MK3 # -[print:0.20mm QUALITY @MK3] -inherits = *0.20mm*; *MK3* -bridge_speed = 30 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 -external_perimeter_speed = 25 -infill_acceleration = 1000 -infill_speed = 80 -max_print_speed = 200 -perimeter_speed = 45 -solid_infill_speed = 80 -top_solid_infill_speed = 40 -fill_pattern = gyroid -fill_density = 15% - -[print:0.20mm SPEED @MK3] -inherits = *0.20mm*; *MK3* -bridge_speed = 30 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 -external_perimeter_speed = 35 -infill_acceleration = 1000 -infill_speed = 200 -max_print_speed = 200 -perimeter_speed = 60 -solid_infill_speed = 200 -top_solid_infill_speed = 50 - -# MK3 MMU # -[print:0.20mm SOLUBLE FULL @MK3] -inherits = 0.20mm SPEED @MK3; *soluble_support* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 -notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder -support_material_extruder = 5 -support_material_interface_extruder = 5 -perimeter_speed = 40 -solid_infill_speed = 40 -infill_speed = 80 -top_infill_extrusion_width = 0.45 -top_solid_infill_speed = 30 -support_material_speed = 45 - -# MK3 MMU # -[print:0.20mm SOLUBLE INTERFACE @MK3] -inherits = 0.20mm SOLUBLE FULL @MK3 -notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder -support_material_extruder = 0 -support_material_interface_layers = 3 -support_material_with_sheath = 0 - -# MK2 # -[print:0.20mm NORMAL] -inherits = *0.20mm* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 - -# MK2 # -[print:0.20mm NORMAL @0.6 nozzle] -inherits = *0.20mm*; *0.6nozzle* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 - -# MK2 MMU # -[print:0.20mm NORMAL SOLUBLE FULL] -inherits = *0.20mm*; *soluble_support* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 and num_extruders>1 -external_perimeter_speed = 30 -notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder -perimeter_speed = 40 -solid_infill_speed = 40 -top_solid_infill_speed = 30 - -# MK2 MMU # -[print:0.20mm NORMAL SOLUBLE INTERFACE] -inherits = 0.20mm NORMAL SOLUBLE FULL -notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder -support_material_extruder = 0 -support_material_interface_layers = 3 -support_material_with_sheath = 0 -support_material_xy_spacing = 80% - -# MK3 # -[print:0.20mm DETAIL @0.6 nozzle MK3] -inherits = *0.20mm*; *0.6nozzleMK3*; *MK306* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 -external_perimeter_speed = 35 -infill_acceleration = 1000 -infill_speed = 70 -max_print_speed = 100 -perimeter_speed = 45 -solid_infill_speed = 70 -top_solid_infill_speed = 45 - - -# XXXXXXXXXXXXXXXXXXXX -# XXX--- 0.25mm ---XXX -# XXXXXXXXXXXXXXXXXXXX - [print:*0.25mm*] inherits = *common* bottom_solid_layers = 4 @@ -852,10 +490,6 @@ layer_height = 0.25 perimeter_speed = 50 top_solid_layers = 4 -# XXXXXXXXXXXXXXXXXXXX -# XXX--- 0.30mm ---XXX -# XXXXXXXXXXXXXXXXXXXX - [print:*0.30mm*] inherits = *common* bottom_solid_layers = 4 @@ -869,65 +503,7 @@ perimeter_speed = 50 solid_infill_speed = 50 top_infill_extrusion_width = 0.4 top_solid_layers = 4 - -[print:0.30mm QUALITY @0.6 nozzle MK3] -inherits = *0.30mm*; *0.6nozzleMK3*; *MK306* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 -external_perimeter_speed = 35 -infill_acceleration = 1000 -infill_speed = 70 -max_print_speed = 100 -perimeter_speed = 45 -solid_infill_speed = 70 -top_solid_infill_speed = 45 - -[print:0.30mm SOLUBLE FULL @0.6 nozzle MK3] -inherits = 0.30mm QUALITY @0.6 nozzle MK3; *soluble_support* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 and num_extruders>1 -notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder -support_material_extruder = 5 -support_material_interface_extruder = 5 -support_material_speed = 40 -perimeter_speed = 40 -solid_infill_speed = 40 -top_infill_extrusion_width = 0.6 -support_material_extrusion_width = 0.6 -top_solid_infill_speed = 30 -support_material_xy_spacing = 80% - -[print:0.30mm SOLUBLE INTERFACE @0.6 nozzle MK3] -inherits = 0.30mm SOLUBLE FULL @0.6 nozzle MK3 -notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder -support_material_extruder = 0 -support_material_interface_layers = 3 -support_material_with_sheath = 0 - -[print:0.30mm DRAFT @MK3] -inherits = *0.30mm*; *MK3* -bottom_solid_layers = 3 -bridge_speed = 30 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 -external_perimeter_speed = 35 -infill_acceleration = 1000 -infill_speed = 85 -max_print_speed = 200 -perimeter_speed = 50 -small_perimeter_speed = 30 -solid_infill_speed = 80 -top_solid_infill_speed = 40 -support_material_speed = 45 -external_perimeter_extrusion_width = 0.6 -extrusion_width = 0.5 -first_layer_extrusion_width = 0.42 -infill_extrusion_width = 0.5 -perimeter_extrusion_width = 0.5 -solid_infill_extrusion_width = 0.5 -top_infill_extrusion_width = 0.45 -support_material_extrusion_width = 0.38 - -# XXXXXXXXXXXXXXXXXXXX -# XXX--- 0.35mm ---XXX -# XXXXXXXXXXXXXXXXXXXX +support_material_contact_distance = 0.3 [print:*0.35mm*] inherits = *common* @@ -946,65 +522,6 @@ solid_infill_speed = 60 top_solid_infill_speed = 50 top_solid_layers = 4 -# MK2 # -[print:0.35mm FAST] -inherits = *0.35mm* -bridge_flow_ratio = 0.95 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 -first_layer_extrusion_width = 0.42 -perimeter_extrusion_width = 0.43 -solid_infill_extrusion_width = 0.7 -top_infill_extrusion_width = 0.43 -support_material_extrusion_width = 0.37 - -# MK2 # -[print:0.35mm FAST @0.6 nozzle] -inherits = *0.35mm*; *0.6nozzle* -# alias = 0.35mm FAST -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 - -# MK2 MMU # -[print:0.35mm FAST sol full @0.6 nozzle] -inherits = *0.35mm*; *0.6nozzle*; *soluble_support* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_model=="MK2SMM" and nozzle_diameter[0]==0.6 and num_extruders>1 -external_perimeter_extrusion_width = 0.6 -external_perimeter_speed = 30 -notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder -perimeter_speed = 40 -support_material_speed = 40 -support_material_interface_layers = 2 -support_material_xy_spacing = 120% -top_infill_extrusion_width = 0.6 -support_material_extrusion_width = 0.6 - -# MK2 MMU # -[print:0.35mm FAST sol int @0.6 nozzle] -inherits = 0.35mm FAST sol full @0.6 nozzle -support_material_extruder = 0 -support_material_interface_layers = 3 -support_material_with_sheath = 0 -support_material_xy_spacing = 150% - -# MK3 # -[print:0.35mm SPEED @0.6 nozzle MK3] -inherits = *0.35mm*; *0.6nozzleMK3*; *MK306* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 -external_perimeter_speed = 35 -infill_acceleration = 1000 -infill_speed = 70 -max_print_speed = 100 -perimeter_speed = 45 -solid_infill_speed = 70 -top_solid_infill_speed = 45 -external_perimeter_extrusion_width = 0.68 -perimeter_extrusion_width = 0.68 -infill_extrusion_width = 0.68 -solid_infill_extrusion_width = 0.68 - -# XXXXXXXXXXXXXXXXXXXX -# XXX--- 0.40mm ---XXX -# XXXXXXXXXXXXXXXXXXXX - [print:*0.40mm*] inherits = *common* bottom_solid_layers = 3 @@ -1022,71 +539,236 @@ solid_infill_speed = 60 top_solid_infill_speed = 40 top_solid_layers = 4 -# MK3 # -[print:0.40mm DRAFT @0.6 nozzle MK3] -inherits = *0.40mm*; *0.6nozzleMK3*; *MK306* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 -external_perimeter_speed = 35 -infill_acceleration = 1000 -infill_speed = 70 +## MK2 family ## + +## MK2 - 0.4mm nozzle +[print:0.05mm ULTRADETAIL] +inherits = *0.05mm* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1 +infill_extrusion_width = 0.5 + +[print:0.10mm DETAIL] +inherits = *0.10mm* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1 +external_perimeter_speed = 40 +infill_acceleration = 2000 +infill_speed = 60 +perimeter_speed = 50 +solid_infill_speed = 50 +perimeters = 3 +bridge_acceleration = 800 + +[print:0.15mm 100mms Linear Advance] +inherits = *0.15mm* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 +external_perimeter_speed = 50 +infill_speed = 100 +max_print_speed = 150 +perimeter_speed = 60 +small_perimeter_speed = 30 +solid_infill_speed = 100 +support_material_speed = 60 +top_solid_infill_speed = 70 + +[print:0.15mm OPTIMAL] +inherits = *0.15mm* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 +top_infill_extrusion_width = 0.45 + +[print:0.20mm 100mms Linear Advance] +inherits = *0.20mm* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 +external_perimeter_speed = 50 +infill_speed = 100 +max_print_speed = 150 +perimeter_speed = 60 +small_perimeter_speed = 30 +solid_infill_speed = 100 +support_material_speed = 60 +top_solid_infill_speed = 70 + +[print:0.20mm NORMAL] +inherits = *0.20mm* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 + +[print:0.35mm FAST] +inherits = *0.35mm* +bridge_flow_ratio = 0.95 +bridge_speed = 30 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 +first_layer_extrusion_width = 0.42 +perimeter_extrusion_width = 0.43 +solid_infill_extrusion_width = 0.7 +top_infill_extrusion_width = 0.45 +support_material_extrusion_width = 0.37 +support_material_contact_distance = 0.1 +top_solid_infill_speed = 40 +thick_bridges = 1 + +## MMU1 specific +[print:0.15mm OPTIMAL SOLUBLE FULL] +inherits = *0.15mm*; *soluble_support* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 and num_extruders>1 +external_perimeter_speed = 25 +notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder +perimeter_speed = 40 +solid_infill_speed = 40 +top_infill_extrusion_width = 0.45 +top_solid_infill_speed = 30 +bridge_flow_ratio = 0.8 +bridge_speed = 30 + +[print:0.15mm OPTIMAL SOLUBLE INTERFACE] +inherits = 0.15mm OPTIMAL SOLUBLE FULL +notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder +support_material_extruder = 0 +support_material_interface_layers = 3 +support_material_with_sheath = 0 +support_material_xy_spacing = 80% + +[print:0.20mm NORMAL SOLUBLE FULL] +inherits = *0.20mm*; *soluble_support* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 and num_extruders>1 +external_perimeter_speed = 30 +notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder +perimeter_speed = 40 +solid_infill_speed = 40 +top_solid_infill_speed = 30 +bridge_flow_ratio = 0.95 +bridge_speed = 30 + +[print:0.20mm NORMAL SOLUBLE INTERFACE] +inherits = 0.20mm NORMAL SOLUBLE FULL +notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder +support_material_extruder = 0 +support_material_interface_layers = 3 +support_material_with_sheath = 0 +support_material_xy_spacing = 80% + +## MK2 - 0.25mm nozzle + +[print:0.05mm ULTRADETAIL @0.25 nozzle] +inherits = *0.05mm*; *0.25nozzle* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.25 and num_extruders==1 +fill_density = 20% +infill_speed = 20 max_print_speed = 100 -perimeter_speed = 45 -solid_infill_speed = 70 -top_solid_infill_speed = 45 -external_perimeter_extrusion_width = 0.68 -perimeter_extrusion_width = 0.68 -infill_extrusion_width = 0.68 -solid_infill_extrusion_width = 0.68 +perimeter_speed = 20 +small_perimeter_speed = 15 +solid_infill_speed = 20 +support_material_speed = 20 +support_material_contact_distance = 0.07 -# XXXXXXXXXXXXXXXXXXXXXX -# XXX----- MK2.5 ----XXX -# XXXXXXXXXXXXXXXXXXXXXX +[print:0.10mm DETAIL @0.25 nozzle] +inherits = *0.10mm*; *0.25nozzle* +bridge_acceleration = 600 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.25 +external_perimeter_speed = 20 +infill_acceleration = 1000 +infill_speed = 40 +perimeter_acceleration = 600 +perimeter_speed = 25 +small_perimeter_speed = 15 +solid_infill_speed = 40 +top_solid_infill_speed = 30 +support_material_contact_distance = 0.07 -# MK2.5 # -[print:0.15mm 100mms Linear Advance @MK2.5] -inherits = 0.15mm 100mms Linear Advance -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 -single_extruder_multi_material_priming = 0 +[print:0.15mm OPTIMAL @0.25 nozzle] +inherits = *0.15mm*; *0.25nozzle* +bridge_acceleration = 600 +bridge_flow_ratio = 0.8 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.25 +external_perimeter_speed = 20 +infill_acceleration = 1000 +infill_speed = 40 +perimeter_acceleration = 600 +perimeter_speed = 25 +small_perimeter_speed = 15 +solid_infill_speed = 40 +top_solid_infill_speed = 30 +support_material_contact_distance = 0.08 -# MK2.5 # -[print:0.15mm OPTIMAL @MK2.5] -inherits = 0.15mm OPTIMAL -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 -single_extruder_multi_material_priming = 0 +## MK2 - 0.6mm nozzle + +[print:0.15mm OPTIMAL @0.6 nozzle] +inherits = *0.15mm*; *0.6nozzle* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 + +[print:0.20mm NORMAL @0.6 nozzle] +inherits = *0.20mm*; *0.6nozzle* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 + +[print:0.35mm FAST @0.6 nozzle] +inherits = *0.35mm*; *0.6nozzle* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 + +## MMU1 specific +[print:0.35mm FAST sol full @0.6 nozzle] +inherits = *0.35mm*; *0.6nozzle*; *soluble_support* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_model=="MK2SMM" and nozzle_diameter[0]==0.6 and num_extruders>1 +external_perimeter_extrusion_width = 0.6 +external_perimeter_speed = 30 +notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder +perimeter_speed = 40 +support_material_speed = 40 +support_material_interface_layers = 2 +support_material_xy_spacing = 120% +top_infill_extrusion_width = 0.6 +support_material_extrusion_width = 0.6 + +[print:0.35mm FAST sol int @0.6 nozzle] +inherits = 0.35mm FAST sol full @0.6 nozzle +support_material_extruder = 0 +support_material_interface_layers = 3 +support_material_with_sheath = 0 +support_material_xy_spacing = 150% + +## MK2.5 -# MK2.5 MMU2 # [print:0.10mm DETAIL @MK2.5] inherits = 0.10mm DETAIL compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 single_extruder_multi_material_priming = 0 -# MK2.5 MMU2 # +[print:0.15mm 100mms Linear Advance @MK2.5] +inherits = 0.15mm 100mms Linear Advance +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 +single_extruder_multi_material_priming = 0 + +[print:0.15mm OPTIMAL @MK2.5] +inherits = 0.15mm OPTIMAL +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 +single_extruder_multi_material_priming = 0 + +[print:0.20mm 100mms Linear Advance @MK2.5] +inherits = 0.20mm 100mms Linear Advance +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 +single_extruder_multi_material_priming = 0 + +[print:0.20mm NORMAL @MK2.5] +inherits = 0.20mm NORMAL +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 +single_extruder_multi_material_priming = 0 + +[print:0.35mm FAST @MK2.5] +inherits = 0.35mm FAST +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 +single_extruder_multi_material_priming = 0 + +## MK2.5 - MMU2 specific + [print:0.15mm OPTIMAL SOLUBLE FULL @MK2.5] inherits = 0.15mm OPTIMAL SOLUBLE FULL support_material_extruder = 5 support_material_interface_extruder = 5 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 -# MK2.5 MMU2 # [print:0.15mm OPTIMAL SOLUBLE INTERFACE @MK2.5] inherits = 0.15mm OPTIMAL SOLUBLE INTERFACE support_material_extruder = 0 support_material_interface_extruder = 5 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 -# MK2.5 # -[print:0.20mm 100mms Linear Advance @MK2.5] -inherits = 0.20mm 100mms Linear Advance -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 -single_extruder_multi_material_priming = 0 - -# MK2.5 # -[print:0.20mm NORMAL @MK2.5] -inherits = 0.20mm NORMAL -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 -single_extruder_multi_material_priming = 0 - -# MK2.5 MMU2 # [print:0.20mm NORMAL SOLUBLE FULL @MK2.5] inherits = 0.20mm NORMAL SOLUBLE FULL support_material_extruder = 5 @@ -1094,7 +776,6 @@ support_material_interface_extruder = 5 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 single_extruder_multi_material_priming = 0 -# MK2.5 MMU2 # [print:0.20mm NORMAL SOLUBLE INTERFACE @MK2.5] inherits = 0.20mm NORMAL SOLUBLE INTERFACE support_material_extruder = 0 @@ -1102,14 +783,7 @@ support_material_interface_extruder = 5 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 single_extruder_multi_material_priming = 0 -# MK2.5 # -[print:0.35mm FAST @MK2.5] -inherits = 0.35mm FAST -# alias = 0.35mm FAST -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 -single_extruder_multi_material_priming = 0 - -# MK2.5 MMU2 0.6 nozzle # +# MK2.5 MMU2 0.6 nozzle [print:0.35mm SOLUBLE FULL @0.6 nozzle MK2.5] inherits = *0.35mm*; *0.6nozzle*; *soluble_support* compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and printer_model!="MK2SMM" and nozzle_diameter[0]==0.6 and num_extruders>1 @@ -1132,12 +806,296 @@ support_material_interface_layers = 3 support_material_with_sheath = 0 support_material_xy_spacing = 80% -## 0.8mm nozzle print profiles +## MK3 family ## + +## MK3 - 0.4mm nozzle + +[print:0.05mm ULTRADETAIL @MK3] +inherits = *0.05mm*; *MK3* +fill_pattern = gyroid +fill_density = 15% +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material +top_infill_extrusion_width = 0.4 + +[print:0.07mm ULTRADETAIL @MK3] +inherits = *0.07mm*; *MK3* +fill_pattern = gyroid +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material +top_infill_extrusion_width = 0.4 + +[print:0.10mm DETAIL @MK3] +inherits = *0.10mm*; *MK3* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 +external_perimeter_speed = 25 +infill_acceleration = 1000 +bridge_acceleration = 800 +infill_speed = 80 +max_print_speed = 200 +perimeter_speed = 45 +solid_infill_speed = 80 +top_infill_extrusion_width = 0.4 +top_solid_infill_speed = 40 +fill_pattern = gyroid +fill_density = 15% +perimeters = 3 + +[print:0.15mm QUALITY @MK3] +inherits = *0.15mm*; *MK3* +bridge_speed = 25 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 +external_perimeter_speed = 25 +infill_acceleration = 1000 +infill_speed = 80 +max_print_speed = 200 +perimeter_speed = 45 +solid_infill_speed = 80 +top_solid_infill_speed = 40 +fill_pattern = gyroid +fill_density = 15% + +[print:0.15mm SPEED @MK3] +inherits = *0.15mm*; *MK3* +bridge_speed = 25 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 +external_perimeter_speed = 35 +infill_acceleration = 1000 +infill_speed = 200 +max_print_speed = 200 +perimeter_speed = 60 +solid_infill_speed = 200 +top_solid_infill_speed = 50 + +[print:0.20mm QUALITY @MK3] +inherits = *0.20mm*; *MK3* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 +external_perimeter_speed = 25 +infill_acceleration = 1000 +infill_speed = 80 +max_print_speed = 200 +perimeter_speed = 45 +solid_infill_speed = 80 +top_solid_infill_speed = 40 +fill_pattern = gyroid +fill_density = 15% + +[print:0.20mm SPEED @MK3] +inherits = *0.20mm*; *MK3* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 +external_perimeter_speed = 35 +infill_acceleration = 1000 +infill_speed = 200 +max_print_speed = 200 +perimeter_speed = 60 +solid_infill_speed = 200 +top_solid_infill_speed = 50 + +[print:0.30mm DRAFT @MK3] +inherits = *0.30mm*; *MK3* +bottom_solid_layers = 3 +bridge_speed = 25 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 +external_perimeter_speed = 35 +infill_acceleration = 1000 +infill_speed = 85 +max_print_speed = 200 +perimeter_speed = 50 +small_perimeter_speed = 30 +solid_infill_speed = 80 +top_solid_infill_speed = 40 +support_material_speed = 45 +external_perimeter_extrusion_width = 0.6 +extrusion_width = 0.5 +first_layer_extrusion_width = 0.42 +infill_extrusion_width = 0.5 +perimeter_extrusion_width = 0.5 +solid_infill_extrusion_width = 0.5 +top_infill_extrusion_width = 0.45 +support_material_extrusion_width = 0.38 +support_material_contact_distance = 0.2 + +## MK3 - MMU2 specific +[print:0.15mm SOLUBLE FULL @MK3] +inherits = 0.15mm SPEED @MK3; *soluble_support* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 +notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder +support_material_extruder = 5 +support_material_interface_extruder = 5 +perimeter_speed = 40 +solid_infill_speed = 40 +infill_speed = 80 +top_infill_extrusion_width = 0.45 +top_solid_infill_speed = 30 +support_material_speed = 45 +bridge_flow_ratio = 0.8 +bridge_speed = 30 + +[print:0.15mm SOLUBLE INTERFACE @MK3] +inherits = 0.15mm SOLUBLE FULL @MK3 +notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder +support_material_extruder = 0 +support_material_interface_layers = 3 +support_material_with_sheath = 0 + +[print:0.20mm SOLUBLE FULL @MK3] +inherits = 0.20mm SPEED @MK3; *soluble_support* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 +notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder +support_material_extruder = 5 +support_material_interface_extruder = 5 +perimeter_speed = 40 +solid_infill_speed = 40 +infill_speed = 80 +top_infill_extrusion_width = 0.45 +top_solid_infill_speed = 30 +support_material_speed = 45 +bridge_flow_ratio = 0.95 +bridge_speed = 30 + +[print:0.20mm SOLUBLE INTERFACE @MK3] +inherits = 0.20mm SOLUBLE FULL @MK3 +notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder +support_material_extruder = 0 +support_material_interface_layers = 3 +support_material_with_sheath = 0 + +## MK3 - 0.25mm nozzle + +[print:0.05mm ULTRADETAIL @0.25 nozzle MK3] +inherits = *0.05mm*; *0.25nozzle*; *MK3* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 and num_extruders==1 +fill_pattern = grid +fill_density = 20% +support_material_contact_distance = 0.07 + +[print:0.07mm ULTRADETAIL @0.25 nozzle MK3] +inherits = *0.07mm*; *0.25nozzle*; *MK3* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 and num_extruders==1 +infill_speed = 30 +solid_infill_speed = 30 +support_material_speed = 30 +top_solid_infill_speed = 20 +fill_pattern = grid +fill_density = 20% +support_material_contact_distance = 0.07 + +[print:0.10mm DETAIL @0.25 nozzle MK3] +inherits = *0.10mm*; *0.25nozzleMK3*; *MK3* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 +fill_pattern = grid +fill_density = 20% +support_material_contact_distance = 0.07 + +[print:0.15mm QUALITY @0.25 nozzle MK3] +inherits = *0.15mm*; *0.25nozzleMK3*; *MK3* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 +fill_pattern = grid +fill_density = 20% +support_material_contact_distance = 0.08 + +## MK3 - 0.6mm nozzle + +[print:0.15mm DETAIL @0.6 nozzle MK3] +inherits = *0.15mm*; *0.6nozzleMK3*; *MK306* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 +external_perimeter_speed = 35 +infill_acceleration = 1000 +infill_speed = 70 +max_print_speed = 100 +perimeter_speed = 45 +solid_infill_speed = 70 +top_solid_infill_speed = 45 +support_material_contact_distance = 0.22 +bridge_flow_ratio = 1 + +[print:0.20mm DETAIL @0.6 nozzle MK3] +inherits = *0.20mm*; *0.6nozzleMK3*; *MK306* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 +external_perimeter_speed = 35 +infill_acceleration = 1000 +infill_speed = 70 +max_print_speed = 100 +perimeter_speed = 45 +solid_infill_speed = 70 +top_solid_infill_speed = 45 +support_material_contact_distance = 0.22 +bridge_flow_ratio = 1 + +[print:0.30mm QUALITY @0.6 nozzle MK3] +inherits = *0.30mm*; *0.6nozzleMK3*; *MK306* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 +external_perimeter_speed = 35 +infill_acceleration = 1000 +infill_speed = 70 +max_print_speed = 100 +perimeter_speed = 45 +solid_infill_speed = 70 +top_solid_infill_speed = 45 +support_material_contact_distance = 0.25 +bridge_flow_ratio = 1 + +[print:0.35mm SPEED @0.6 nozzle MK3] +inherits = *0.35mm*; *0.6nozzleMK3*; *MK306* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 +external_perimeter_speed = 35 +infill_acceleration = 1000 +infill_speed = 70 +max_print_speed = 100 +perimeter_speed = 45 +solid_infill_speed = 70 +top_solid_infill_speed = 45 +external_perimeter_extrusion_width = 0.68 +perimeter_extrusion_width = 0.68 +infill_extrusion_width = 0.68 +solid_infill_extrusion_width = 0.68 +support_material_contact_distance = 0.25 +bridge_flow_ratio = 0.95 + +[print:0.40mm DRAFT @0.6 nozzle MK3] +inherits = *0.40mm*; *0.6nozzleMK3*; *MK306* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 +external_perimeter_speed = 35 +infill_acceleration = 1000 +infill_speed = 70 +max_print_speed = 100 +perimeter_speed = 45 +solid_infill_speed = 70 +top_solid_infill_speed = 45 +external_perimeter_extrusion_width = 0.68 +perimeter_extrusion_width = 0.68 +infill_extrusion_width = 0.68 +solid_infill_extrusion_width = 0.68 +support_material_contact_distance = 0.25 +bridge_flow_ratio = 0.95 + +## MK3 - MMU2 specific + +[print:0.30mm SOLUBLE FULL @0.6 nozzle MK3] +inherits = 0.30mm QUALITY @0.6 nozzle MK3; *soluble_support* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 and num_extruders>1 +notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder +support_material_extruder = 5 +support_material_interface_extruder = 5 +support_material_speed = 40 +perimeter_speed = 40 +solid_infill_speed = 40 +top_infill_extrusion_width = 0.6 +support_material_extrusion_width = 0.6 +top_solid_infill_speed = 30 +support_material_xy_spacing = 80% + +[print:0.30mm SOLUBLE INTERFACE @0.6 nozzle MK3] +inherits = 0.30mm SOLUBLE FULL @0.6 nozzle MK3 +notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder +support_material_extruder = 0 +support_material_interface_layers = 3 +support_material_with_sheath = 0 + +## 0.8mm nozzle - MK2.5 and MK3 +## Only for MMU2 Single mode at the moment [print:0.30mm DETAIL @0.8 nozzle] inherits = *common*; *0.8nozzle* layer_height = 0.30 -## Only for MMU2 Single mode at the moment compatible_printers_condition = printer_model=~/(MK3|MK2.5).*/ and nozzle_diameter[0]==0.8 and num_extruders==1 perimeter_speed = 35 external_perimeter_speed = 25 @@ -1151,7 +1109,6 @@ support_material_speed = 40 [print:0.40mm QUALITY @0.8 nozzle] inherits = *common*; *0.8nozzle* layer_height = 0.4 -## Only for MMU2 Single mode at the moment compatible_printers_condition = printer_model=~/(MK3|MK2.5).*/ and nozzle_diameter[0]==0.8 and num_extruders==1 perimeter_speed = 35 external_perimeter_speed = 25 @@ -1165,7 +1122,6 @@ support_material_speed = 40 [print:0.55mm DRAFT @0.8 nozzle] inherits = *common*; *0.8nozzle* layer_height = 0.55 -## Only for MMU2 Single mode at the moment compatible_printers_condition = printer_model=~/(MK3|MK2.5).*/ and nozzle_diameter[0]==0.8 and num_extruders==1 perimeter_speed = 30 external_perimeter_speed = 25 @@ -1179,9 +1135,9 @@ top_solid_infill_speed = 30 external_perimeter_extrusion_width = 1 perimeter_extrusion_width = 1 -## MINI print profiles +## MINI ## -# 0.4mm nozzle +# MINI - 0.4mm nozzle [print:0.05mm ULTRADETAIL @MINI] inherits = *0.05mm*; *MINI* @@ -1194,6 +1150,8 @@ perimeter_extrusion_width = 0.4 external_perimeter_extrusion_width = 0.4 support_material_xy_spacing = 60% support_material_speed = 30 +support_material_extrusion_width = 0.35 +bridge_acceleration = 300 [print:0.07mm ULTRADETAIL @MINI] inherits = *0.07mm*; *MINI* @@ -1205,10 +1163,13 @@ small_perimeter_speed = 15 perimeter_extrusion_width = 0.4 external_perimeter_extrusion_width = 0.4 support_material_xy_spacing = 60% +support_material_extrusion_width = 0.35 +bridge_acceleration = 300 [print:0.10mm DETAIL @MINI] inherits = *0.10mm*; *MINI* -bridge_speed = 30 +bridge_speed = 20 +bridge_acceleration = 700 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 perimeter_speed = 40 external_perimeter_speed = 30 @@ -1219,12 +1180,10 @@ top_solid_infill_speed = 40 fill_pattern = gyroid fill_density = 15% perimeters = 3 -bridge_acceleration = 1000 support_material_xy_spacing = 60% [print:0.15mm QUALITY @MINI] inherits = *0.15mm*; *MINI* -bridge_speed = 30 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 perimeter_speed = 40 external_perimeter_speed = 30 @@ -1233,24 +1192,20 @@ solid_infill_speed = 80 top_solid_infill_speed = 40 fill_pattern = gyroid fill_density = 15% -bridge_flow_ratio = 0.85 support_material_xy_spacing = 60% [print:0.15mm SPEED @MINI] inherits = *0.15mm*; *MINI* -bridge_speed = 30 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 perimeter_speed = 50 external_perimeter_speed = 40 infill_speed = 140 solid_infill_speed = 140 top_solid_infill_speed = 40 -bridge_flow_ratio = 0.85 support_material_xy_spacing = 60% [print:0.20mm QUALITY @MINI] inherits = *0.20mm*; *MINI* -bridge_speed = 30 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 perimeter_speed = 40 external_perimeter_speed = 30 @@ -1263,7 +1218,6 @@ support_material_xy_spacing = 60% [print:0.20mm SPEED @MINI] inherits = *0.20mm*; *MINI* -bridge_speed = 30 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 perimeter_speed = 50 external_perimeter_speed = 40 @@ -1275,7 +1229,8 @@ support_material_xy_spacing = 60% [print:0.25mm DRAFT @MINI] inherits = *0.25mm*; *MINI* -bridge_speed = 30 +bridge_speed = 25 +bridge_flow_ratio = 0.95 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 external_perimeter_speed = 40 infill_speed = 110 @@ -1288,8 +1243,9 @@ infill_extrusion_width = 0.45 solid_infill_extrusion_width = 0.45 top_infill_extrusion_width = 0.4 support_material_xy_spacing = 60% +support_material_contact_distance = 0.2 -# 0.25mm nozzle +# MINI - 0.25mm nozzle [print:0.05mm ULTRADETAIL @0.25 nozzle MINI] inherits = *0.05mm*; *0.25nozzle*; *MINI* @@ -1297,6 +1253,7 @@ compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and fill_pattern = grid fill_density = 20% support_material_speed = 30 +support_material_contact_distance = 0.07 [print:0.07mm ULTRADETAIL @0.25 nozzle MINI] inherits = *0.07mm*; *0.25nozzle*; *MINI* @@ -1307,20 +1264,23 @@ support_material_speed = 30 top_solid_infill_speed = 20 fill_pattern = grid fill_density = 20% +support_material_contact_distance = 0.07 [print:0.10mm DETAIL @0.25 nozzle MINI] inherits = *0.10mm*; *0.25nozzleMINI*; *MINI* compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.25 fill_pattern = grid fill_density = 20% +support_material_contact_distance = 0.07 [print:0.15mm QUALITY @0.25 nozzle MINI] inherits = *0.15mm*; *0.25nozzleMINI*; *MINI* compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.25 fill_pattern = grid fill_density = 20% +support_material_contact_distance = 0.08 -# 0.6mm nozzle MINI +# MINI - 0.6mm nozzle [print:0.15mm DETAIL @0.6 nozzle MINI] inherits = *0.15mm*; *0.6nozzleMINI* @@ -1333,6 +1293,8 @@ solid_infill_speed = 70 top_solid_infill_speed = 45 infill_extrusion_width = 0.65 solid_infill_extrusion_width = 0.65 +support_material_contact_distance = 0.22 +bridge_flow_ratio = 1 [print:0.20mm DETAIL @0.6 nozzle MINI] inherits = *0.20mm*; *0.6nozzleMINI* @@ -1345,6 +1307,8 @@ solid_infill_speed = 70 top_solid_infill_speed = 45 infill_extrusion_width = 0.65 solid_infill_extrusion_width = 0.65 +support_material_contact_distance = 0.22 +bridge_flow_ratio = 1 [print:0.30mm QUALITY @0.6 nozzle MINI] inherits = *0.30mm*; *0.6nozzleMINI* @@ -1357,6 +1321,8 @@ solid_infill_speed = 65 top_solid_infill_speed = 45 external_perimeter_extrusion_width = 0.68 perimeter_extrusion_width = 0.68 +support_material_contact_distance = 0.25 +bridge_flow_ratio = 1 [print:0.35mm SPEED @0.6 nozzle MINI] inherits = *0.35mm*; *0.6nozzleMINI* @@ -1369,6 +1335,8 @@ solid_infill_speed = 60 top_solid_infill_speed = 45 external_perimeter_extrusion_width = 0.68 perimeter_extrusion_width = 0.68 +support_material_contact_distance = 0.25 +bridge_flow_ratio = 0.95 [print:0.40mm DRAFT @0.6 nozzle MINI] inherits = *0.40mm*; *0.6nozzleMINI* @@ -1383,8 +1351,10 @@ external_perimeter_extrusion_width = 0.68 perimeter_extrusion_width = 0.68 infill_extrusion_width = 0.68 solid_infill_extrusion_width = 0.68 +support_material_contact_distance = 0.25 +bridge_flow_ratio = 0.95 -# 0.8mm nozzle MINI +# MINI - 0.8mm nozzle [print:0.30mm DETAIL @0.8 nozzle MINI] inherits = 0.30mm DETAIL @0.8 nozzle @@ -1490,7 +1460,7 @@ max_fan_speed = 50 min_fan_speed = 30 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" temperature = 240 -filament_retract_length = 1.4 +filament_retract_length = 1 filament_retract_lift = 0.2 compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) @@ -1500,14 +1470,14 @@ compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_model!="MK2S filament_max_volumetric_speed = 15 [filament:*PETMMU1*] -inherits = *PET* +; inherits = *PET* filament_retract_length = nil filament_retract_speed = nil filament_retract_lift = 0.2 compatible_printers_condition = printer_model=="MK2SMM" [filament:*PETMINI*] -inherits = *PET* +; inherits = *PET* filament_retract_length = nil filament_retract_speed = 40 filament_deretract_speed = 25 @@ -1518,7 +1488,7 @@ compatible_printers_condition = printer_model=="MINI" start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.6}0.12{elsif nozzle_diameter[0]==0.8}0.06{else}0.2{endif} ; Filament gcode" [filament:*PETMINI06*] -inherits = *PET* +; inherits = *PET* filament_retract_length = nil filament_retract_speed = 40 filament_deretract_speed = 25 @@ -1529,7 +1499,7 @@ start_filament_gcode = "M900 K0.12 ; Filament gcode" filament_max_volumetric_speed = 13 [filament:*ABSMINI*] -inherits = *ABS* +; inherits = *ABS* bed_temperature = 100 filament_retract_length = 2.7 filament_retract_speed = nil @@ -1861,8 +1831,8 @@ min_fan_speed = 20 max_fan_speed = 20 min_print_speed = 15 slowdown_below_layer_time = 15 -first_layer_temperature = 265 -temperature = 265 +first_layer_temperature = 260 +temperature = 260 filament_type = ASA [filament:Prusament ASA] @@ -2015,9 +1985,10 @@ filament_cost = 27.82 filament_density = 1.04 filament_spool_weight = 245 -[filament:Plasty Mladec ABS] +[filament:Filament PM ABS] inherits = *ABSC* -filament_vendor = Plasty Mladec +renamed_from = "Plasty Mladec ABS" +filament_vendor = Filament PM filament_cost = 27.82 filament_density = 1.08 filament_spool_weight = 230 @@ -2038,9 +2009,21 @@ filament_cost = 27.82 filament_density = 1.27 compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Plasty Mladec PETG] +[filament:Extrudr PETG] inherits = *PET* -filament_vendor = Plasty Mladec +filament_vendor = Extrudr +filament_cost = 35.45 +filament_density = 1.29 +temperature = 220 +bed_temperature = 70 +first_layer_temperature = 220 +first_layer_bed_temperature = 70 +slowdown_below_layer_time = 20 + +[filament:Filament PM PETG] +inherits = *PET* +renamed_from = "Plasty Mladec PETG" +filament_vendor = Filament PM filament_cost = 27.82 filament_density = 1.27 filament_spool_weight = 230 @@ -2301,9 +2284,10 @@ filament_vendor = Made for Prusa filament_cost = 27.82 filament_spool_weight = 230 -[filament:Plasty Mladec ABS @MMU2] +[filament:Filament PM ABS @MMU2] inherits = *ABS MMU2* -filament_vendor = Plasty Mladec +renamed_from = "Plasty Mladec ABS @MMU2" +filament_vendor = Filament PM filament_density = 1.08 filament_cost = 27.82 filament_spool_weight = 230 @@ -2402,9 +2386,10 @@ filament_density = 1.27 filament_spool_weight = 201 filament_type = PETG -[filament:Plasty Mladec PETG @0.6 nozzle] +[filament:Filament PM PETG @0.6 nozzle] inherits = *PET06* -filament_vendor = Plasty Mladec +renamed_from = "Plasty Mladec PETG @0.6 nozzle" +filament_vendor = Filament PM first_layer_temperature = 230 temperature = 240 filament_cost = 27.82 @@ -2458,7 +2443,7 @@ filament_unload_time = 12 filament_unloading_speed = 20 filament_unloading_speed_start = 120 filament_loading_speed_start = 19 -filament_retract_length = 1.4 +filament_retract_length = 1 filament_retract_lift = 0.2 [filament:*PET MMU2 06*] @@ -2471,9 +2456,10 @@ inherits = *PET MMU2* renamed_from = "Generic PET MMU2"; "Generic PETG MMU2" filament_vendor = Generic -[filament:Plasty Mladec PETG @MMU2] +[filament:Filament PM PETG @MMU2] inherits = *PET MMU2* -filament_vendor = Plasty Mladec +renamed_from = "Plasty Mladec PETG @MMU2" +filament_vendor = Filament PM filament_spool_weight = 230 [filament:Prusa PETG @MMU2] @@ -2510,10 +2496,11 @@ filament_cost = 36.29 filament_density = 1.27 filament_spool_weight = 201 -[filament:Plasty Mladec PETG @MMU2 0.6 nozzle] +[filament:Filament PM PETG @MMU2 0.6 nozzle] inherits = *PET MMU2 06* +renamed_from = "Plasty Mladec PETG @MMU2 0.6 nozzle" filament_type = PETG -filament_vendor = Plasty Mladec +filament_vendor = Filament PM filament_spool_weight = 230 [filament:Prusa PLA] @@ -2530,9 +2517,10 @@ filament_vendor = Fiberlogy filament_cost = 25.4 filament_density = 1.24 -[filament:Plasty Mladec PLA] +[filament:Filament PM PLA] inherits = *PLA* -filament_vendor = Plasty Mladec +renamed_from = "Plasty Mladec PLA" +filament_vendor = Filament PM filament_cost = 27.82 filament_density = 1.24 filament_spool_weight = 230 @@ -2576,6 +2564,26 @@ filament_vendor = EUMAKERS filament_cost = 25.4 filament_density = 1.24 +[filament:Extrudr PLA NX1] +inherits = *PLA* +filament_vendor = Extrudr +filament_cost = 22.76 +filament_density = 1.24 +temperature = 205 +bed_temperature = 60 +first_layer_temperature = 205 +first_layer_bed_temperature = 60 +full_fan_speed_layer = 3 +max_fan_speed = 90 +min_fan_speed = 30 +slowdown_below_layer_time = 20 + +[filament:Extrudr PLA NX2] +inherits = Extrudr PLA NX1 +filament_vendor = Extrudr +filament_cost = 23.63 +filament_density = 1.3 + [filament:Floreon3D PLA] inherits = *PLA* filament_vendor = Floreon3D @@ -2930,204 +2938,75 @@ temperature = 220 ## Filaments MMU1 [filament:ColorFabb HT @MMU1] -inherits = *PETMMU1* -filament_vendor = ColorFabb -bed_temperature = 110 -bridge_fan_speed = 30 -cooling = 1 -disable_fan_first_layers = 3 -fan_always_on = 0 -fan_below_layer_time = 10 -filament_cost = 58.66 -filament_density = 1.18 -filament_spool_weight = 236 -first_layer_bed_temperature = 105 -first_layer_temperature = 270 -max_fan_speed = 20 -min_fan_speed = 10 +inherits = ColorFabb HT; *PETMMU1* start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}45{endif}; Filament gcode" -temperature = 270 [filament:ColorFabb XT @MMU1] -inherits = *PETMMU1* -filament_vendor = ColorFabb -filament_type = PETG -filament_cost = 62.90 -filament_density = 1.27 -filament_spool_weight = 236 -first_layer_bed_temperature = 90 -first_layer_temperature = 260 -temperature = 270 +inherits = ColorFabb XT; *PETMMU1* [filament:ColorFabb XT-CF20 @MMU1] -inherits = *PETMMU1* -filament_vendor = ColorFabb -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MK2SMM" -extrusion_multiplier = 1.05 -filament_cost = 80.65 -filament_density = 1.35 -filament_spool_weight = 236 -filament_colour = #804040 -filament_max_volumetric_speed = 2 -first_layer_bed_temperature = 90 -first_layer_temperature = 260 +inherits = ColorFabb XT-CF20; *PETMMU1* start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}30{endif}; Filament gcode" -temperature = 260 [filament:ColorFabb nGen @MMU1] -inherits = *PETMMU1* -filament_vendor = ColorFabb -filament_cost = 21.2 -filament_density = 1.2 -filament_spool_weight = 236 -bridge_fan_speed = 40 -fan_always_on = 0 -fan_below_layer_time = 10 -filament_type = NGEN -first_layer_temperature = 240 -max_fan_speed = 35 -min_fan_speed = 20 +inherits = ColorFabb nGen; *PETMMU1* [filament:E3D Edge @MMU1] -inherits = *PETMMU1* -filament_vendor = E3D -filament_cost = 56.9 -filament_density = 1.26 -filament_type = EDGE +inherits = E3D Edge; *PETMMU1* [filament:Fillamentum CPE @MMU1] -inherits = *PETMMU1* -filament_vendor = Fillamentum -filament_cost = 56.45 -filament_density = 1.25 -filament_spool_weight = 230 -filament_type = CPE -first_layer_bed_temperature = 90 -first_layer_temperature = 275 -max_fan_speed = 50 -min_fan_speed = 50 -disable_fan_first_layers = 3 -full_fan_speed_layer = 5 -temperature = 275 +inherits = Fillamentum CPE; *PETMMU1* [filament:Generic PETG @MMU1] -inherits = *PETMMU1* +inherits = Generic PETG; *PETMMU1* renamed_from = "Generic PET MMU1"; "Generic PETG MMU1" -filament_vendor = Generic -filament_cost = 27.82 -filament_density = 1.27 [filament:Devil Design PETG @MMU1] -inherits = *PETMMU1* -filament_vendor = Devil Design -filament_cost = 20.99 -filament_density = 1.23 -filament_spool_weight = 250 -first_layer_temperature = 230 -first_layer_bed_temperature = 85 -temperature = 230 -bed_temperature = 90 +inherits = Devil Design PETG; *PETMMU1* -[filament:Plasty Mladec PETG @MMU1] -inherits = *PETMMU1* -filament_vendor = Plasty Mladec -filament_cost = 27.82 -filament_density = 1.27 -filament_spool_weight = 230 +[filament:Filament PM PETG @MMU1] +inherits = Filament PM PETG; *PETMMU1* +renamed_from = "Plasty Mladec PETG @MMU1" [filament:Verbatim PETG @MMU1] -inherits = *PETMMU1* -filament_vendor = Verbatim -filament_cost = 27.90 -filament_density = 1.27 -filament_spool_weight = 235 +inherits = Verbatim PETG; *PETMMU1* [filament:Fiberlogy PETG @MMU1] -inherits = *PETMMU1* -filament_vendor = Fiberlogy -filament_cost = 21.50 -filament_density = 1.27 +inherits = Fiberlogy PETG; *PETMMU1* [filament:Prusa PETG @MMU1] -inherits = *PETMMU1* +inherits = Prusa PETG; *PETMMU1* renamed_from = "Prusa PET MMU1"; "Prusa PETG MMU1" -filament_vendor = Made for Prusa -filament_cost = 27.82 -filament_density = 1.27 -filament_spool_weight = 230 [filament:Prusament PETG @MMU1] -inherits = *PETMMU1* -filament_vendor = Prusa Polymers -first_layer_temperature = 240 -temperature = 250 -filament_cost = 36.29 -filament_density = 1.27 -filament_spool_weight = 201 -filament_type = PETG +inherits = Prusament PETG; *PETMMU1* + +[filament:Extrudr PETG @MMU1] +inherits = Extrudr PETG; *PETMMU1* +filament_vendor = Extrudr [filament:Taulman T-Glase @MMU1] -inherits = *PETMMU1* -filament_vendor = Taulman -filament_cost = 40 -filament_density = 1.27 -bridge_fan_speed = 40 -cooling = 0 -fan_always_on = 0 -first_layer_bed_temperature = 90 -first_layer_temperature = 240 -max_fan_speed = 5 -min_fan_speed = 0 +inherits = Taulman T-Glase; *PETMMU1* start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}30{endif}; Filament gcode" [filament:Fiberthree F3 PA Pure Pro @MMU1] -inherits = *common* -filament_vendor = Fiberthree -filament_cost = 200.84 -filament_density = 1.2 -bed_temperature = 70 -first_layer_bed_temperature = 75 -first_layer_temperature = 270 -temperature = 270 -bridge_fan_speed = 30 -cooling = 1 -disable_fan_first_layers = 3 -fan_always_on = 1 -fan_below_layer_time = 20 -min_print_speed = 15 -slowdown_below_layer_time = 10 -filament_colour = #DEE0E6 +inherits = Fiberthree F3 PA Pure Pro filament_max_volumetric_speed = 4 -filament_soluble = 0 -filament_type = NYLON -max_fan_speed = 20 -min_fan_speed = 20 -start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.05{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0" +filament_retract_length = nil +filament_retract_speed = nil +filament_retract_lift = nil +filament_retract_before_travel = nil +filament_wipe = nil compatible_printers_condition = printer_model=="MK2SMM" [filament:Fiberthree F3 PA-CF Pro @MMU1] -inherits = *common* -filament_vendor = Fiberthree -filament_cost = 208.1 -filament_density = 1.25 -bed_temperature = 70 -first_layer_bed_temperature = 75 -first_layer_temperature = 275 -temperature = 275 -bridge_fan_speed = 30 -cooling = 1 -disable_fan_first_layers = 3 -fan_always_on = 0 -fan_below_layer_time = 20 -min_print_speed = 15 -slowdown_below_layer_time = 10 -filament_colour = #DEE0E6 +inherits = Fiberthree F3 PA-CF Pro filament_max_volumetric_speed = 4 -filament_soluble = 0 -filament_type = NYLON -max_fan_speed = 0 -min_fan_speed = 0 -start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.05{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0" +filament_retract_length = nil +filament_retract_speed = nil +filament_retract_lift = nil +filament_retract_before_travel = nil +filament_wipe = nil compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MK2SMM" [filament:Fiberthree F3 PA-GF Pro @MMU1] @@ -3166,108 +3045,47 @@ compatible_printers_condition = printer_model=="MK2SMM" [filament:Generic PETG @MINI] inherits = Generic PETG; *PETMINI* renamed_from = "Generic PET MINI"; "Generic PETG MINI" -filament_vendor = Generic -filament_cost = 27.82 -filament_density = 1.27 compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Devil Design PETG @MINI] -inherits = Generic PETG; *PETMINI* -filament_vendor = Devil Design -filament_cost = 20.99 -filament_density = 1.23 -filament_spool_weight = 250 -first_layer_temperature = 230 -first_layer_bed_temperature = 85 -temperature = 230 -bed_temperature = 90 +inherits = Devil Design PETG; *PETMINI* compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.6 -[filament:Plasty Mladec PETG @MINI] -inherits = Generic PETG; *PETMINI* -filament_vendor = Plasty Mladec -filament_cost = 27.82 -filament_density = 1.27 -filament_spool_weight = 230 +[filament:Filament PM PETG @MINI] +inherits = Filament PM PETG; *PETMINI* +renamed_from = "Plasty Mladec PETG @MINI" compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.6 [filament:Verbatim PETG @MINI] -inherits = Generic PETG; *PETMINI* -filament_vendor = Verbatim -filament_cost = 27.90 -filament_density = 1.27 -filament_spool_weight = 235 +inherits = Verbatim PETG; *PETMINI* compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.6 [filament:Fiberlogy PETG @MINI] -inherits = Generic PETG; *PETMINI* -filament_vendor = Fiberlogy -filament_cost = 21.50 -filament_density = 1.27 +inherits = Fiberlogy PETG; *PETMINI* compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.6 [filament:Generic ABS @MINI] inherits = Generic ABS; *ABSMINI* -filament_vendor = Generic -filament_cost = 27.82 -filament_density = 1.08 -fan_always_on = 0 -cooling = 1 -min_fan_speed = 15 -max_fan_speed = 15 -disable_fan_first_layers = 4 -fan_below_layer_time = 30 -bridge_fan_speed = 25 compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.8 [filament:Fiberthree F3 PA Pure Pro @MINI] -inherits = *common* -filament_vendor = Fiberthree -filament_cost = 200.84 -filament_density = 1.2 -bed_temperature = 70 -first_layer_bed_temperature = 75 -first_layer_temperature = 270 -temperature = 270 -bridge_fan_speed = 30 -cooling = 1 -disable_fan_first_layers = 3 -fan_always_on = 1 -fan_below_layer_time = 20 -min_print_speed = 15 -slowdown_below_layer_time = 10 -filament_colour = #DEE0E6 +inherits = Fiberthree F3 PA Pure Pro filament_max_volumetric_speed = 4 -filament_soluble = 0 -filament_type = NYLON -max_fan_speed = 20 -min_fan_speed = 20 -start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.05{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0" +filament_retract_length = nil +filament_retract_speed = nil +filament_retract_lift = nil +filament_retract_before_travel = nil +filament_wipe = nil compatible_printers_condition = printer_model=="MINI" [filament:Fiberthree F3 PA-CF Pro @MINI] -inherits = *common* -filament_vendor = Fiberthree -filament_cost = 208.1 -filament_density = 1.25 -bed_temperature = 70 -first_layer_bed_temperature = 75 -first_layer_temperature = 275 -temperature = 275 -bridge_fan_speed = 30 -cooling = 1 -disable_fan_first_layers = 3 -fan_always_on = 0 -fan_below_layer_time = 20 -min_print_speed = 15 -slowdown_below_layer_time = 10 -filament_colour = #DEE0E6 +inherits = Fiberthree F3 PA-CF Pro filament_max_volumetric_speed = 4 -filament_soluble = 0 -filament_type = NYLON -max_fan_speed = 0 -min_fan_speed = 0 -start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.05{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0" +filament_retract_length = nil +filament_retract_speed = nil +filament_retract_lift = nil +filament_retract_before_travel = nil +filament_wipe = nil compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI" [filament:Fiberthree F3 PA-GF Pro @MINI] @@ -3280,14 +3098,8 @@ max_fan_speed = 15 min_fan_speed = 15 [filament:Kimya ABS Carbon @MINI] -inherits = *ABSMINI* -filament_vendor = Kimya -filament_cost = 140.4 -filament_density = 1.032 -filament_colour = #804040 +inherits = Kimya ABS Carbon; *ABSMINI* filament_max_volumetric_speed = 6 -first_layer_temperature = 260 -temperature = 260 compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI" [filament:Kimya ABS Kevlar @MINI] @@ -3296,146 +3108,57 @@ filament_vendor = Kimya filament_density = 1.037 [filament:Esun ABS @MINI] -inherits = Generic ABS; *ABSMINI* -filament_vendor = Esun -filament_cost = 27.82 -filament_density = 1.01 -filament_spool_weight = 265 -fan_always_on = 0 -cooling = 1 -min_fan_speed = 15 -max_fan_speed = 15 -disable_fan_first_layers = 4 -fan_below_layer_time = 30 -bridge_fan_speed = 25 +inherits = Esun ABS; *ABSMINI* [filament:Hatchbox ABS @MINI] -inherits = Generic ABS; *ABSMINI* -filament_vendor = Hatchbox -filament_cost = 27.82 -filament_density = 1.08 -filament_spool_weight = 245 -fan_always_on = 0 -cooling = 1 -min_fan_speed = 15 -max_fan_speed = 15 -disable_fan_first_layers = 4 -fan_below_layer_time = 30 -bridge_fan_speed = 25 +inherits = Hatchbox ABS; *ABSMINI* -[filament:Plasty Mladec ABS @MINI] -inherits = Generic ABS; *ABSMINI* -filament_vendor = Plasty Mladec -filament_cost = 27.82 -filament_density = 1.08 -filament_spool_weight = 230 -fan_always_on = 0 -cooling = 1 -min_fan_speed = 15 -max_fan_speed = 15 -disable_fan_first_layers = 4 -fan_below_layer_time = 30 -bridge_fan_speed = 25 +[filament:Filament PM ABS @MINI] +inherits = Filament PM ABS; *ABSMINI* +renamed_from = "Plasty Mladec ABS @MINI" [filament:Verbatim ABS @MINI] -inherits = Generic ABS; *ABSMINI* -filament_vendor = Verbatim -filament_cost = 25.87 -filament_density = 1.05 -filament_spool_weight = 235 -fan_always_on = 0 -cooling = 1 -min_fan_speed = 15 -max_fan_speed = 15 -disable_fan_first_layers = 4 -fan_below_layer_time = 30 -bridge_fan_speed = 25 +inherits = Verbatim ABS; *ABSMINI* [filament:Prusament PETG @MINI] inherits = Prusament PETG; *PETMINI* -filament_vendor = Prusa Polymers -first_layer_temperature = 240 -temperature = 250 -filament_density = 1.27 -filament_spool_weight = 201 -filament_cost = 36.29 compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +[filament:Extrudr PETG @MINI] +inherits = Extrudr PETG; *PETMINI* +filament_vendor = Extrudr + [filament:Kimya PETG Carbon @MINI] -inherits = *PETMINI* -filament_vendor = Kimya -extrusion_multiplier = 1.05 -filament_cost = 150.02 -filament_density = 1.317 -filament_colour = #804040 +inherits = Kimya PETG Carbon; *PETMINI* filament_max_volumetric_speed = 6 -first_layer_bed_temperature = 85 -first_layer_temperature = 240 -temperature = 240 filament_retract_length = nil filament_retract_lift = 0.3 compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI" [filament:Prusament PETG @0.6 nozzle MINI] inherits = Prusament PETG; *PETMINI06* -first_layer_temperature = 240 -temperature = 250 -filament_density = 1.27 -filament_spool_weight = 201 -filament_cost = 36.29 [filament:Generic PETG @0.6 nozzle MINI] inherits = Generic PETG; *PETMINI06* renamed_from = "Generic PET 0.6 nozzle MINI"; "Generic PETG 0.6 nozzle MINI" -filament_cost = 27.82 -filament_density = 1.27 [filament:Devil Design PETG @0.6 nozzle MINI] -inherits = Generic PETG; *PETMINI06* -filament_vendor = Devil Design -first_layer_temperature = 230 -first_layer_bed_temperature = 85 -temperature = 230 -bed_temperature = 90 -filament_cost = 20.99 -filament_density = 1.23 -filament_spool_weight = 250 +inherits = Devil Design PETG; *PETMINI06* -[filament:Plasty Mladec PETG @0.6 nozzle MINI] -inherits = Generic PETG; *PETMINI06* -filament_vendor = Plasty Mladec -filament_cost = 27.82 -filament_density = 1.27 -filament_spool_weight = 230 +[filament:Filament PM PETG @0.6 nozzle MINI] +inherits = Filament PM PETG; *PETMINI06* +renamed_from = "Plasty Mladec PETG @0.6 nozzle MINI" [filament:Verbatim PETG @0.6 nozzle MINI] -inherits = Generic PETG; *PETMINI06* -filament_vendor = Verbatim -filament_spool_weight = 235 +inherits = Verbatim PETG; *PETMINI06* [filament:Fiberlogy PETG @0.6 nozzle MINI] -inherits = Generic PETG; *PETMINI06* -filament_vendor = Fiberlogy +inherits = Fiberlogy PETG; *PETMINI06* [filament:Prusament ASA @MINI] inherits = Prusament ASA; *ABSMINI* -first_layer_temperature = 260 first_layer_bed_temperature = 100 -temperature = 260 bed_temperature = 100 -fan_always_on = 1 -cooling = 1 -min_fan_speed = 20 -max_fan_speed = 20 -bridge_fan_speed = 30 -min_print_speed = 15 -slowdown_below_layer_time = 15 -disable_fan_first_layers = 4 -filament_type = ASA -filament_colour = #FFF2EC -filament_cost = 42.69 -filament_density = 1.07 -filament_spool_weight = 201 compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.8 [filament:Fillamentum Flexfill 98A @MINI] @@ -3541,89 +3264,43 @@ inherits = Fillamentum CPE; *PETMINI* first_layer_temperature = 265 first_layer_bed_temperature = 90 temperature = 265 -filament_type = CPE -filament_cost = 56.45 -filament_density = 1.25 -filament_spool_weight = 230 disable_fan_first_layers = 3 full_fan_speed_layer = 5 [filament:ColorFabb nGen @MINI] inherits = ColorFabb nGen; *PETMINI* -filament_cost = 52.46 -filament_density = 1.2 -filament_spool_weight = 236 [filament:E3D PC-ABS @MINI] inherits = E3D PC-ABS; *ABSMINI* -filament_density = 1.05 -filament_cost = 28.80 +filament_retract_length = nil +filament_retract_before_travel = nil +filament_wipe = nil [filament:Fillamentum ABS @MINI] inherits = Fillamentum ABS; *ABSMINI* -filament_cost = 32.4 -filament_density = 1.04 -filament_spool_weight = 230 -fan_always_on = 0 -cooling = 1 -min_fan_speed = 15 -max_fan_speed = 15 -disable_fan_first_layers = 4 -fan_below_layer_time = 30 -bridge_fan_speed = 25 [filament:Fillamentum ASA @MINI] inherits = Fillamentum ASA; *ABSMINI* -first_layer_temperature = 255 first_layer_bed_temperature = 100 -temperature = 255 bed_temperature = 100 -fan_always_on = 1 -cooling = 1 -min_fan_speed = 20 -max_fan_speed = 20 -min_print_speed = 15 -slowdown_below_layer_time = 15 -disable_fan_first_layers = 4 -filament_type = ASA -filament_colour = #FFF2EC -filament_cost = 38.7 -filament_density = 1.07 -filament_spool_weight = 230 [filament:Polymaker PC-Max @MINI] inherits = Polymaker PC-Max; *ABSMINI* -filament_type = PC filament_max_volumetric_speed = 7 bed_temperature = 100 -filament_colour = #FFF2EC first_layer_bed_temperature = 100 first_layer_temperature = 270 temperature = 270 -bridge_fan_speed = 0 -filament_cost = 77.3 -filament_density = 1.20 +filament_retract_length = nil +filament_retract_before_travel = nil +filament_wipe = nil [filament:Prusament PC Blend @MINI] -inherits = *ABSMINI* -filament_vendor = Prusa Polymers -filament_cost = 60.49 -filament_density = 1.22 -filament_spool_weight = 201 -fan_always_on = 0 +inherits = Prusament PC Blend; *ABSMINI* first_layer_temperature = 275 first_layer_bed_temperature = 100 temperature = 275 bed_temperature = 100 -cooling = 1 -min_fan_speed = 20 -max_fan_speed = 20 -bridge_fan_speed = 30 -min_print_speed = 15 -disable_fan_first_layers = 4 -fan_below_layer_time = 30 -filament_type = PC -filament_colour = #DEE0E6 filament_max_volumetric_speed = 7 filament_retract_length = nil filament_retract_speed = nil @@ -3634,117 +3311,43 @@ filament_wipe = nil compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.8 [filament:Prusa ABS @MINI] -inherits = *ABSMINI* -filament_vendor = Made for Prusa -filament_cost = 27.82 -filament_density = 1.08 -filament_spool_weight = 230 -fan_always_on = 0 -cooling = 1 -min_fan_speed = 15 -max_fan_speed = 15 -disable_fan_first_layers = 4 -fan_below_layer_time = 30 -bridge_fan_speed = 25 +inherits = Prusa ABS; *ABSMINI* compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.8 [filament:Generic HIPS @MINI] -inherits = *ABSMINI* -filament_vendor = Generic -filament_cost = 27.3 -filament_density = 1.04 -bridge_fan_speed = 50 -cooling = 1 -extrusion_multiplier = 1 -fan_always_on = 1 -fan_below_layer_time = 10 -filament_colour = #FFFFD7 -filament_soluble = 1 -filament_type = HIPS -first_layer_temperature = 230 -max_fan_speed = 20 -min_fan_speed = 20 -temperature = 230 +inherits = Generic HIPS; *ABSMINI* [filament:ColorFabb HT @MINI] -inherits = *PETMINI* -filament_vendor = ColorFabb +inherits = ColorFabb HT; *PETMINI* bed_temperature = 100 -bridge_fan_speed = 30 -cooling = 1 -disable_fan_first_layers = 3 -fan_always_on = 0 -fan_below_layer_time = 10 -filament_cost = 58.66 -filament_density = 1.18 -filament_spool_weight = 236 first_layer_bed_temperature = 100 -first_layer_temperature = 270 -max_fan_speed = 20 -min_fan_speed = 10 -temperature = 270 +min_fan_speed = 15 [filament:ColorFabb XT @MINI] -inherits = *PETMINI* -filament_vendor = ColorFabb -filament_type = PETG -filament_cost = 62.90 -filament_density = 1.27 -filament_spool_weight = 236 +inherits = ColorFabb XT; *PETMINI* first_layer_bed_temperature = 90 -first_layer_temperature = 260 -temperature = 270 [filament:ColorFabb XT-CF20 @MINI] -inherits = *PETMINI* -filament_vendor = ColorFabb +inherits = ColorFabb XT-CF20; *PETMINI* compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI" -extrusion_multiplier = 1.05 -filament_cost = 80.65 -filament_density = 1.35 -filament_spool_weight = 236 -filament_colour = #804040 -filament_max_volumetric_speed = 2 first_layer_bed_temperature = 90 first_layer_temperature = 260 temperature = 260 [filament:Taulman T-Glase @MINI] -inherits = *PETMINI* -filament_vendor = Taulman -filament_cost = 40 -filament_density = 1.27 -bridge_fan_speed = 40 -cooling = 0 -fan_always_on = 0 -first_layer_bed_temperature = 90 -first_layer_temperature = 240 -max_fan_speed = 5 -min_fan_speed = 0 +inherits = Taulman T-Glase; *PETMINI* [filament:E3D Edge @MINI] -inherits = *PETMINI* -filament_vendor = E3D -filament_cost = 56.9 -filament_density = 1.26 -filament_type = EDGE +inherits = E3D Edge; *PETMINI* [filament:Prusa PETG @MINI] -inherits = *PETMINI* +inherits = Prusa PETG; *PETMINI* renamed_from = "Prusa PET MINI"; "Prusa PETG MINI" -filament_vendor = Made for Prusa -filament_cost = 27.82 -filament_density = 1.27 -filament_spool_weight = 230 compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusa PETG @0.6 nozzle MINI] -inherits = *PETMINI06* +inherits = Prusa PETG; *PETMINI06* renamed_from = "Prusa PET 0.6 nozzle MINI"; "Prusa PETG 0.6 nozzle MINI" -filament_vendor = Made for Prusa -filament_cost = 27.82 -filament_density = 1.27 -filament_spool_weight = 230 ## Filaments 0.8 nozzle @@ -5321,7 +4924,7 @@ retract_speed = 35 serial_port = serial_speed = 250000 single_extruder_multi_material = 0 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.2.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting] ; MK2 firmware only supports the old M204 format\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.2.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting] ; MK2 firmware only supports the old M204 format\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-2 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 toolchange_gcode = use_firmware_retraction = 0 use_relative_e_distances = 1 @@ -5356,19 +4959,19 @@ printer_model = MK2SMM [printer:*mm-single*] inherits = *multimaterial* -end_gcode = G1 E-4 F2100.00000\nG91\nG1 Z1 F7200.000\nG90\nG1 X245 Y1\nG1 X240 E4\nG1 F4000\nG1 X190 E2.7 \nG1 F4600\nG1 X110 E2.8\nG1 F5200\nG1 X40 E3 \nG1 E-15.0000 F5000\nG1 E-50.0000 F5400\nG1 E-15.0000 F3000\nG1 E-12.0000 F2000\nG1 F1600\nG1 X0 Y1 E3.0000\nG1 X50 Y1 E-5.0000\nG1 F2000\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-3.0000\nG4 S0\nM107 ; turn off fan\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nG28 X0 ; home X axis\nM900 K0 ; reset LA\nM84 ; disable motors\n\n +end_gcode = G1 E-4 F2100\nG91\nG1 Z1 F7200\nG90\nG1 X245 Y1\nG1 X240 E4\nG1 F4000\nG1 X190 E2.7\nG1 F4600\nG1 X110 E2.8\nG1 F5200\nG1 X40 E3\nG1 E-15 F5000\nG1 E-50 F5400\nG1 E-15 F3000\nG1 E-12 F2000\nG1 F1600\nG1 X0 Y1 E3\nG1 X50 Y1 E-5\nG1 F2000\nG1 X0 Y1 E5\nG1 X50 Y1 E-5\nG1 F2400\nG1 X0 Y1 E5\nG1 X50 Y1 E-5\nG1 F2400\nG1 X0 Y1 E5\nG1 X50 Y1 E-3\nG4 S0\nM107 ; turn off fan\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nG28 X0 ; home X axis\nM900 K0 ; reset LA\nM84 ; disable motors\n\n printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2\nPRINTER_HAS_BOWDEN -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.2.3 ; tell printer latest fw version\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting] ; MK2 firmware only supports the old M204 format\n; Start G-Code sequence START\nT?\nM104 S[first_layer_temperature]\nM140 S[first_layer_bed_temperature]\nM109 S[first_layer_temperature]\nM190 S[first_layer_bed_temperature]\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100\nM92 E140\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.2.3 ; tell printer latest fw version\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting] ; MK2 firmware only supports the old M204 format\n; Start G-Code sequence START\nT?\nM104 S[first_layer_temperature]\nM140 S[first_layer_bed_temperature]\nM109 S[first_layer_temperature]\nM190 S[first_layer_bed_temperature]\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0\nM203 E100\nM92 E140\nG1 Z0.25 F7200\nG1 X50 E80 F1000\nG1 X160 E20 F1000\nG1 Z0.2 F7200\nG1 X220 E13 F1000\nG1 X240 E0 F1000\nG92 E0 default_print_profile = 0.15mm OPTIMAL [printer:*mm-multi*] inherits = *multimaterial* high_current_on_filament_swap = 1 -end_gcode = {if not has_wipe_tower}\n; Pull the filament into the cooling tubes.\nG1 E-4 F2100.00000\nG91\nG1 Z1 F7200.000\nG90\nG1 X245 Y1\nG1 X240 E4\nG1 F4000\nG1 X190 E2.7 \nG1 F4600\nG1 X110 E2.8\nG1 F5200\nG1 X40 E3 \nG1 E-15.0000 F5000\nG1 E-50.0000 F5400\nG1 E-15.0000 F3000\nG1 E-12.0000 F2000\nG1 F1600\nG1 X0 Y1 E3.0000\nG1 X50 Y1 E-5.0000\nG1 F2000\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-3.0000\nG4 S0\n{endif}\nM107 ; turn off fan\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nG28 X0 ; home X axis\nM900 K0 ; reset LA\nM84 ; disable motors +end_gcode = {if not has_wipe_tower}\n; Pull the filament into the cooling tubes.\nG1 E-4 F2100\nG91\nG1 Z1 F7200\nG90\nG1 X245 Y1\nG1 X240 E4\nG1 F4000\nG1 X190 E2.7\nG1 F4600\nG1 X110 E2.8\nG1 F5200\nG1 X40 E3\nG1 E-15 F5000\nG1 E-50 F5400\nG1 E-15 F3000\nG1 E-12 F2000\nG1 F1600\nG1 X0 Y1 E3\nG1 X50 Y1 E-5\nG1 F2000\nG1 X0 Y1 E5\nG1 X50 Y1 E-5\nG1 F2400\nG1 X0 Y1 E5\nG1 X50 Y1 E-5\nG1 F2400\nG1 X0 Y1 E5\nG1 X50 Y1 E-3\nG4 S0\n{endif}\nM107 ; turn off fan\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nG28 X0 ; home X axis\nM900 K0 ; reset LA\nM84 ; disable motors extruder_colour = #FFAA55;#E37BA0;#4ECDD3;#FB7259 nozzle_diameter = 0.4,0.4,0.4,0.4 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2\nPRINTER_HAS_BOWDEN -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.2.3 ; tell printer latest fw version\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting] ; MK2 firmware only supports the old M204 format\n; Start G-Code sequence START\nT[initial_tool]\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100 ; set max feedrate\nM92 E140 ; E-steps per filament milimeter\n{if not has_single_extruder_multi_material_priming}\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\n{endif}\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.2.3 ; tell printer latest fw version\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting] ; MK2 firmware only supports the old M204 format\n; Start G-Code sequence START\nT[initial_tool]\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0\nM203 E100 ; set max feedrate\nM92 E140 ; E-steps per filament milimeter\n{if not has_single_extruder_multi_material_priming}\nG1 Z0.25 F7200\nG1 X50 E80 F1000\nG1 X160 E20 F1000\nG1 Z0.2 F7200\nG1 X220 E13 F1000\nG1 X240 E0 F1000\n{endif}\nG92 E0 default_print_profile = 0.15mm OPTIMAL # XXXXXXXXXXXXXXXXX @@ -5438,21 +5041,21 @@ inherits = Original Prusa i3 MK2S printer_model = MK2.5 remaining_times = 1 machine_max_jerk_e = 4.5 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 [printer:Original Prusa i3 MK2.5 0.25 nozzle] inherits = Original Prusa i3 MK2S 0.25 nozzle printer_model = MK2.5 remaining_times = 1 machine_max_jerk_e = 4.5 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 [printer:Original Prusa i3 MK2.5 0.6 nozzle] inherits = Original Prusa i3 MK2S 0.6 nozzle printer_model = MK2.5 remaining_times = 1 machine_max_jerk_e = 4.5 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 [printer:Original Prusa i3 MK2.5 0.8 nozzle] inherits = Original Prusa i3 MK2S 0.6 nozzle @@ -5464,39 +5067,20 @@ min_layer_height = 0.2 retract_length = 1 remaining_times = 1 machine_max_jerk_e = 4.5 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle [printer:Original Prusa i3 MK2.5 MMU2 Single] -inherits = Original Prusa i3 MK2.5; *mm2* +inherits = *25mm2* printer_model = MK2.5MMU2 single_extruder_multi_material = 0 max_print_height = 200 -remaining_times = 1 -silent_mode = 0 -retract_lift_below = 199 -machine_max_acceleration_e = 10000 -machine_max_acceleration_extruding = 2000 -machine_max_acceleration_retracting = 1500 -machine_max_acceleration_x = 9000 -machine_max_acceleration_y = 9000 -machine_max_acceleration_z = 500 -machine_max_feedrate_e = 120 -machine_max_feedrate_x = 500 -machine_max_feedrate_y = 500 -machine_max_feedrate_z = 12 -machine_max_jerk_e = 4.5 -machine_max_jerk_x = 10 -machine_max_jerk_y = 10 -machine_max_jerk_z = 0.2 -machine_min_extruding_rate = 0 -machine_min_travel_rate = 0 default_print_profile = 0.15mm OPTIMAL @MK2.5 default_filament_profile = Prusament PLA printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2.5\n -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\n; select extruder\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; load to nozzle\nTc\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n -end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM104 S0 ; turn off temperature\nM900 K0 ; reset LA\nM84 ; disable motors +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\n; select extruder\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; load to nozzle\nTc\n; purge line\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.20 F1000\nG1 X5 E4 F1000\nG92 E0\n +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM104 S0 ; turn off temperature\nM900 K0 ; reset LA\nM84 ; disable motors [printer:Original Prusa i3 MK2.5 MMU2 Single 0.8 nozzle] inherits = Original Prusa i3 MK2.5S MMU2S Single 0.8 nozzle @@ -5511,87 +5095,43 @@ inherits = Original Prusa i3 MK2.5S MMU2S Single 0.25 nozzle printer_model = MK2.5MMU2 [printer:Original Prusa i3 MK2.5 MMU2] -inherits = Original Prusa i3 MK2.5; *mm2* +inherits = *25mm2* printer_model = MK2.5MMU2 max_print_height = 200 -remaining_times = 1 -silent_mode = 0 -retract_lift_below = 199 -machine_max_acceleration_e = 10000 -machine_max_acceleration_extruding = 2000 -machine_max_acceleration_retracting = 1500 -machine_max_acceleration_x = 9000 -machine_max_acceleration_y = 9000 -machine_max_acceleration_z = 500 -machine_max_feedrate_e = 120 -machine_max_feedrate_x = 500 -machine_max_feedrate_y = 500 -machine_max_feedrate_z = 12 -machine_max_jerk_e = 4.5 -machine_max_jerk_x = 10 -machine_max_jerk_y = 10 -machine_max_jerk_z = 0.2 -machine_min_extruding_rate = 0 -machine_min_travel_rate = 0 default_print_profile = 0.15mm OPTIMAL @MK2.5 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2.5\n single_extruder_multi_material = 1 -# The 5x nozzle diameter defines the number of extruders. Other extruder parameters -# (for example the retract values) are duplicaed from the first value, so they do not need -# to be defined explicitely. nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG92 E0.0\n -end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM900 K0 ; reset LA\nM84 ; disable motors\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E32 F1073\nG1 X5 E32 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\nG92 E0\n +end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM900 K0 ; reset LA\nM84 ; disable motors\n [printer:Original Prusa i3 MK2.5S] inherits = Original Prusa i3 MK2.5 printer_model = MK2.5S -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 [printer:Original Prusa i3 MK2.5S 0.25 nozzle] inherits = Original Prusa i3 MK2.5 0.25 nozzle printer_model = MK2.5S -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 [printer:Original Prusa i3 MK2.5S 0.6 nozzle] inherits = Original Prusa i3 MK2.5 0.6 nozzle printer_model = MK2.5S -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 [printer:Original Prusa i3 MK2.5S 0.8 nozzle] inherits = Original Prusa i3 MK2.5 0.8 nozzle printer_model = MK2.5S [printer:Original Prusa i3 MK2.5S MMU2S Single] -inherits = Original Prusa i3 MK2.5; *mm2s* +inherits = *25mm2s* printer_model = MK2.5SMMU2S single_extruder_multi_material = 0 max_print_height = 200 -remaining_times = 1 -silent_mode = 0 -retract_lift_below = 199 -machine_max_acceleration_e = 10000 -machine_max_acceleration_extruding = 2000 -machine_max_acceleration_retracting = 1500 -machine_max_acceleration_x = 9000 -machine_max_acceleration_y = 9000 -machine_max_acceleration_z = 500 -machine_max_feedrate_e = 120 -machine_max_feedrate_x = 500 -machine_max_feedrate_y = 500 -machine_max_feedrate_z = 12 -machine_max_jerk_e = 4.5 -machine_max_jerk_x = 10 -machine_max_jerk_y = 10 -machine_max_jerk_z = 0.2 -machine_min_extruding_rate = 0 -machine_min_travel_rate = 0 default_print_profile = 0.15mm OPTIMAL @MK2.5 default_filament_profile = Prusament PLA printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2.5\n -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n -end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM104 S0 ; turn off temperature\nM900 K0 ; reset LA\nM84 ; disable motors +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM104 S0 ; turn off temperature\nM900 K0 ; reset LA\nM84 ; disable motors [printer:Original Prusa i3 MK2.5S MMU2S Single 0.8 nozzle] inherits = Original Prusa i3 MK2.5S MMU2S Single @@ -5601,7 +5141,7 @@ min_layer_height = 0.2 nozzle_diameter = 0.8 printer_variant = 0.8 retract_length = 1 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle @@ -5623,41 +5163,19 @@ nozzle_diameter = 0.25 printer_variant = 0.25 retract_lift = 0.15 default_print_profile = 0.10mm DETAIL 0.25 nozzle -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F1400.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F1400\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n [printer:Original Prusa i3 MK2.5S MMU2S] -inherits = Original Prusa i3 MK2.5; *mm2s* +inherits = *25mm2s* printer_model = MK2.5SMMU2S max_print_height = 200 -remaining_times = 1 -silent_mode = 0 -retract_lift_below = 199 -machine_max_acceleration_e = 10000 -machine_max_acceleration_extruding = 2000 -machine_max_acceleration_retracting = 1500 -machine_max_acceleration_x = 9000 -machine_max_acceleration_y = 9000 -machine_max_acceleration_z = 500 -machine_max_feedrate_e = 120 -machine_max_feedrate_x = 500 -machine_max_feedrate_y = 500 -machine_max_feedrate_z = 12 -machine_max_jerk_e = 4.5 -machine_max_jerk_x = 10 -machine_max_jerk_y = 10 -machine_max_jerk_z = 0.2 -machine_min_extruding_rate = 0 -machine_min_travel_rate = 0 default_print_profile = 0.15mm OPTIMAL @MK2.5 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2.5\n single_extruder_multi_material = 1 -# The 5x nozzle diameter defines the number of extruders. Other extruder parameters -# (for example the retract values) are duplicaed from the first value, so they do not need -# to be defined explicitely. nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG92 E0.0\n -end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM900 K0 ; reset LA\nM84 ; disable motors\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E29 F1073\nG1 X5 E29 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\nG92 E0\n +end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM900 K0 ; reset LA\nM84 ; disable motors\n [printer:Original Prusa i3 MK2.5S MMU2S 0.6 nozzle] inherits = Original Prusa i3 MK2.5S MMU2S @@ -5675,7 +5193,7 @@ min_layer_height = 0.15 printer_variant = 0.6 default_print_profile = 0.20mm NORMAL @0.6 nozzle -## For later use. 0.8mm nozzle profiles are only available for MMU2 Single mode at the moment. +## 0.8mm nozzle profiles are only available for MMU2 Single mode at the moment. ## [printer:Original Prusa i3 MK2.5S MMU2S 0.8 nozzle] ## inherits = Original Prusa i3 MK2.5S MMU2S @@ -5725,7 +5243,7 @@ remaining_times = 1 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n retract_lift_below = 209 max_print_height = 210 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} printer_model = MK3 default_print_profile = 0.15mm QUALITY @MK3 @@ -5736,7 +5254,7 @@ max_layer_height = 0.15 min_layer_height = 0.05 printer_variant = 0.25 retract_lift = 0.15 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E8.0 F700.0 ; intro line\nG1 X100.0 E12.5 F700.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E8 F700 ; intro line\nG1 X100 E12.5 F700 ; intro line\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} default_print_profile = 0.10mm DETAIL @0.25 nozzle MK3 [printer:Original Prusa i3 MK3 0.6 nozzle] @@ -5745,7 +5263,7 @@ nozzle_diameter = 0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif} default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 [printer:Original Prusa i3 MK3 0.8 nozzle] @@ -5755,7 +5273,7 @@ max_layer_height = 0.6 min_layer_height = 0.2 printer_variant = 0.8 retract_length = 1 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0\nM221 S95 default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle @@ -5763,13 +5281,11 @@ default_filament_profile = Prusament PLA @0.8 nozzle inherits = Original Prusa i3 MK3 renamed_from = "Original Prusa i3 MK3S" printer_model = MK3S -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} [printer:Original Prusa i3 MK3S & MK3S+ 0.25 nozzle] inherits = Original Prusa i3 MK3 0.25 nozzle renamed_from = "Original Prusa i3 MK3S 0.25 nozzle" printer_model = MK3S -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E8.0 F700.0 ; intro line\nG1 X100.0 E12.5 F700.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} [printer:Original Prusa i3 MK3S & MK3S+ 0.6 nozzle] inherits = Original Prusa i3 MK3 0.6 nozzle @@ -5804,12 +5320,32 @@ printer_model = MK3SMMU2S default_print_profile = 0.15mm QUALITY @MK3 default_filament_profile = Prusament PLA @MMU2 +[printer:*25mm2*] +inherits = Original Prusa i3 MK2.5 +single_extruder_multi_material = 1 +cooling_tube_length = 10 +cooling_tube_retraction = 30 +parking_pos_retraction = 85 +retract_length_toolchange = 3 +extra_loading_move = -13 +default_filament_profile = Prusament PLA @MMU2 + +[printer:*25mm2s*] +inherits = Original Prusa i3 MK2.5S +single_extruder_multi_material = 1 +cooling_tube_length = 20 +cooling_tube_retraction = 40 +parking_pos_retraction = 85 +retract_length_toolchange = 3 +extra_loading_move = -25 +default_filament_profile = Prusament PLA @MMU2 + [printer:Original Prusa i3 MK3 MMU2 Single] inherits = *mm2* single_extruder_multi_material = 0 default_filament_profile = Prusament PLA -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} -end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM84 ; disable motors +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM84 ; disable motors [printer:Original Prusa i3 MK3 MMU2 Single 0.6 nozzle] inherits = Original Prusa i3 MK3 MMU2 Single @@ -5818,7 +5354,7 @@ nozzle_diameter = 0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 [printer:Original Prusa i3 MK3 MMU2 Single 0.8 nozzle] @@ -5829,7 +5365,7 @@ max_layer_height = 0.6 min_layer_height = 0.2 printer_variant = 0.8 retract_length = 1 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle @@ -5841,27 +5377,24 @@ max_layer_height = 0.15 min_layer_height = 0.05 printer_variant = 0.25 retract_lift = 0.15 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 E8.0 F1000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F1400.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 E8 F1000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F1400\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} default_print_profile = 0.10mm DETAIL @0.25 nozzle MK3 [printer:Original Prusa i3 MK3 MMU2] inherits = *mm2* -# The 5x nozzle diameter defines the number of extruders. Other extruder parameters -# (for example the retract values) are duplicaed from the first value, so they do not need -# to be defined explicitely. machine_max_acceleration_e = 8000,8000 nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} -end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM84 ; disable motors\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E32 F1073\nG1 X5 E32 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} +end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM84 ; disable motors\n [printer:Original Prusa i3 MK3S & MK3S+ MMU2S Single] inherits = *mm2s* renamed_from = "Original Prusa i3 MK3S MMU2S Single" single_extruder_multi_material = 0 default_filament_profile = Prusament PLA -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} -end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM84 ; disable motors +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM84 ; disable motors [printer:Original Prusa i3 MK3S & MK3S+ MMU2S Single 0.6 nozzle] inherits = Original Prusa i3 MK3S & MK3S+ MMU2S Single @@ -5871,7 +5404,7 @@ nozzle_diameter = 0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 [printer:Original Prusa i3 MK3S & MK3S+ MMU2S Single 0.8 nozzle] @@ -5882,7 +5415,7 @@ max_layer_height = 0.6 min_layer_height = 0.2 printer_variant = 0.8 retract_length = 1 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle @@ -5895,7 +5428,7 @@ max_layer_height = 0.15 min_layer_height = 0.05 printer_variant = 0.25 retract_lift = 0.15 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F1400.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F1400\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} default_print_profile = 0.10mm DETAIL @0.25 nozzle MK3 [printer:Original Prusa i3 MK3S & MK3S+ MMU2S] @@ -5904,8 +5437,8 @@ renamed_from = "Original Prusa i3 MK3S MMU2S" machine_max_acceleration_e = 8000,8000 nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} -end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM84 ; disable motors\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E29 F1073\nG1 X5 E29 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} +end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM84 ; disable motors\n ## 0.6mm nozzle MMU2/S printer profiles @@ -5916,7 +5449,7 @@ nozzle_diameter = 0.6,0.6,0.6,0.6,0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E29 F1073\nG1 X5 E29 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 [printer:Original Prusa i3 MK3 MMU2 0.6 nozzle] @@ -5925,7 +5458,7 @@ nozzle_diameter = 0.6,0.6,0.6,0.6,0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E32 F1073\nG1 X5 E32 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 ## 0.8mm nozzle MMU2/S printer profiles @@ -5938,7 +5471,7 @@ default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 ## max_layer_height = 0.6 ## min_layer_height = 0.2 ## printer_variant = 0.8 -## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 +## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 ## default_print_profile = 0.40mm QUALITY @0.8 nozzle ## [printer:Original Prusa i3 MK3S & MK3S+ MMU2S 0.8 nozzle] @@ -5963,10 +5496,11 @@ thumbnails = 16x16,220x124 bed_shape = 0x0,180x0,180x180,0x180 default_filament_profile = "Prusament PLA" default_print_profile = 0.15mm QUALITY @MINI -gcode_flavor = marlin +gcode_flavor = marlinfirmware machine_max_acceleration_e = 5000 machine_max_acceleration_extruding = 1250 machine_max_acceleration_retracting = 1250 +machine_max_acceleration_travel = 1250 machine_max_acceleration_x = 1250 machine_max_acceleration_y = 1250 machine_max_acceleration_z = 400 @@ -5996,7 +5530,7 @@ retract_lift_below = 179 retract_layer_change = 1 silent_mode = 0 remaining_times = 1 -start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0.0\nG1 Y-2.0 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110.0 E8.0 F900\nG1 X40.0 E10.0 F700\nG92 E0.0\n\nM221 S95 ; set flow +start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0\nG1 Y-2 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110 E8 F900\nG1 X40 E10 F700\nG92 E0\n\nM221 S95 ; set flow end_gcode = G1 E-1 F2100 ; retract\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)}{endif} F720 ; Move print head up\nG1 X178 Y178 F4200 ; park print head\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} F720 ; Move print head further up\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM221 S100 ; reset flow\nM900 K0 ; reset LA\nM84 ; disable motors printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MINI\n extruder_colour = @@ -6012,7 +5546,7 @@ default_print_profile = 0.10mm DETAIL @0.25 nozzle MINI retract_length = 3 retract_lift = 0.15 retract_before_travel = 1 -start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0.0\nG1 Y-2.0 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110.0 E8.0 F600\nG1 X40.0 E10.0 F400\nG92 E0.0\n\nM221 S95 ; set flow +start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0\nG1 Y-2 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110 E8 F600\nG1 X40 E10 F400\nG92 E0\n\nM221 S95 ; set flow [printer:Original Prusa MINI & MINI+ 0.6 nozzle] inherits = Original Prusa MINI & MINI+ From 61528cbdc8c67a791d832d8b8dd037eb988bfa71 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 14 Apr 2021 07:38:50 +0200 Subject: [PATCH 06/26] Show info about custom supports and seam in ObjectList Slight refactoring in GLGizmosManager so it is easier to open a gizmo from the ObjectList --- resources/icons/info.png | Bin 0 -> 308 bytes src/slic3r/GUI/GUI_ObjectList.cpp | 68 +++++++++++++++--- src/slic3r/GUI/GUI_ObjectList.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 7 +- src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp | 8 ++- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 45 +++++++----- src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 2 + src/slic3r/GUI/ObjectDataViewModel.cpp | 72 ++++++++++++++++++- src/slic3r/GUI/ObjectDataViewModel.hpp | 18 ++++- 9 files changed, 190 insertions(+), 32 deletions(-) create mode 100644 resources/icons/info.png diff --git a/resources/icons/info.png b/resources/icons/info.png new file mode 100644 index 0000000000000000000000000000000000000000..9eeee9b3cdbac7fd819d3f4c3ee85414c5f1ed50 GIT binary patch literal 308 zcmV-40n7f0P)6|`_Rtm|7y^1~Ew=AW5=n0W(fT;mbLc<=CvuP!m`yl+rpgAzqp8NMnw zN!+2v9C;$1N30I+zs_}ZYEa-QtZ4bm;QmokMfEnO_zs(PV)Mv3c0VL!_bF(`IW#IJ zSafGP0SinrjIiz@J%^>R#;Ci-vyowo@ddeKY%{FzAieuODeleteWarningIcon(m_objects_model->GetParent(item)); m_objects_model->Delete(item); + update_info_items(obj_idx); } void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item) @@ -2118,20 +2119,32 @@ void ObjectList::part_selection_changed() { if (item) { - if (m_objects_model->GetParent(item) == wxDataViewItem(nullptr)) { - obj_idx = m_objects_model->GetIdByItem(item); + const ItemType type = m_objects_model->GetItemType(item); + const wxDataViewItem parent = m_objects_model->GetParent(item); + const ItemType parent_type = m_objects_model->GetItemType(parent); + obj_idx = m_objects_model->GetObjectIdByItem(item); + + if (parent == wxDataViewItem(nullptr) + || type == itInfo) { og_name = _(L("Object manipulation")); m_config = &(*m_objects)[obj_idx]->config; update_and_show_manipulations = true; + + if (type == itInfo) { + Unselect(item); + assert(parent_type == itObject); + Select(parent); + InfoItemType info_type = m_objects_model->GetInfoItemType(item); + GLGizmosManager::EType gizmo_type = + info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports + : GLGizmosManager::EType::Seam; + GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager(); + if (gizmos_mgr.get_current_type() != gizmo_type) + gizmos_mgr.open_gizmo(gizmo_type); + } } else { - obj_idx = m_objects_model->GetObjectIdByItem(item); - - const ItemType type = m_objects_model->GetItemType(item); if (type & itSettings) { - const auto parent = m_objects_model->GetParent(item); - const ItemType parent_type = m_objects_model->GetItemType(parent); - if (parent_type & itObject) { og_name = _(L("Object Settings to modify")); m_config = &(*m_objects)[obj_idx]->config; @@ -2243,6 +2256,38 @@ wxDataViewItem ObjectList::add_settings_item(wxDataViewItem parent_item, const D return ret; } + +void ObjectList::update_info_items(size_t obj_idx) +{ + const ModelObject* model_object = (*m_objects)[obj_idx]; + wxDataViewItem item_obj = m_objects_model->GetItemById(obj_idx); + assert(item_obj.IsOk()); + + for (InfoItemType type : {InfoItemType::CustomSupports, InfoItemType::CustomSeam}) { + wxDataViewItem item = m_objects_model->GetInfoItemByType(item_obj, type); + bool shows = item.IsOk(); + bool should_show = printer_technology() == ptFFF + && std::any_of(model_object->volumes.begin(), model_object->volumes.end(), + [type](const ModelVolume* mv) { + return ! (type == InfoItemType::CustomSupports + ? mv->supported_facets.empty() + : mv->seam_facets.empty()); + }); + + if (! shows && should_show) { + m_objects_model->AddInfoChild(item_obj, type); + Expand(item_obj); + } + else if (shows && ! should_show) { + Unselect(item); + m_objects_model->Delete(item); + Select(item_obj); + } + } +} + + + void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) { auto model_object = (*m_objects)[obj_idx]; @@ -2251,6 +2296,8 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) model_object->config.has("extruder") ? model_object->config.extruder() : 0, get_mesh_errors_count(obj_idx) > 0); + update_info_items(obj_idx); + // add volumes to the object if (model_object->volumes.size() > 1) { for (const ModelVolume* volume : model_object->volumes) { @@ -3029,7 +3076,7 @@ void ObjectList::update_selections_on_canvas() if (sel_cnt == 1) { wxDataViewItem item = GetSelection(); - if (m_objects_model->GetItemType(item) & (itSettings | itInstanceRoot | itLayerRoot | itLayer)) + if (m_objects_model->GetItemType(item) & (itSettings | itInstanceRoot | itLayerRoot | itLayer | itInfo)) add_to_selection(m_objects_model->GetParent(item), selection, instance_idx, mode); else add_to_selection(item, selection, instance_idx, mode); @@ -3442,6 +3489,9 @@ void ObjectList::update_object_list_by_printer_technology() m_objects_model->GetChildren(wxDataViewItem(nullptr), object_items); for (auto& object_item : object_items) { + // update custom supports info + update_info_items(m_objects_model->GetObjectIdByItem(object_item)); + // Update Settings Item for object update_settings_item_and_selection(object_item, sel); diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index d70c53849d..5812e26f75 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -18,7 +18,6 @@ class wxBoxSizer; class wxBitmapComboBox; class wxMenuItem; -class ObjectDataViewModel; class MenuWithSeparators; namespace Slic3r { @@ -347,6 +346,7 @@ public: void update_and_show_object_settings_item(); void update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections); void update_object_list_by_printer_technology(); + void update_info_items(size_t obj_idx); void instances_to_separated_object(const int obj_idx, const std::set& inst_idx); void instances_to_separated_objects(const int obj_idx); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index f3f87cc33e..d870687129 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -7,6 +7,7 @@ #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/ImGuiWrapper.hpp" #include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/GUI_ObjectList.hpp" #include @@ -316,8 +317,12 @@ void GLGizmoFdmSupports::update_model_object() const updated |= mv->supported_facets.set(*m_triangle_selectors[idx].get()); } - if (updated) + if (updated) { + const ModelObjectPtrs& mos = wxGetApp().model().objects; + wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin()); + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp index 6c3a8ddd3a..9724767550 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp @@ -7,7 +7,7 @@ #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/ImGuiWrapper.hpp" #include "slic3r/GUI/Plater.hpp" - +#include "slic3r/GUI/GUI_ObjectList.hpp" #include @@ -222,8 +222,12 @@ void GLGizmoSeam::update_model_object() const updated |= mv->seam_facets.set(*m_triangle_selectors[idx].get()); } - if (updated) + if (updated) { + const ModelObjectPtrs& mos = wxGetApp().model().objects; + wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin()); + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 453b98dad3..b1f38d309b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -165,6 +165,17 @@ void GLGizmosManager::reset_all_states() m_hover = Undefined; } +bool GLGizmosManager::open_gizmo(EType type) +{ + int idx = int(type); + if (m_gizmos[idx]->is_selectable() && m_gizmos[idx]->is_activable()) { + activate_gizmo(m_current == idx ? Undefined : (EType)idx); + update_data(); + return true; + } + return false; +} + void GLGizmosManager::set_hover_id(int id) { if (!m_enabled || m_current == Undefined) @@ -268,24 +279,21 @@ bool GLGizmosManager::is_running() const bool GLGizmosManager::handle_shortcut(int key) { - if (!m_enabled) + if (!m_enabled || m_parent.get_selection().is_empty()) return false; - if (m_parent.get_selection().is_empty()) + auto it = std::find_if(m_gizmos.begin(), m_gizmos.end(), + [key](const std::unique_ptr& gizmo) { + int gizmo_key = gizmo->get_shortcut_key(); + return gizmo->is_selectable() + && ((gizmo_key == key - 64) || (gizmo_key == key - 96)); + }); + + if (it == m_gizmos.end()) return false; - bool handled = false; - - for (size_t idx : get_selectable_idxs()) { - int it_key = m_gizmos[idx]->get_shortcut_key(); - - if (m_gizmos[idx]->is_activable() && ((it_key == key - 64) || (it_key == key - 96))) { - activate_gizmo(m_current == idx ? Undefined : (EType)idx); - handled = true; - } - } - - return handled; + EType gizmo_type = EType(it - m_gizmos.begin()); + return open_gizmo(gizmo_type); } bool GLGizmosManager::is_dragging() const @@ -819,10 +827,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) if (!processed && !evt.HasModifiers()) { if (handle_shortcut(keyCode)) - { - update_data(); processed = true; - } } if (processed) @@ -1161,5 +1166,11 @@ bool GLGizmosManager::is_in_editing_mode(bool error_notification) const return true; } + +int GLGizmosManager::get_shortcut_key(GLGizmosManager::EType type) const +{ + return m_gizmos[type]->get_shortcut_key(); +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 02fcc4ed88..01d7ea85c8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -172,6 +172,7 @@ public: void refresh_on_off_state(); void reset_all_states(); bool is_serializing() const { return m_serializing; } + bool open_gizmo(EType type); void set_hover_id(int id); void enable_grabber(EType type, unsigned int id, bool enable); @@ -229,6 +230,7 @@ public: void update_after_undo_redo(const UndoRedo::Snapshot& snapshot); int get_selectable_icons_cnt() const { return get_selectable_idxs().size(); } + int get_shortcut_key(GLGizmosManager::EType) const; private: void render_background(float left, float top, float right, float bottom, float border) const; diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index cf7b7b4796..395e329e8b 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -47,6 +47,19 @@ void ObjectDataViewModelNode::init_container() static constexpr char LayerRootIcon[] = "edit_layers_all"; static constexpr char LayerIcon[] = "edit_layers_some"; static constexpr char WarningIcon[] = "exclamation"; +static constexpr char InfoIcon[] = "info"; + +ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const InfoItemType info_type) : + m_parent(parent), + m_type(itInfo), + m_extruder(wxEmptyString) +{ + m_name = info_type == InfoItemType::CustomSupports + ? _L("Paint-on supports") + : _L("Paint-on seam"); + m_info_item_type = info_type; +} + ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type) : m_parent(parent), @@ -69,6 +82,8 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent m_bmp = create_scaled_bitmap(LayerRootIcon); // FIXME: pass window ptr m_name = _(L("Layers")); } + else if (type == itInfo) + assert(false); if (type & (itInstanceRoot | itLayerRoot)) init_container(); @@ -250,6 +265,7 @@ ObjectDataViewModel::ObjectDataViewModel() m_volume_bmps = MenuFactory::get_volume_bitmaps(); m_warning_bmp = create_scaled_bitmap(WarningIcon); + m_info_bmp = create_scaled_bitmap(InfoIcon); } ObjectDataViewModel::~ObjectDataViewModel() @@ -330,13 +346,44 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent return child; } +wxDataViewItem ObjectDataViewModel::AddInfoChild(const wxDataViewItem &parent_item, InfoItemType info_type) +{ + ObjectDataViewModelNode *root = static_cast(parent_item.GetID()); + if (!root) return wxDataViewItem(0); + + const auto node = new ObjectDataViewModelNode(root, info_type); + + // The new item should be added according to its order in InfoItemType. + // Find last info item with lower index and append after it. + const auto& children = root->GetChildren(); + int idx = -1; + for (int i=0; iGetType() == itInfo && int(children[i]->GetInfoItemType()) < int(info_type) ) + idx = i; + } + + root->Insert(node, idx+1); + node->SetBitmap(m_info_bmp); + // notify control + const wxDataViewItem child((void*)node); + ItemAdded(parent_item, child); + return child; +} + wxDataViewItem ObjectDataViewModel::AddSettingsChild(const wxDataViewItem &parent_item) { ObjectDataViewModelNode *root = static_cast(parent_item.GetID()); if (!root) return wxDataViewItem(0); const auto node = new ObjectDataViewModelNode(root, itSettings); - root->Insert(node, 0); + + // In case there are some info items, append after them. + size_t i = 0; + for (i = 0; iGetChildCount(); ++i) + if (root->GetNthChild(i)->GetType() != itInfo) + break; + + root->Insert(node, i); // notify control const wxDataViewItem child((void*)node); ItemAdded(parent_item, child); @@ -1379,6 +1426,14 @@ ItemType ObjectDataViewModel::GetItemType(const wxDataViewItem &item) const return node->m_type < 0 ? itUndef : node->m_type; } +InfoItemType ObjectDataViewModel::GetInfoItemType(const wxDataViewItem &item) const +{ + if (!item.IsOk()) + return InfoItemType::Undef; + ObjectDataViewModelNode *node = static_cast(item.GetID()); + return node->m_info_item_type; +} + wxDataViewItem ObjectDataViewModel::GetItemByType(const wxDataViewItem &parent_item, ItemType type) const { if (!parent_item.IsOk()) @@ -1411,6 +1466,21 @@ wxDataViewItem ObjectDataViewModel::GetLayerRootItem(const wxDataViewItem &item) return GetItemByType(item, itLayerRoot); } +wxDataViewItem ObjectDataViewModel::GetInfoItemByType(const wxDataViewItem &parent_item, InfoItemType type) const +{ + if (! parent_item.IsOk()) + return wxDataViewItem(0); + + ObjectDataViewModelNode *node = static_cast(parent_item.GetID()); + for (size_t i = 0; i < node->GetChildCount(); i++) { + const ObjectDataViewModelNode* child_node = node->GetNthChild(i); + if (child_node->m_type == itInfo && child_node->m_info_item_type == type) + return wxDataViewItem((void*)child_node); + } + + return wxDataViewItem(0); // not found +} + bool ObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const { if (!item.IsOk()) diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index c23ec195bd..cf440f5dc4 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -27,6 +27,7 @@ enum ItemType { itSettings = 16, itLayerRoot = 32, itLayer = 64, + itInfo = 128 }; enum ColumnNumber @@ -44,6 +45,13 @@ enum PrintIndicator piUnprintable , // unprintable }; +enum class InfoItemType +{ + Undef, + CustomSupports, + CustomSeam +}; + class ObjectDataViewModelNode; WX_DEFINE_ARRAY_PTR(ObjectDataViewModelNode*, MyObjectTreeModelNodePtrArray); @@ -69,6 +77,7 @@ class ObjectDataViewModelNode std::string m_action_icon_name = ""; ModelVolumeType m_volume_type; + InfoItemType m_info_item_type {InfoItemType::Undef}; public: ObjectDataViewModelNode(const wxString& name, @@ -104,6 +113,7 @@ public: const wxString& extruder = wxEmptyString ); ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type); + ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const InfoItemType type); ~ObjectDataViewModelNode() { @@ -176,6 +186,7 @@ public: const wxBitmap& GetBitmap() const { return m_bmp; } const wxString& GetName() const { return m_name; } ItemType GetType() const { return m_type; } + InfoItemType GetInfoItemType() const { return m_info_item_type; } void SetIdx(const int& idx); int GetIdx() const { return m_idx; } ModelVolumeType GetVolumeType() { return m_volume_type; } @@ -244,6 +255,7 @@ class ObjectDataViewModel :public wxDataViewModel std::vector m_objects; std::vector m_volume_bmps; wxBitmap m_warning_bmp; + wxBitmap m_info_bmp; wxDataViewCtrl* m_ctrl { nullptr }; @@ -261,6 +273,7 @@ public: const int extruder = 0, const bool create_frst_child = true); wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item); + wxDataViewItem AddInfoChild(const wxDataViewItem &parent_item, InfoItemType info_type); wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num); wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, const std::vector& print_indicator); wxDataViewItem AddLayersRoot(const wxDataViewItem &parent_item); @@ -335,12 +348,15 @@ public: // In our case it is an item with all columns bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; } - ItemType GetItemType(const wxDataViewItem &item) const ; + ItemType GetItemType(const wxDataViewItem &item) const; + InfoItemType GetInfoItemType(const wxDataViewItem &item) const; wxDataViewItem GetItemByType( const wxDataViewItem &parent_item, ItemType type) const; wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const; wxDataViewItem GetInstanceRootItem(const wxDataViewItem &item) const; wxDataViewItem GetLayerRootItem(const wxDataViewItem &item) const; + wxDataViewItem GetInfoItemByType(const wxDataViewItem &parent_item, InfoItemType type) const; + bool IsSettingsItem(const wxDataViewItem &item) const; void UpdateSettingsDigest( const wxDataViewItem &item, const std::vector& categories); From e48bc7a5e866afb931f1f2bac44fc5f037e065a6 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 19 Apr 2021 11:51:56 +0200 Subject: [PATCH 07/26] Info in ObjectList: Settings should be above the new info items, info items are selectable --- src/slic3r/GUI/GUI_ObjectList.cpp | 13 ++++++++----- src/slic3r/GUI/ObjectDataViewModel.cpp | 8 +------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index dbfc05a6d3..652fcc65b4 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1525,14 +1525,20 @@ void ObjectList::del_subobject_item(wxDataViewItem& item) if (type == itUndef) return; + wxDataViewItem parent = m_objects_model->GetParent(item); + if (type & itSettings) - del_settings_from_config(m_objects_model->GetParent(item)); + del_settings_from_config(parent); else if (type & itInstanceRoot && obj_idx != -1) del_instances_from_object(obj_idx); else if (type & itLayerRoot && obj_idx != -1) del_layers_from_object(obj_idx); else if (type & itLayer && obj_idx != -1) del_layer_from_object(obj_idx, m_objects_model->GetLayerRangeByItem(item)); + else if (type & itInfo && obj_idx != -1) { + Unselect(item); + Select(parent); + } else if (idx == -1) return; else if (!del_subobject_from_object(obj_idx, idx, type)) @@ -1540,7 +1546,7 @@ void ObjectList::del_subobject_item(wxDataViewItem& item) // If last volume item with warning was deleted, unmark object item if (type & itVolume && (*m_objects)[obj_idx]->get_mesh_errors_count() == 0) - m_objects_model->DeleteWarningIcon(m_objects_model->GetParent(item)); + m_objects_model->DeleteWarningIcon(parent); m_objects_model->Delete(item); update_info_items(obj_idx); @@ -2131,9 +2137,6 @@ void ObjectList::part_selection_changed() update_and_show_manipulations = true; if (type == itInfo) { - Unselect(item); - assert(parent_type == itObject); - Select(parent); InfoItemType info_type = m_objects_model->GetInfoItemType(item); GLGizmosManager::EType gizmo_type = info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 395e329e8b..541c2f8a9e 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -377,13 +377,7 @@ wxDataViewItem ObjectDataViewModel::AddSettingsChild(const wxDataViewItem &paren const auto node = new ObjectDataViewModelNode(root, itSettings); - // In case there are some info items, append after them. - size_t i = 0; - for (i = 0; iGetChildCount(); ++i) - if (root->GetNthChild(i)->GetType() != itInfo) - break; - - root->Insert(node, i); + root->Insert(node, 0); // notify control const wxDataViewItem child((void*)node); ItemAdded(parent_item, child); From bf1fc7d436e4696dd74f207984593f5c1b280a68 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 19 Apr 2021 15:42:57 +0200 Subject: [PATCH 08/26] Info in ObjectList: Added variable layer height --- src/slic3r/GUI/GLCanvas3D.cpp | 4 +++ src/slic3r/GUI/GUI_ObjectList.cpp | 45 ++++++++++++++++++-------- src/slic3r/GUI/ObjectDataViewModel.cpp | 6 ++-- src/slic3r/GUI/ObjectDataViewModel.hpp | 3 +- src/slic3r/GUI/Plater.cpp | 6 ++++ src/slic3r/GUI/Plater.hpp | 1 + 6 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 025b940043..105eeab06b 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -508,6 +508,7 @@ void GLCanvas3D::LayersEditing::reset_layer_height_profile(GLCanvas3D& canvas) m_layer_height_profile.clear(); m_layers_texture.valid = false; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + wxGetApp().obj_list()->update_info_items(last_object_id); } void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas, float quality_factor) @@ -517,6 +518,7 @@ void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas const_cast(m_model_object)->layer_height_profile.set(m_layer_height_profile); m_layers_texture.valid = false; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + wxGetApp().obj_list()->update_info_items(last_object_id); } void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_params) @@ -526,6 +528,7 @@ void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, const_cast(m_model_object)->layer_height_profile.set(m_layer_height_profile); m_layers_texture.valid = false; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + wxGetApp().obj_list()->update_info_items(last_object_id); } void GLCanvas3D::LayersEditing::generate_layer_height_texture() @@ -565,6 +568,7 @@ void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas) wxGetApp().plater()->take_snapshot(_(L("Variable layer height - Manual edit"))); const_cast(m_model_object)->layer_height_profile.set(m_layer_height_profile); canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + wxGetApp().obj_list()->update_info_items(last_object_id); } } m_layer_height_profile_modified = false; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 652fcc65b4..15c4578d82 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2138,12 +2138,15 @@ void ObjectList::part_selection_changed() if (type == itInfo) { InfoItemType info_type = m_objects_model->GetInfoItemType(item); - GLGizmosManager::EType gizmo_type = - info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports - : GLGizmosManager::EType::Seam; - GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager(); - if (gizmos_mgr.get_current_type() != gizmo_type) - gizmos_mgr.open_gizmo(gizmo_type); + if (info_type != InfoItemType::VariableLayerHeight) { + GLGizmosManager::EType gizmo_type = + info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports + : GLGizmosManager::EType::Seam; + GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager(); + if (gizmos_mgr.get_current_type() != gizmo_type) + gizmos_mgr.open_gizmo(gizmo_type); + } else + wxGetApp().plater()->toggle_layers_editing(true); } } else { @@ -2266,16 +2269,30 @@ void ObjectList::update_info_items(size_t obj_idx) wxDataViewItem item_obj = m_objects_model->GetItemById(obj_idx); assert(item_obj.IsOk()); - for (InfoItemType type : {InfoItemType::CustomSupports, InfoItemType::CustomSeam}) { + for (InfoItemType type : {InfoItemType::CustomSupports, + InfoItemType::CustomSeam, + InfoItemType::VariableLayerHeight}) { wxDataViewItem item = m_objects_model->GetInfoItemByType(item_obj, type); bool shows = item.IsOk(); - bool should_show = printer_technology() == ptFFF - && std::any_of(model_object->volumes.begin(), model_object->volumes.end(), - [type](const ModelVolume* mv) { - return ! (type == InfoItemType::CustomSupports - ? mv->supported_facets.empty() - : mv->seam_facets.empty()); - }); + bool should_show = false; + + switch (type) { + case InfoItemType::CustomSupports : + case InfoItemType::CustomSeam : + should_show = printer_technology() == ptFFF + && std::any_of(model_object->volumes.begin(), model_object->volumes.end(), + [type](const ModelVolume* mv) { + return ! (type == InfoItemType::CustomSupports + ? mv->supported_facets.empty() + : mv->seam_facets.empty()); + }); + break; + + case InfoItemType::VariableLayerHeight : + should_show = printer_technology() == ptFFF + && ! model_object->layer_height_profile.empty(); + break; + } if (! shows && should_show) { m_objects_model->AddInfoChild(item_obj, type); diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 541c2f8a9e..49c75f9f2f 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -54,9 +54,9 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent m_type(itInfo), m_extruder(wxEmptyString) { - m_name = info_type == InfoItemType::CustomSupports - ? _L("Paint-on supports") - : _L("Paint-on seam"); + m_name = info_type == InfoItemType::CustomSupports ? _L("Paint-on supports") + : info_type == InfoItemType::CustomSeam ? _L("Paint-on seam") + : _L("Variable layer height"); m_info_item_type = info_type; } diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index cf440f5dc4..1dd41bb107 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -49,7 +49,8 @@ enum class InfoItemType { Undef, CustomSupports, - CustomSeam + CustomSeam, + VariableLayerHeight }; class ObjectDataViewModelNode; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 2c3fbe7af7..c4489251a6 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4969,6 +4969,12 @@ void Plater::convert_unit(ConversionType conv_type) } } +void Plater::toggle_layers_editing(bool enable) +{ + if (canvas3D()->is_layers_editing_enabled() != enable) + wxPostEvent(canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); +} + void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper, bool keep_lower, bool rotate_lower) { wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds"); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 16590ed9b1..e99feee82f 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -191,6 +191,7 @@ public: bool is_selection_empty() const; void scale_selection_to_fit_print_volume(); void convert_unit(ConversionType conv_type); + void toggle_layers_editing(bool enable); void cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false); From 4f950343c862e4ee67f9b57395fd9883c0b573c0 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 20 Apr 2021 15:07:00 +0200 Subject: [PATCH 09/26] MMU segmentation refactoring: Most of the MMU segmentation code was extracted to its own file. --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/MultiMaterialSegmentation.cpp | 1498 +++++++++++++++++++ src/libslic3r/MultiMaterialSegmentation.hpp | 18 + src/libslic3r/Print.hpp | 5 - src/libslic3r/PrintObject.cpp | 1487 +----------------- 5 files changed, 1520 insertions(+), 1490 deletions(-) create mode 100644 src/libslic3r/MultiMaterialSegmentation.cpp create mode 100644 src/libslic3r/MultiMaterialSegmentation.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 2abe94656d..e59d1efd89 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -128,6 +128,8 @@ add_library(libslic3r STATIC Model.hpp ModelArrange.hpp ModelArrange.cpp + MultiMaterialSegmentation.cpp + MultiMaterialSegmentation.hpp CustomGCode.cpp CustomGCode.hpp Arrange.hpp diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp new file mode 100644 index 0000000000..d4840a363a --- /dev/null +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -0,0 +1,1498 @@ +#include "BoundingBox.hpp" +#include "ClipperUtils.hpp" +#include "EdgeGrid.hpp" +#include "Layer.hpp" +#include "Print.hpp" +#include "VoronoiVisualUtils.hpp" + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +namespace Slic3r { +struct ColoredLine { + Line line; + int color; + int poly_idx = -1; + int local_line_idx = -1; +}; +} + +#include +namespace boost { namespace polygon { +template <> +struct geometry_concept { typedef segment_concept type; }; + +template <> +struct segment_traits { + typedef coord_t coordinate_type; + typedef Slic3r::Point point_type; + + static inline point_type get(const Slic3r::ColoredLine& line, direction_1d dir) { + return dir.to_int() ? line.line.b : line.line.a; + } +}; +} } + +namespace Slic3r { + +// Assumes that is at most same projected_l length or below than projection_l +static bool project_line_on_line(const Line &projection_l, const Line &projected_l, Line *new_projected) +{ + const Vec2d v1 = (projection_l.b - projection_l.a).cast(); + const Vec2d va = (projected_l.a - projection_l.a).cast(); + const Vec2d vb = (projected_l.b - projection_l.a).cast(); + const double l2 = v1.squaredNorm(); // avoid a sqrt + if (l2 == 0.0) + return false; + double t1 = va.dot(v1) / l2; + double t2 = vb.dot(v1) / l2; + t1 = std::clamp(t1, 0., 1.); + t2 = std::clamp(t2, 0., 1.); + assert(t1 >= 0.); + assert(t2 >= 0.); + assert(t1 <= 1.); + assert(t2 <= 1.); + + Point p1 = (projection_l.a.cast() + t1 * v1).cast(); + Point p2 = (projection_l.a.cast() + t2 * v1).cast(); + *new_projected = Line(p1, p2); + return true; +} + +struct PaintedLine +{ + size_t contour_idx; + size_t line_idx; + Line projected_line; + int color = 1; +}; + +struct PaintedLineVisitor +{ + PaintedLineVisitor(const EdgeGrid::Grid &grid, std::vector &painted_lines) : grid(grid), painted_lines(painted_lines) + { + painted_lines_set.reserve(painted_lines.capacity()); + } + + void reset() { painted_lines_set.clear(); } + + bool operator()(coord_t iy, coord_t ix) + { + // Called with a row and column of the grid cell, which is intersected by a line. + auto cell_data_range = grid.cell_data_range(iy, ix); + for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++it_contour_and_segment) { + Line grid_line = grid.line(*it_contour_and_segment); + + const Vec2d v1 = (line_to_test.b - line_to_test.a).cast().normalized(); + const Vec2d v2 = (grid_line.b - grid_line.a).cast().normalized(); + double angle = ::acos(clamp(-1.0, 1.0, v1.dot(v2))); + double angle_deg = Geometry::rad2deg(angle); + // When lines have too different length, it is necessary to normalize them + if ((angle_deg >= 0 && angle_deg <= 30) || (angle_deg >= 150)) { + Line line_to_test_projected; + project_line_on_line(grid_line, line_to_test, &line_to_test_projected); + + if (painted_lines_set.find(*it_contour_and_segment) == painted_lines_set.end()) { + if (Line(grid_line.a, line_to_test_projected.a).length() > Line(grid_line.a, line_to_test_projected.b).length()) { + line_to_test_projected.reverse(); + } + + double dist_1 = grid_line.distance_to(line_to_test.a); + double dist_2 = grid_line.distance_to(line_to_test.b); + double dist_3 = line_to_test.distance_to(grid_line.a); + double dist_4 = line_to_test.distance_to(grid_line.b); + double total_dist = std::min(std::min(dist_1, dist_2), std::min(dist_3, dist_4)); + + if (total_dist > 50 * SCALED_EPSILON) + continue; + + painted_lines.push_back({it_contour_and_segment->first, it_contour_and_segment->second, line_to_test_projected, this->color}); + painted_lines_set.insert(*it_contour_and_segment); + } + } + } + // Continue traversing the grid along the edge. + return true; + } + + const EdgeGrid::Grid &grid; + std::vector &painted_lines; + Line line_to_test; + std::unordered_set, boost::hash>> painted_lines_set; + int color = -1; +}; + +static std::vector to_colored_lines(const Polygon &polygon, int color) +{ + std::vector lines; + lines.reserve(polygon.points.size()); + if (polygon.points.size() > 2) { + for (auto it = polygon.points.begin(); it != polygon.points.end() - 1; ++it) + lines.push_back({Line(*it, *(it + 1)), color}); + lines.push_back({Line(polygon.points.back(), polygon.points.front()), color}); + } + return lines; +} + +static Polygon colored_points_to_polygon(const std::vector &lines) +{ + Points out; + for (const ColoredLine &l : lines) + out.emplace_back(l.line.a); + return Polygon(out); +} + +static Polygons colored_points_to_polygon(const std::vector> &lines) +{ + Polygons out; + for (const std::vector &l : lines) + out.emplace_back(colored_points_to_polygon(l)); + return out; +} + +inline std::vector to_lines(const std::vector> &c_lines) +{ + size_t n_lines = 0; + for (const auto &c_line : c_lines) + n_lines += c_line.size(); + std::vector lines; + lines.reserve(n_lines); + for (const auto &c_line : c_lines) + lines.insert(lines.end(), c_line.begin(), c_line.end()); + return lines; +} + +// Double vertex equal to a coord_t point after conversion to double. +template +inline bool vertex_equal_to_point(const VertexType &vertex, const Point &ipt) +{ + // Convert ipt to doubles, force the 80bit FPU temporary to 64bit and then compare. + // This should work with any settings of math compiler switches and the C++ compiler + // shall understand the memcpies as type punning and it shall optimize them out. + using ulp_cmp_type = boost::polygon::detail::ulp_comparison; + ulp_cmp_type ulp_cmp; + static constexpr int ULPS = boost::polygon::voronoi_diagram_traits::vertex_equality_predicate_type::ULPS; + return ulp_cmp(vertex.x(), double(ipt.x()), ULPS) == ulp_cmp_type::EQUAL && + ulp_cmp(vertex.y(), double(ipt.y()), ULPS) == ulp_cmp_type::EQUAL; +} + +bool vertex_equal_to_point(const Voronoi::VD::vertex_type *vertex, const Point &ipt) { + return vertex_equal_to_point(*vertex, ipt); +} + +static std::vector> get_segments(const std::vector &polygon) +{ + std::vector> segments; + + size_t segment_end = 0; + while (segment_end + 1 < polygon.size() && polygon[segment_end].color == polygon[segment_end + 1].color) + segment_end++; + + if (segment_end == polygon.size() - 1) + return {std::make_pair(0, polygon.size() - 1)}; + + size_t first_different_color = (segment_end + 1) % polygon.size(); + for (size_t line_offset_idx = 0; line_offset_idx < polygon.size(); ++line_offset_idx) { + size_t start_s = (first_different_color + line_offset_idx) % polygon.size(); + size_t end_s = start_s; + + while (line_offset_idx + 1 < polygon.size() && polygon[start_s].color == polygon[(first_different_color + line_offset_idx + 1) % polygon.size()].color) { + end_s = (first_different_color + line_offset_idx + 1) % polygon.size(); + line_offset_idx++; + } + segments.emplace_back(start_s, end_s); + } + return segments; +} + +static std::vector>> get_all_segments(const std::vector> &color_poly) +{ + std::vector>> all_segments(color_poly.size()); + for (size_t poly_idx = 0; poly_idx < color_poly.size(); ++poly_idx) { + const std::vector &c_polygon = color_poly[poly_idx]; + all_segments[poly_idx] = get_segments(c_polygon); + } + return all_segments; +} + +static std::vector colorize_line(const Line & line_to_process, + const size_t start_idx, + const size_t end_idx, + std::vector &painted_lines) +{ + std::vector internal_painted; + for (size_t line_idx = start_idx; line_idx <= end_idx; ++line_idx) { internal_painted.emplace_back(painted_lines[line_idx]); } + const int filter_eps_value = scale_(0.1f); + std::vector filtered_lines; + filtered_lines.emplace_back(internal_painted.front()); + for (size_t line_idx = 1; line_idx < internal_painted.size(); ++line_idx) { + PaintedLine &prev = filtered_lines.back(); + PaintedLine &curr = internal_painted[line_idx]; + + double prev_length = prev.projected_line.length(); + double curr_dist_start = (curr.projected_line.a - prev.projected_line.a).cast().norm(); + double dist_between_lines = curr_dist_start - prev_length; + + if (dist_between_lines >= 0) { + if (prev.color == curr.color) { + if (dist_between_lines <= filter_eps_value) { + prev.projected_line.b = curr.projected_line.b; + } else { + filtered_lines.emplace_back(curr); + } + } else { + filtered_lines.emplace_back(curr); + } + } else { + double curr_dist_end = (curr.projected_line.b - prev.projected_line.a).cast().norm(); + if (curr_dist_end <= prev_length) { + } else { + if (prev.color == curr.color) { + prev.projected_line.b = curr.projected_line.b; + } else { + curr.projected_line.a = prev.projected_line.b; + filtered_lines.emplace_back(curr); + } + } + } + } + + std::vector final_lines; + double dist_to_start = (filtered_lines.front().projected_line.a - line_to_process.a).cast().norm(); + if (dist_to_start <= filter_eps_value) { + filtered_lines.front().projected_line.a = line_to_process.a; + final_lines.push_back({filtered_lines.front().projected_line, filtered_lines.front().color}); + } else { + final_lines.push_back({Line(line_to_process.a, filtered_lines.front().projected_line.a), 0}); + final_lines.push_back({filtered_lines.front().projected_line, filtered_lines.front().color}); + } + + for (size_t line_idx = 1; line_idx < filtered_lines.size(); ++line_idx) { + ColoredLine &prev = final_lines.back(); + PaintedLine &curr = filtered_lines[line_idx]; + + double line_dist = (curr.projected_line.a - prev.line.b).cast().norm(); + if (line_dist <= filter_eps_value) { + if (prev.color == curr.color) { + prev.line.b = curr.projected_line.b; + } else { + prev.line.b = curr.projected_line.a; + final_lines.push_back({curr.projected_line, curr.color}); + } + } else { + final_lines.push_back({Line(prev.line.b, curr.projected_line.a), 0}); + final_lines.push_back({curr.projected_line, curr.color}); + } + } + + double dist_to_end = (final_lines.back().line.b - line_to_process.b).cast().norm(); + if (dist_to_end <= filter_eps_value) + final_lines.back().line.b = line_to_process.b; + else + final_lines.push_back({Line(final_lines.back().line.b, line_to_process.b), 0}); + + for (size_t line_idx = 1; line_idx < final_lines.size(); ++line_idx) + assert(final_lines[line_idx - 1].line.b == final_lines[line_idx].line.a); + + for (size_t line_idx = 2; line_idx < final_lines.size(); ++line_idx) { + const ColoredLine &line_0 = final_lines[line_idx - 2]; + ColoredLine & line_1 = final_lines[line_idx - 1]; + const ColoredLine &line_2 = final_lines[line_idx - 0]; + + if (line_0.color == line_2.color && line_0.color != line_1.color) + if (line_1.line.length() <= scale_(0.2)) line_1.color = line_0.color; + } + + std::vector colored_lines_simpl; + colored_lines_simpl.emplace_back(final_lines.front()); + for (size_t line_idx = 1; line_idx < final_lines.size(); ++line_idx) { + const ColoredLine &line_0 = final_lines[line_idx]; + + if (colored_lines_simpl.back().color == line_0.color) + colored_lines_simpl.back().line.b = line_0.line.b; + else + colored_lines_simpl.emplace_back(line_0); + } + + final_lines = colored_lines_simpl; + + if (final_lines.size() > 1) { + if (final_lines.front().color != final_lines[1].color && final_lines.front().line.length() <= scale_(0.2)) { + final_lines[1].line.a = final_lines.front().line.a; + final_lines.erase(final_lines.begin()); + } + } + + if (final_lines.size() > 1) { + if (final_lines.back().color != final_lines[final_lines.size() - 2].color && final_lines.back().line.length() <= scale_(0.2)) { + final_lines[final_lines.size() - 2].line.b = final_lines.back().line.b; + final_lines.pop_back(); + } + } + + return final_lines; +} + +static std::vector colorize_polygon(const Polygon &poly, const size_t start_idx, const size_t end_idx, std::vector &painted_lines) +{ + std::vector new_lines; + Lines lines = poly.lines(); + + for (size_t idx = 0; idx < painted_lines[start_idx].line_idx; ++idx) + new_lines.emplace_back(ColoredLine{lines[idx], 0}); + + for (size_t first_idx = start_idx; first_idx <= end_idx; ++first_idx) { + size_t second_idx = first_idx; + while (second_idx <= end_idx && painted_lines[first_idx].line_idx == painted_lines[second_idx].line_idx) ++second_idx; + --second_idx; + + assert(painted_lines[first_idx].line_idx == painted_lines[second_idx].line_idx); + std::vector lines_c_line = colorize_line(lines[painted_lines[first_idx].line_idx], first_idx, second_idx, painted_lines); + new_lines.insert(new_lines.end(), lines_c_line.begin(), lines_c_line.end()); + + if (second_idx + 1 <= end_idx) + for (size_t idx = painted_lines[second_idx].line_idx + 1; idx < painted_lines[second_idx + 1].line_idx; ++idx) + new_lines.emplace_back(ColoredLine{lines[idx], 0}); + + first_idx = second_idx; + } + + for (size_t idx = painted_lines[end_idx].line_idx + 1; idx < poly.size(); ++idx) + new_lines.emplace_back(ColoredLine{lines[idx], 0}); + + for (size_t line_idx = 2; line_idx < new_lines.size(); ++line_idx) { + const ColoredLine &line_0 = new_lines[line_idx - 2]; + ColoredLine & line_1 = new_lines[line_idx - 1]; + const ColoredLine &line_2 = new_lines[line_idx - 0]; + + if (line_0.color == line_2.color && line_0.color != line_1.color && line_0.color >= 1) { + if (line_1.line.length() <= scale_(0.5)) line_1.color = line_0.color; + } + } + + for (size_t line_idx = 3; line_idx < new_lines.size(); ++line_idx) { + const ColoredLine &line_0 = new_lines[line_idx - 3]; + ColoredLine & line_1 = new_lines[line_idx - 2]; + ColoredLine & line_2 = new_lines[line_idx - 1]; + const ColoredLine &line_3 = new_lines[line_idx - 0]; + + if (line_0.color == line_3.color && (line_0.color != line_1.color || line_0.color != line_2.color) && line_0.color >= 1 && line_3.color >= 1) { + if ((line_1.line.length() + line_2.line.length()) <= scale_(0.5)) { + line_1.color = line_0.color; + line_2.color = line_0.color; + } + } + } + + std::vector> segments = get_segments(new_lines); + auto segment_length = [&new_lines](const std::pair &segment) { + double total_length = 0; + for (size_t seg_start_idx = segment.first; seg_start_idx != segment.second; seg_start_idx = (seg_start_idx + 1 < new_lines.size()) ? seg_start_idx + 1 : 0) + total_length += new_lines[seg_start_idx].line.length(); + total_length += new_lines[segment.second].line.length(); + return total_length; + }; + + for (size_t pair_idx = 1; pair_idx < segments.size(); ++pair_idx) { + int color0 = new_lines[segments[pair_idx - 1].first].color; + int color1 = new_lines[segments[pair_idx - 0].first].color; + + double seg0l = segment_length(segments[pair_idx - 1]); + double seg1l = segment_length(segments[pair_idx - 0]); + + if (color0 != color1 && seg0l >= scale_(0.1) && seg1l <= scale_(0.2)) { + for (size_t seg_start_idx = segments[pair_idx].first; seg_start_idx != segments[pair_idx].second; seg_start_idx = (seg_start_idx + 1 < new_lines.size()) ? seg_start_idx + 1 : 0) + new_lines[seg_start_idx].color = color0; + new_lines[segments[pair_idx].second].color = color0; + } + } + + segments = get_segments(new_lines); + for (size_t pair_idx = 1; pair_idx < segments.size(); ++pair_idx) { + int color0 = new_lines[segments[pair_idx - 1].first].color; + int color1 = new_lines[segments[pair_idx - 0].first].color; + double seg1l = segment_length(segments[pair_idx - 0]); + + if (color0 >= 1 && color0 != color1 && seg1l <= scale_(0.2)) { + for (size_t seg_start_idx = segments[pair_idx].first; seg_start_idx != segments[pair_idx].second; seg_start_idx = (seg_start_idx + 1 < new_lines.size()) ? seg_start_idx + 1 : 0) + new_lines[seg_start_idx].color = color0; + new_lines[segments[pair_idx].second].color = color0; + } + } + + for (size_t pair_idx = 2; pair_idx < segments.size(); ++pair_idx) { + int color0 = new_lines[segments[pair_idx - 2].first].color; + int color1 = new_lines[segments[pair_idx - 1].first].color; + int color2 = new_lines[segments[pair_idx - 0].first].color; + + if (color0 > 0 && color0 == color2 && color0 != color1 && segment_length(segments[pair_idx - 1]) <= scale_(0.5)) { + for (size_t seg_start_idx = segments[pair_idx].first; seg_start_idx != segments[pair_idx].second; seg_start_idx = (seg_start_idx + 1 < new_lines.size()) ? seg_start_idx + 1 : 0) + new_lines[seg_start_idx].color = color0; + new_lines[segments[pair_idx].second].color = color0; + } + } + + return new_lines; +} + +static std::vector> colorize_polygons(const Polygons &polygons, std::vector &painted_lines) +{ + const size_t start_idx = 0; + const size_t end_idx = painted_lines.size() - 1; + + std::vector> new_polygons; + + for (size_t idx = 0; idx < painted_lines[start_idx].contour_idx; ++idx) + new_polygons.emplace_back(to_colored_lines(polygons[idx], 0)); + + for (size_t first_idx = start_idx; first_idx <= end_idx; ++first_idx) { + size_t second_idx = first_idx; + while (second_idx <= end_idx && painted_lines[first_idx].contour_idx == painted_lines[second_idx].contour_idx) + ++second_idx; + --second_idx; + + assert(painted_lines[first_idx].contour_idx == painted_lines[second_idx].contour_idx); + std::vector polygon_c = colorize_polygon(polygons[painted_lines[first_idx].contour_idx], first_idx, second_idx, painted_lines); + new_polygons.emplace_back(polygon_c); + + if (second_idx + 1 <= end_idx) + for (size_t idx = painted_lines[second_idx].contour_idx + 1; idx < painted_lines[second_idx + 1].contour_idx; ++idx) + new_polygons.emplace_back(to_colored_lines(polygons[idx], 0)); + + first_idx = second_idx; + } + + for (size_t idx = painted_lines[end_idx].contour_idx + 1; idx < polygons.size(); ++idx) + new_polygons.emplace_back(to_colored_lines(polygons[idx], 0)); + + return new_polygons; +} + +using boost::polygon::voronoi_diagram; + +struct MMU_Graph +{ + enum class ARC_TYPE { BORDER, NON_BORDER }; + + struct Arc + { + size_t from_idx; + size_t to_idx; + int color; + ARC_TYPE type; + bool used{false}; + + bool operator==(const Arc &rhs) const { return (from_idx == rhs.from_idx) && (to_idx == rhs.to_idx) && (color == rhs.color) && (type == rhs.type); } + bool operator!=(const Arc &rhs) const { return !operator==(rhs); } + }; + + struct Node + { + Point point; + std::list neighbours; + + void remove_edge(const size_t to_idx) + { + for (auto arc_it = this->neighbours.begin(); arc_it != this->neighbours.end(); ++arc_it) { + if (arc_it->to_idx == to_idx) { + assert(arc_it->type != ARC_TYPE::BORDER); + this->neighbours.erase(arc_it); + break; + } + } + } + }; + + std::vector nodes; + std::vector arcs; + size_t all_border_points{}; + + std::vector polygon_idx_offset; + std::vector polygon_sizes; + + void remove_edge(const size_t from_idx, const size_t to_idx) + { + nodes[from_idx].remove_edge(to_idx); + nodes[to_idx].remove_edge(from_idx); + } + + size_t get_global_index(const size_t poly_idx, const size_t point_idx) const { return polygon_idx_offset[poly_idx] + point_idx; } + + void append_edge(const size_t &from_idx, const size_t &to_idx, int color = -1, ARC_TYPE type = ARC_TYPE::NON_BORDER) + { + // Don't append duplicate edges between the same nodes. + for (const MMU_Graph::Arc &arc : this->nodes[from_idx].neighbours) + if (arc.to_idx == to_idx) + return; + for (const MMU_Graph::Arc &arc : this->nodes[to_idx].neighbours) + if (arc.to_idx == to_idx) + return; + + this->nodes[from_idx].neighbours.push_back({from_idx, to_idx, color, type}); + this->nodes[to_idx].neighbours.push_back({to_idx, from_idx, color, type}); + this->arcs.push_back({from_idx, to_idx, color, type}); + this->arcs.push_back({to_idx, from_idx, color, type}); + } + + // Ignoring arcs in the opposite direction + MMU_Graph::Arc get_arc(size_t idx) { return this->arcs[idx * 2]; } + + size_t nodes_count() const { return this->nodes.size(); } + + void remove_nodes_with_one_arc() + { + std::queue update_queue; + for (const MMU_Graph::Node &node : this->nodes) + if (node.neighbours.size() == 1) update_queue.emplace(&node - &this->nodes.front()); + + while (!update_queue.empty()) { + size_t node_from_idx = update_queue.front(); + MMU_Graph::Node &node_from = this->nodes[update_queue.front()]; + update_queue.pop(); + if (node_from.neighbours.empty()) + continue; + + assert(node_from.neighbours.size() == 1); + size_t node_to_idx = node_from.neighbours.front().to_idx; + MMU_Graph::Node &node_to = this->nodes[node_to_idx]; + this->remove_edge(node_from_idx, node_to_idx); + if (node_to.neighbours.size() == 1) + update_queue.emplace(node_to_idx); + } + } + + void add_contours(const std::vector> &color_poly) + { + this->all_border_points = nodes.size(); + this->polygon_sizes = std::vector(color_poly.size()); + for (size_t polygon_idx = 0; polygon_idx < color_poly.size(); ++polygon_idx) + this->polygon_sizes[polygon_idx] = color_poly[polygon_idx].size(); + this->polygon_idx_offset = std::vector(color_poly.size()); + this->polygon_idx_offset[0] = 0; + for (size_t polygon_idx = 1; polygon_idx < color_poly.size(); ++polygon_idx) { + this->polygon_idx_offset[polygon_idx] = this->polygon_idx_offset[polygon_idx - 1] + color_poly[polygon_idx - 1].size(); + } + + size_t poly_idx = 0; + for (const std::vector &color_lines : color_poly) { + size_t line_idx = 0; + for (const ColoredLine &color_line : color_lines) { + size_t from_idx = this->get_global_index(poly_idx, line_idx); + size_t to_idx = this->get_global_index(poly_idx, (line_idx + 1) % color_lines.size()); + this->append_edge(from_idx, to_idx, color_line.color, ARC_TYPE::BORDER); + ++line_idx; + } + ++poly_idx; + } + } + + // Nodes 0..all_border_points are only one with are on countour. Other vertexis are consider as not on coouter. So we check if base on attach index + inline bool is_vertex_on_contour(const Voronoi::VD::vertex_type *vertex) const + { + assert(vertex != nullptr); + return vertex->color() < this->all_border_points; + } + + inline bool is_edge_attach_to_contour(const voronoi_diagram::const_edge_iterator &edge_iterator) const + { + return this->is_vertex_on_contour(edge_iterator->vertex0()) || this->is_vertex_on_contour(edge_iterator->vertex1()); + } + + inline bool is_edge_connecting_two_contour_vertices(const voronoi_diagram::const_edge_iterator &edge_iterator) const + { + return this->is_vertex_on_contour(edge_iterator->vertex0()) && this->is_vertex_on_contour(edge_iterator->vertex1()); + } +}; + +namespace bg = boost::geometry; +namespace bgm = boost::geometry::model; +namespace bgi = boost::geometry::index; + +// float is needed because for coord_t bgi::intersects throws "bad numeric conversion: positive overflow" +using rtree_point_t = bgm::point; +using rtree_t = bgi::rtree, bgi::rstar<16, 4>>; + +static inline rtree_point_t mk_rtree_point(const Point &pt) { return rtree_point_t(float(pt.x()), float(pt.y())); } + +static inline Point mk_point(const Voronoi::VD::vertex_type *point) { return Point(coord_t(point->x()), coord_t(point->y())); } + +static inline Point mk_point(const Voronoi::Internal::point_type &point) { return Point(coord_t(point.x()), coord_t(point.y())); } + +static inline Point mk_point(const voronoi_diagram::vertex_type &point) { return Point(coord_t(point.x()), coord_t(point.y())); } + +static inline void mark_processed(const voronoi_diagram::const_edge_iterator &edge_iterator) +{ + edge_iterator->color(true); + edge_iterator->twin()->color(true); +} + +// Return true, if "p" is closer to line.a, then line.b +static inline bool is_point_closer_to_beginning_of_line(const Line &line, const Point &p) +{ + return (p - line.a).cast().squaredNorm() < (p - line.b).cast().squaredNorm(); +} + +static inline bool has_same_color(const ColoredLine &cl1, const ColoredLine &cl2) { return cl1.color == cl2.color; } + +// Determines if the line points from the point between two contour lines is pointing inside polygon or outside. +static inline bool points_inside(const Line &contour_first, const Line &contour_second, const Point &new_point) +{ + // Used in points_inside for decision if line leading thought the common point of two lines is pointing inside polygon or outside + auto three_points_inward_normal = [](const Point &left, const Point &middle, const Point &right) -> Vec2d { + assert(left != middle); + assert(middle != right); + return (perp(Point(middle - left)).cast().normalized() + perp(Point(right - middle)).cast().normalized()).normalized(); + }; + + assert(contour_first.b == contour_second.a); + Vec2d inward_normal = three_points_inward_normal(contour_first.a, contour_first.b, contour_second.b); + Vec2d edge_norm = (new_point - contour_first.b).cast().normalized(); + double side = inward_normal.dot(edge_norm); + // assert(side != 0.); + return side > 0.; +} + +static inline bool line_intersection_with_epsilon(const Line &line_to_extend, const Line &other, Point *intersection) +{ + Line extended_line = line_to_extend; + extended_line.extend(15 * SCALED_EPSILON); + return extended_line.intersection(other, intersection); +} + +// For every ColoredLine in lines_colored_out, assign the index of the polygon to which belongs and also the index of this line inside of the polygon. +static inline void init_polygon_indices(const MMU_Graph &graph, + const std::vector> &color_poly, + std::vector &lines_colored_out) +{ + size_t poly_idx = 0; + for (const std::vector &color_lines : color_poly) { + size_t line_idx = 0; + for (size_t color_line_idx = 0; color_line_idx < color_lines.size(); ++color_line_idx) { + size_t from_idx = graph.get_global_index(poly_idx, line_idx); + lines_colored_out[from_idx].poly_idx = int(poly_idx); + lines_colored_out[from_idx].local_line_idx = int(line_idx); + ++line_idx; + } + ++poly_idx; + } +} + +static MMU_Graph build_graph(size_t layer_idx, const std::vector> &color_poly) +{ + Geometry::VoronoiDiagram vd; + std::vector lines_colored = to_lines(color_poly); + Polygons color_poly_tmp = colored_points_to_polygon(color_poly); + const Points points = to_points(color_poly_tmp); + const Lines lines = to_lines(color_poly_tmp); + + boost::polygon::construct_voronoi(lines_colored.begin(), lines_colored.end(), &vd); + MMU_Graph graph; + for (const Point &point : points) + graph.nodes.push_back({point}); + + graph.add_contours(color_poly); + init_polygon_indices(graph, color_poly, lines_colored); + + assert(graph.nodes.size() == lines_colored.size()); + + // All Voronoi vertices are post-processes to merge very close vertices to single. Witch Eliminates issues with intersection edges. + // Also, Voronoi vertices outside of the bounding of input polygons are throw away by marking them. + auto append_voronoi_vertices_to_graph = [&graph, &color_poly_tmp, &vd]() -> void { + auto is_equal_points = [](const Point &p1, const Point &p2) { return p1 == p2 || (p1 - p2).cast().norm() <= 3 * SCALED_EPSILON; }; + + BoundingBox bbox = get_extents(color_poly_tmp); + bbox.offset(SCALED_EPSILON); + // EdgeGrid is used for vertices near to contour and rtree for other vertices + // FIXME Lukas H.: Get rid of EdgeGrid and rtree. Use only one structure for both cases. + EdgeGrid::Grid grid; + grid.set_bbox(bbox); + grid.create(color_poly_tmp, coord_t(scale_(10.))); + rtree_t rtree; + for (const voronoi_diagram::vertex_type &vertex : vd.vertices()) { + vertex.color(-1); + Point vertex_point = mk_point(vertex); + + const Point &first_point = graph.nodes[graph.get_arc(vertex.incident_edge()->cell()->source_index()).from_idx].point; + const Point &second_point = graph.nodes[graph.get_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx].point; + + if (vertex_equal_to_point(&vertex, first_point)) { + assert(vertex.color() != vertex.incident_edge()->cell()->source_index()); + assert(vertex.color() != vertex.incident_edge()->twin()->cell()->source_index()); + vertex.color(graph.get_arc(vertex.incident_edge()->cell()->source_index()).from_idx); + } else if (vertex_equal_to_point(&vertex, second_point)) { + assert(vertex.color() != vertex.incident_edge()->cell()->source_index()); + assert(vertex.color() != vertex.incident_edge()->twin()->cell()->source_index()); + vertex.color(graph.get_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx); + } else if (bbox.contains(vertex_point)) { + EdgeGrid::Grid::ClosestPointResult cp = grid.closest_point_signed_distance(vertex_point, coord_t(3 * SCALED_EPSILON)); + if (cp.valid()) { + size_t global_idx = graph.get_global_index(cp.contour_idx, cp.start_point_idx); + size_t global_idx_next = graph.get_global_index(cp.contour_idx, (cp.start_point_idx + 1) % color_poly_tmp[cp.contour_idx].points.size()); + vertex.color(is_equal_points(vertex_point, graph.nodes[global_idx].point) ? global_idx : global_idx_next); + } else { + if (rtree.empty()) { + rtree.insert(std::make_pair(mk_rtree_point(vertex_point), graph.nodes_count())); + vertex.color(graph.nodes_count()); + graph.nodes.push_back({vertex_point}); + } else { + std::vector> closest; + rtree.query(bgi::nearest(mk_rtree_point(vertex_point), 1), std::back_inserter(closest)); + assert(!closest.empty()); + rtree_point_t r_point = closest.front().first; + Point closest_p(bg::get<0>(r_point), bg::get<1>(r_point)); + if (Line(vertex_point, closest_p).length() > 3 * SCALED_EPSILON) { + rtree.insert(std::make_pair(mk_rtree_point(vertex_point), graph.nodes_count())); + vertex.color(graph.nodes_count()); + graph.nodes.push_back({vertex_point}); + } else { + vertex.color(closest.front().second); + } + } + } + } + } + }; + + append_voronoi_vertices_to_graph(); + + auto get_prev_contour_line = [&lines_colored, &color_poly, &graph](const voronoi_diagram::const_edge_iterator &edge_it) -> ColoredLine { + size_t contour_line_local_idx = lines_colored[edge_it->cell()->source_index()].local_line_idx; + size_t contour_line_size = color_poly[lines_colored[edge_it->cell()->source_index()].poly_idx].size(); + size_t contour_prev_idx = graph.get_global_index(lines_colored[edge_it->cell()->source_index()].poly_idx, + (contour_line_local_idx > 0) ? contour_line_local_idx - 1 : contour_line_size - 1); + return lines_colored[contour_prev_idx]; + }; + + auto get_next_contour_line = [&lines_colored, &color_poly, &graph](const voronoi_diagram::const_edge_iterator &edge_it) -> ColoredLine { + size_t contour_line_local_idx = lines_colored[edge_it->cell()->source_index()].local_line_idx; + size_t contour_line_size = color_poly[lines_colored[edge_it->cell()->source_index()].poly_idx].size(); + size_t contour_next_idx = graph.get_global_index(lines_colored[edge_it->cell()->source_index()].poly_idx, + (contour_line_local_idx + 1) % contour_line_size); + return lines_colored[contour_next_idx]; + }; + + BoundingBox bbox = get_extents(color_poly_tmp); + bbox.offset(scale_(10.)); + const double bbox_dim_max = double(std::max(bbox.size().x(), bbox.size().y())); + + // Make a copy of the input segments with the double type. + std::vector segments; + for (const Line &line : lines) + segments.emplace_back(Voronoi::Internal::point_type(double(line.a(0)), double(line.a(1))), + Voronoi::Internal::point_type(double(line.b(0)), double(line.b(1)))); + + for (auto edge_it = vd.edges().begin(); edge_it != vd.edges().end(); ++edge_it) { + // Skip second half-edge + if (edge_it->cell()->source_index() > edge_it->twin()->cell()->source_index() || edge_it->color()) + continue; + + if (edge_it->is_infinite()) { + // Infinite edge is leading through a point on the counter, but there are no Voronoi vertices. + // So we could fix this case by computing the intersection between the contour line and infinity edge. + std::vector samples; + Voronoi::Internal::clip_infinite_edge(points, segments, *edge_it, bbox_dim_max, &samples); + if (samples.empty()) + continue; + + const Line edge_line(mk_point(samples[0]), mk_point(samples[1])); + const ColoredLine &contour_line = lines_colored[edge_it->cell()->source_index()]; + Point contour_intersection; + + if (line_intersection_with_epsilon(contour_line.line, edge_line, &contour_intersection)) { + const MMU_Graph::Arc &graph_arc = graph.get_arc(edge_it->cell()->source_index()); + const size_t from_idx = (edge_it->vertex1() != nullptr) ? edge_it->vertex1()->color() : edge_it->vertex0()->color(); + size_t to_idx = ((contour_line.line.a - contour_intersection).cast().squaredNorm() < + (contour_line.line.b - contour_intersection).cast().squaredNorm()) ? + graph_arc.from_idx : + graph_arc.to_idx; + if (from_idx != to_idx && from_idx < graph.nodes_count() && to_idx < graph.nodes_count()) { + graph.append_edge(from_idx, to_idx); + mark_processed(edge_it); + } + } + } else if (edge_it->is_finite()) { + const Point v0 = mk_point(edge_it->vertex0()); + const Point v1 = mk_point(edge_it->vertex1()); + const size_t from_idx = edge_it->vertex0()->color(); + const size_t to_idx = edge_it->vertex1()->color(); + + // Both points are on contour, so skip them. In cases of duplicate Voronoi vertices, skip edges between the same two points. + if (graph.is_edge_connecting_two_contour_vertices(edge_it) || (edge_it->vertex0()->color() == edge_it->vertex1()->color())) continue; + + const Line edge_line(v0, v1); + const Line contour_line = lines_colored[edge_it->cell()->source_index()].line; + const ColoredLine colored_line = lines_colored[edge_it->cell()->source_index()]; + const ColoredLine contour_line_prev = get_prev_contour_line(edge_it); + const ColoredLine contour_line_next = get_next_contour_line(edge_it); + + Point intersection; + if (edge_it->vertex0()->color() >= graph.nodes_count() || edge_it->vertex1()->color() >= graph.nodes_count()) { +// if(edge_it->vertex0()->color() < graph.nodes_count() && !graph.is_vertex_on_contour(edge_it->vertex0())) { +// +// } + if (edge_it->vertex1()->color() < graph.nodes_count() && !graph.is_vertex_on_contour(edge_it->vertex1())) { + Line contour_line_twin = lines_colored[edge_it->twin()->cell()->source_index()].line; + if (line_intersection_with_epsilon(contour_line_twin, edge_line, &intersection)) { + const MMU_Graph::Arc &graph_arc = graph.get_arc(edge_it->twin()->cell()->source_index()); + const size_t to_idx_l = is_point_closer_to_beginning_of_line(contour_line_twin, intersection) ? graph_arc.from_idx : + graph_arc.to_idx; + graph.append_edge(edge_it->vertex1()->color(), to_idx_l); + } else if (line_intersection_with_epsilon(contour_line, edge_line, &intersection)) { + const MMU_Graph::Arc &graph_arc = graph.get_arc(edge_it->cell()->source_index()); + const size_t to_idx_l = is_point_closer_to_beginning_of_line(contour_line, intersection) ? graph_arc.from_idx : graph_arc.to_idx; + graph.append_edge(edge_it->vertex1()->color(), to_idx_l); + } + mark_processed(edge_it); + } + } else if (graph.is_edge_attach_to_contour(edge_it)) { + mark_processed(edge_it); + // Skip edges witch connection two points on a contour + if (graph.is_edge_connecting_two_contour_vertices(edge_it)) + continue; + + if (graph.is_vertex_on_contour(edge_it->vertex0())) { + if (is_point_closer_to_beginning_of_line(contour_line, v0)) { + if (!has_same_color(contour_line_prev, colored_line) && points_inside(contour_line_prev.line, contour_line, v1)) { + graph.append_edge(from_idx, to_idx); + } + } else { + if (!has_same_color(contour_line_next, colored_line) && points_inside(contour_line, contour_line_next.line, v1)) { + graph.append_edge(from_idx, to_idx); + } + } + } else { + assert(graph.is_vertex_on_contour(edge_it->vertex1())); + if (is_point_closer_to_beginning_of_line(contour_line, v1)) { + if (!has_same_color(contour_line_prev, colored_line) && points_inside(contour_line_prev.line, contour_line, v0)) { + graph.append_edge(from_idx, to_idx); + } + } else { + if (!has_same_color(contour_line_next, colored_line) && points_inside(contour_line, contour_line_next.line, v0)) { + graph.append_edge(from_idx, to_idx); + } + } + } + } else if (line_intersection_with_epsilon(contour_line, edge_line, &intersection)) { + mark_processed(edge_it); + Point real_v0 = graph.nodes[edge_it->vertex0()->color()].point; + Point real_v1 = graph.nodes[edge_it->vertex1()->color()].point; + + if (is_point_closer_to_beginning_of_line(contour_line, intersection)) { + Line first_part(intersection, real_v0); + Line second_part(intersection, real_v1); + + if (!has_same_color(contour_line_prev, colored_line)) { + if (points_inside(contour_line_prev.line, contour_line, first_part.b)) { + graph.append_edge(edge_it->vertex0()->color(), graph.get_arc(edge_it->cell()->source_index()).from_idx); + } + if (points_inside(contour_line_prev.line, contour_line, second_part.b)) { + graph.append_edge(edge_it->vertex1()->color(), graph.get_arc(edge_it->cell()->source_index()).from_idx); + } + } + } else { + const size_t int_point_idx = graph.get_arc(edge_it->cell()->source_index()).to_idx; + const Point int_point = graph.nodes[int_point_idx].point; + + const Line first_part(int_point, real_v0); + const Line second_part(int_point, real_v1); + + if (!has_same_color(contour_line_next, colored_line)) { + if (points_inside(contour_line, contour_line_next.line, first_part.b)) { + graph.append_edge(edge_it->vertex0()->color(), int_point_idx); + } + if (points_inside(contour_line, contour_line_next.line, second_part.b)) { + graph.append_edge(edge_it->vertex1()->color(), int_point_idx); + } + } + } + } + } + } + + for (auto edge_it = vd.edges().begin(); edge_it != vd.edges().end(); ++edge_it) { + // Skip second half-edge and processed edges + if (edge_it->cell()->source_index() > edge_it->twin()->cell()->source_index() || edge_it->color()) + continue; + + if (edge_it->is_finite() && !bool(edge_it->color()) && edge_it->vertex0()->color() < graph.nodes_count() && + edge_it->vertex1()->color() < graph.nodes_count()) { + // Skip cases, when the edge is between two same vertices, which is in cases two near vertices were merged together. + if (edge_it->vertex0()->color() == edge_it->vertex1()->color()) + continue; + + size_t from_idx = edge_it->vertex0()->color(); + size_t to_idx = edge_it->vertex1()->color(); + graph.append_edge(from_idx, to_idx); + } + mark_processed(edge_it); + } + + graph.remove_nodes_with_one_arc(); + return graph; +} + +static inline Polygon to_polygon(const Lines &lines) +{ + Polygon poly_out; + poly_out.points.reserve(lines.size()); + for (const Line &line : lines) + poly_out.points.emplace_back(line.a); + return poly_out; +} + +// Returns list of polygons and assigned colors. +// It iterates through all nodes on the border between two different colors, and from this point, +// start selection always left most edges for every node to construct CCW polygons. +// Assumes that graph is planar (without self-intersection edges) +static std::vector> extract_colored_segments(MMU_Graph &graph) +{ + // When there is no next arc, then is returned original_arc or edge with is marked as used + auto get_next = [&graph](const Line &process_line, MMU_Graph::Arc &original_arc) -> MMU_Graph::Arc & { + std::vector> sorted_arcs; + for (MMU_Graph::Arc &arc : graph.nodes[original_arc.to_idx].neighbours) { + if (graph.nodes[arc.to_idx].point == process_line.a || arc.used) + continue; + + assert(original_arc.to_idx == arc.from_idx); + Vec2d process_line_vec_n = (process_line.a - process_line.b).cast().normalized(); + Vec2d neighbour_line_vec_n = (graph.nodes[arc.to_idx].point - graph.nodes[arc.from_idx].point).cast().normalized(); + + double angle = ::acos(clamp(-1.0, 1.0, neighbour_line_vec_n.dot(process_line_vec_n))); + if (Slic3r::cross2(neighbour_line_vec_n, process_line_vec_n) < 0.0) + angle = 2.0 * (double) PI - angle; + + sorted_arcs.emplace_back(&arc, angle); + } + + std::sort(sorted_arcs.begin(), sorted_arcs.end(), + [](std::pair &l, std::pair &r) -> bool { return l.second < r.second; }); + + // Try to return left most edge witch is unused + for (auto &sorted_arc : sorted_arcs) + if (!sorted_arc.first->used) + return *sorted_arc.first; + + if (sorted_arcs.empty()) + return original_arc; + + return *(sorted_arcs.front().first); + }; + + std::vector> polygons_segments; + for (MMU_Graph::Node &node : graph.nodes) + for (MMU_Graph::Arc &arc : node.neighbours) + arc.used = false; + + for (size_t node_idx = 0; node_idx < graph.all_border_points; ++node_idx) { + MMU_Graph::Node &node = graph.nodes[node_idx]; + + for (MMU_Graph::Arc &arc : node.neighbours) { + if (arc.type == MMU_Graph::ARC_TYPE::NON_BORDER || arc.used) continue; + + Line process_line(node.point, graph.nodes[arc.to_idx].point); + arc.used = true; + + Lines face_lines; + face_lines.emplace_back(process_line); + Point start_p = process_line.a; + + Line p_vec = process_line; + MMU_Graph::Arc *p_arc = &arc; + do { + MMU_Graph::Arc &next = get_next(p_vec, *p_arc); + face_lines.emplace_back(Line(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point)); + if (next.used) break; + + next.used = true; + p_vec = Line(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point); + p_arc = &next; + } while (graph.nodes[p_arc->to_idx].point != start_p); + + Polygon poly = to_polygon(face_lines); + if (poly.is_counter_clockwise() && poly.is_valid()) + polygons_segments.emplace_back(poly, arc.color); + } + } + return polygons_segments; +} + +// Used in remove_multiple_edges_in_vertices() +// Returns length of edge with is connected to contour. To this length is include other edges with follows it if they are almost straight (with the +// tolerance of 15) And also if node between two subsequent edges is connected only to these two edges. +static inline double compute_edge_length(MMU_Graph &graph, size_t start_idx, MMU_Graph::Arc &start_edge) +{ + for (MMU_Graph::Node &node : graph.nodes) + for (MMU_Graph::Arc &arc : node.neighbours) + arc.used = false; + + start_edge.used = true; + MMU_Graph::Arc *arc = &start_edge; + size_t idx = start_idx; + double line_total_length = Line(graph.nodes[idx].point, graph.nodes[arc->to_idx].point).length(); + while (graph.nodes[arc->to_idx].neighbours.size() == 2) { + bool found = false; + for (MMU_Graph::Arc &arc_n : graph.nodes[arc->to_idx].neighbours) { + if (arc_n.type == MMU_Graph::ARC_TYPE::NON_BORDER && !arc_n.used && arc_n.to_idx != idx) { + Line first_line(graph.nodes[idx].point, graph.nodes[arc->to_idx].point); + Line second_line(graph.nodes[arc->to_idx].point, graph.nodes[arc_n.to_idx].point); + + Vec2d first_line_vec = (first_line.a - first_line.b).cast(); + Vec2d second_line_vec = (second_line.b - second_line.a).cast(); + Vec2d first_line_vec_n = first_line_vec.normalized(); + Vec2d second_line_vec_n = second_line_vec.normalized(); + double angle = ::acos(clamp(-1.0, 1.0, first_line_vec_n.dot(second_line_vec_n))); + if (Slic3r::cross2(first_line_vec_n, second_line_vec_n) < 0.0) + angle = 2.0 * (double) PI - angle; + + if (std::abs(angle - PI) >= (PI / 12)) + continue; + + idx = arc->to_idx; + arc = &arc_n; + + line_total_length += Line(graph.nodes[idx].point, graph.nodes[arc->to_idx].point).length(); + arc_n.used = true; + found = true; + break; + } + } + if (!found) + break; + } + + return line_total_length; +} + +// Used for fixing double Voronoi edges for concave parts of the polygon. +static void remove_multiple_edges_in_vertices(MMU_Graph &graph, const std::vector> &color_poly) +{ + std::vector>> colored_segments = get_all_segments(color_poly); + for (const std::vector> &colored_segment_p : colored_segments) { + size_t poly_idx = &colored_segment_p - &colored_segments.front(); + for (const std::pair &colored_segment : colored_segment_p) { + size_t first_idx = graph.get_global_index(poly_idx, colored_segment.first); + size_t second_idx = graph.get_global_index(poly_idx, (colored_segment.second + 1) % graph.polygon_sizes[poly_idx]); + Line seg_line(graph.nodes[first_idx].point, graph.nodes[second_idx].point); + + if (graph.nodes[first_idx].neighbours.size() >= 3) { + std::vector> arc_to_check; + for (MMU_Graph::Arc &n_arc : graph.nodes[first_idx].neighbours) { + if (n_arc.type == MMU_Graph::ARC_TYPE::NON_BORDER) { + double total_len = compute_edge_length(graph, first_idx, n_arc); + arc_to_check.emplace_back(&n_arc, total_len); + } + } + std::sort(arc_to_check.begin(), arc_to_check.end(), + [](std::pair &l, std::pair &r) -> bool { return l.second > r.second; }); + + while (arc_to_check.size() > 1) { + graph.remove_edge(first_idx, arc_to_check.back().first->to_idx); + arc_to_check.pop_back(); + } + } + } + } +} + +static void cut_segmented_layers(const ConstLayerPtrsAdaptor layers, std::vector>> &segmented_regions, const float cut_width) { + tbb::parallel_for(tbb::blocked_range(0, segmented_regions.size()),[&](const tbb::blocked_range& range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + std::vector> segmented_regions_cuts; + for (const std::pair &colored_expoly : segmented_regions[layer_idx]) { + ExPolygons cut_colored_expoly = diff_ex({colored_expoly.first}, offset_ex(layers[layer_idx]->lslices, cut_width)); + for (const ExPolygon &expoly : cut_colored_expoly) { + segmented_regions_cuts.emplace_back(expoly, colored_expoly.second); + } + } + segmented_regions[layer_idx] = segmented_regions_cuts; + } + }); // end of parallel_for +} + +// Returns MMU segmentation of top and bottom layers based on painting in MMU segmentation gizmo +static inline std::vector> mmu_segmentation_top_and_bottom_layers(const PrintObject &print_object) +{ + const ConstLayerPtrsAdaptor layers = print_object.layers(); + std::vector> triangles_by_color(3); + triangles_by_color.assign(3, std::vector(layers.size())); + for (const ModelVolume *mv : print_object.model_object()->volumes) { + for (const auto ¶ms : {std::make_pair(EnforcerBlockerType::NONE, 0), std::make_pair(EnforcerBlockerType::ENFORCER, 1), + std::make_pair(EnforcerBlockerType::BLOCKER, 2)}) { + const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, params.first); + if (!mv->is_model_part() || custom_facets.indices.empty()) + continue; + + const Transform3f tr = print_object.trafo().cast() * mv->get_matrix().cast(); + for (size_t facet_idx = 0; facet_idx < custom_facets.indices.size(); ++facet_idx) { + float min_z = std::numeric_limits::max(); + float max_z = std::numeric_limits::lowest(); + + std::array facet; + Points projected_facet(3); + for (int p_idx = 0; p_idx < 3; ++p_idx) { + facet[p_idx] = tr * custom_facets.vertices[custom_facets.indices[facet_idx](p_idx)]; + max_z = std::max(max_z, facet[p_idx].z()); + min_z = std::min(min_z, facet[p_idx].z()); + } + + // Sort the vertices by z-axis for simplification of projected_facet on slices + std::sort(facet.begin(), facet.end(), [](const Vec3f &p1, const Vec3f &p2) { return p1.z() < p2.z(); }); + + for (int p_idx = 0; p_idx < 3; ++p_idx) { + projected_facet[p_idx] = Point(scale_(facet[p_idx].x()), scale_(facet[p_idx].y())); + projected_facet[p_idx] = projected_facet[p_idx] - print_object.center_offset(); + } + + ExPolygon triangle = ExPolygon(projected_facet); + + // Find lowest slice not below the triangle. + auto first_layer = std::upper_bound(layers.begin(), layers.end(), float(min_z - EPSILON), + [](float z, const Layer *l1) { return z < l1->slice_z + l1->height * 0.5; }); + auto last_layer = std::upper_bound(layers.begin(), layers.end(), float(max_z - EPSILON), + [](float z, const Layer *l1) { return z < l1->slice_z + l1->height * 0.5; }); + + if (last_layer == layers.end()) + --last_layer; + + if (first_layer == layers.end() || (first_layer != layers.begin() && facet[0].z() < (*first_layer)->print_z - EPSILON)) + --first_layer; + + for (auto layer_it = first_layer; (layer_it != (last_layer + 1) && layer_it != layers.end()); ++layer_it) { + size_t layer_idx = layer_it - layers.begin(); + triangles_by_color[params.second][layer_idx].emplace_back(triangle); + } + } + } + } + + auto get_extrusion_width = [&layers = std::as_const(layers)](const size_t layer_idx) -> float { + auto extrusion_width_it = std::max_element(layers[layer_idx]->regions().begin(), layers[layer_idx]->regions().end(), + [](const LayerRegion *l1, const LayerRegion *l2) { + return l1->region()->config().perimeter_extrusion_width < + l2->region()->config().perimeter_extrusion_width; + }); + assert(extrusion_width_it != layers[layer_idx]->regions().end()); + return float((*extrusion_width_it)->region()->config().perimeter_extrusion_width); + }; + + auto get_top_solid_layers = [&layers = std::as_const(layers)](const size_t layer_idx) -> int { + auto top_solid_layer_it = std::max_element(layers[layer_idx]->regions().begin(), layers[layer_idx]->regions().end(), + [](const LayerRegion *l1, const LayerRegion *l2) { + return l1->region()->config().top_solid_layers < l2->region()->config().top_solid_layers; + }); + assert(top_solid_layer_it != layers[layer_idx]->regions().end()); + return (*top_solid_layer_it)->region()->config().top_solid_layers; + }; + + auto get_bottom_solid_layers = [&layers = std::as_const(layers)](const size_t layer_idx) -> int { + auto top_bottom_layer_it = std::max_element(layers[layer_idx]->regions().begin(), layers[layer_idx]->regions().end(), + [](const LayerRegion *l1, const LayerRegion *l2) { + return l1->region()->config().bottom_solid_layers < l2->region()->config().bottom_solid_layers; + }); + assert(top_bottom_layer_it != layers[layer_idx]->regions().end()); + return (*top_bottom_layer_it)->region()->config().bottom_solid_layers; + }; + + std::vector top_layers(layers.size()); + top_layers.back() = layers.back()->lslices; + tbb::parallel_for(tbb::blocked_range(1, layers.size()), [&](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + float extrusion_width = 0.1f * float(scale_(get_extrusion_width(layer_idx))); + top_layers[layer_idx - 1] = diff_ex(layers[layer_idx - 1]->lslices, offset_ex(layers[layer_idx]->lslices, extrusion_width)); + } + }); // end of parallel_for + + std::vector bottom_layers(layers.size()); + bottom_layers.front() = layers.front()->lslices; + tbb::parallel_for(tbb::blocked_range(0, layers.size() - 1), [&](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + float extrusion_width = 0.1f * float(scale_(get_extrusion_width(layer_idx))); + bottom_layers[layer_idx + 1] = diff_ex(layers[layer_idx + 1]->lslices, offset_ex(layers[layer_idx]->lslices, extrusion_width)); + } + }); // end of parallel_for + + tbb::parallel_for(tbb::blocked_range(0, print_object.layers().size()), [&](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + float extrusion_width = 0.1f * float(scale_(get_extrusion_width(layer_idx))); + for (std::vector &triangles : triangles_by_color) { + if (!triangles[layer_idx].empty() && (!top_layers[layer_idx].empty() || !bottom_layers[layer_idx].empty())) { + ExPolygons connected = union_ex(offset_ex(triangles[layer_idx], float(10 * SCALED_EPSILON))); + triangles[layer_idx] = union_ex(offset_ex(offset_ex(connected, -extrusion_width / 1), extrusion_width / 1)); + } else { + triangles[layer_idx].clear(); + } + } + } + }); // end of parallel_for + + std::vector> triangles_by_color_bottom(3); + std::vector> triangles_by_color_top(3); + triangles_by_color_bottom.assign(3, std::vector(layers.size())); + triangles_by_color_top.assign(3, std::vector(layers.size())); + + for (size_t layer_idx = 0; layer_idx < print_object.layers().size(); ++layer_idx) { + BOOST_LOG_TRIVIAL(debug) << "MMU segmentation of top layer: " << layer_idx; + float extrusion_width = scale_(get_extrusion_width(layer_idx)); + int top_solid_layers = get_top_solid_layers(layer_idx); + ExPolygons top_expolygon = top_layers[layer_idx]; + if (top_expolygon.empty()) + continue; + + for (size_t color_idx = 0; color_idx < triangles_by_color.size(); ++color_idx) { + if (triangles_by_color[color_idx][layer_idx].empty()) + continue; + ExPolygons intersection_poly = intersection_ex(triangles_by_color[color_idx][layer_idx], top_expolygon); + if (!intersection_poly.empty()) { + triangles_by_color_top[color_idx][layer_idx].insert(triangles_by_color_top[color_idx][layer_idx].end(), intersection_poly.begin(), + intersection_poly.end()); + for (int last_idx = int(layer_idx) - 1; last_idx >= std::max(int(layer_idx - top_solid_layers), int(0)); --last_idx) { + float offset_value = float(layer_idx - last_idx) * (-1.0f) * extrusion_width; + if (offset_ex(top_expolygon, offset_value).empty()) + continue; + ExPolygons layer_slices_trimmed = layers[last_idx]->lslices; + + for (int last_idx_1 = last_idx; last_idx_1 < int(layer_idx); ++last_idx_1) { + layer_slices_trimmed = intersection_ex(layer_slices_trimmed, layers[last_idx_1 + 1]->lslices); + } + + ExPolygons offset_e = offset_ex(layer_slices_trimmed, offset_value); + ExPolygons intersection_poly_2 = intersection_ex(triangles_by_color_top[color_idx][layer_idx], offset_e); + triangles_by_color_top[color_idx][last_idx].insert(triangles_by_color_top[color_idx][last_idx].end(), intersection_poly_2.begin(), + intersection_poly_2.end()); + } + } + } + } + + for (size_t layer_idx = 0; layer_idx < print_object.layers().size(); ++layer_idx) { + BOOST_LOG_TRIVIAL(debug) << "MMU segmentation of bottom layer: " << layer_idx; + float extrusion_width = scale_(get_extrusion_width(layer_idx)); + int bottom_solid_layers = get_bottom_solid_layers(layer_idx); + ExPolygons bottom_expolygon = bottom_layers[layer_idx]; + if (bottom_expolygon.empty()) + continue; + + for (size_t color_idx = 0; color_idx < triangles_by_color.size(); ++color_idx) { + if (triangles_by_color[color_idx][layer_idx].empty()) + continue; + + ExPolygons intersection_poly = intersection_ex(triangles_by_color[color_idx][layer_idx], bottom_expolygon); + if (!intersection_poly.empty()) { + triangles_by_color_bottom[color_idx][layer_idx].insert(triangles_by_color_bottom[color_idx][layer_idx].end(), intersection_poly.begin(), + intersection_poly.end()); + for (size_t last_idx = layer_idx + 1; last_idx < std::min(layer_idx + bottom_solid_layers, layers.size()); ++last_idx) { + float offset_value = float(last_idx - layer_idx) * (-1.0f) * extrusion_width; + if (offset_ex(bottom_expolygon, offset_value).empty()) + continue; + ExPolygons layer_slices_trimmed = layers[last_idx]->lslices; + for (int last_idx_1 = int(last_idx); last_idx_1 > int(layer_idx); --last_idx_1) { + layer_slices_trimmed = intersection_ex(layer_slices_trimmed, offset_ex(layers[last_idx_1 - 1]->lslices, offset_value)); + } + + ExPolygons offset_e = offset_ex(layer_slices_trimmed, offset_value); + ExPolygons intersection_poly_2 = intersection_ex(triangles_by_color_bottom[color_idx][layer_idx], offset_e); + triangles_by_color_bottom[color_idx][last_idx].insert(triangles_by_color_bottom[color_idx][last_idx].end(), intersection_poly_2.begin(), + intersection_poly_2.end()); + } + } + } + } + + std::vector> triangles_by_color_merged(3); + triangles_by_color_merged.assign(3, std::vector(layers.size())); + for (size_t layer_idx = 0; layer_idx < layers.size(); ++layer_idx) { + for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) { + triangles_by_color_merged[color_idx][layer_idx].insert(triangles_by_color_merged[color_idx][layer_idx].end(), + triangles_by_color_bottom[color_idx][layer_idx].begin(), + triangles_by_color_bottom[color_idx][layer_idx].end()); + triangles_by_color_merged[color_idx][layer_idx].insert(triangles_by_color_merged[color_idx][layer_idx].end(), + triangles_by_color_top[color_idx][layer_idx].begin(), + triangles_by_color_top[color_idx][layer_idx].end()); + triangles_by_color_merged[color_idx][layer_idx] = union_ex(triangles_by_color_merged[color_idx][layer_idx]); + } + + // Cut all colors for cases when two colors are overlapping + for (size_t color_idx = 1; color_idx < triangles_by_color_merged.size(); ++color_idx) { + triangles_by_color_merged[color_idx][layer_idx] = diff_ex(triangles_by_color_merged[color_idx][layer_idx], + triangles_by_color_merged[color_idx - 1][layer_idx]); + } + } + + return triangles_by_color_merged; +} + +static std::vector>> merge_segmented_layers( + const std::vector>> &segmented_regions, const std::vector> &top_and_bottom_layers) +{ + std::vector>> segmented_regions_merged(segmented_regions.size()); + + tbb::parallel_for(tbb::blocked_range(0, segmented_regions.size()), [&](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - merging region: " << layer_idx; + for (const std::pair &colored_expoly : segmented_regions[layer_idx]) { + ExPolygons cut_colored_expoly = {colored_expoly.first}; + for (const std::vector &top_and_bottom_layer : top_and_bottom_layers) + cut_colored_expoly = diff_ex(cut_colored_expoly, top_and_bottom_layer[layer_idx]); + for (ExPolygon &ex_poly : cut_colored_expoly) + segmented_regions_merged[layer_idx].emplace_back(ex_poly, colored_expoly.second); + + } + + for (size_t color_idx = 0; color_idx < top_and_bottom_layers.size(); ++color_idx) { + ExPolygons top_and_bottom_expoly = top_and_bottom_layers[color_idx][layer_idx]; + for (const ExPolygon &expoly : top_and_bottom_expoly) { segmented_regions_merged[layer_idx].emplace_back(expoly, color_idx); } + } + } + }); // end of parallel_for + + return segmented_regions_merged; +} + +std::vector>> multi_material_segmentation_by_painting(const PrintObject &print_object) +{ + std::vector>> segmented_regions(print_object.layers().size()); + std::vector> painted_lines(print_object.layers().size()); + std::vector edge_grids(print_object.layers().size()); + const ConstLayerPtrsAdaptor layers = print_object.layers(); + + for (size_t layer_idx = 0; layer_idx < layers.size(); ++layer_idx) { + const Layer *layer = layers[layer_idx]; + BoundingBox bbox(get_extents(layer->lslices)); + bbox.offset(SCALED_EPSILON); + edge_grids[layer_idx].set_bbox(bbox); + edge_grids[layer_idx].create(layer->lslices, coord_t(scale_(10.))); + } + + for (const ModelVolume *mv : print_object.model_object()->volumes) { + for (const auto ¶ms : {std::make_pair(EnforcerBlockerType::ENFORCER, 1), std::make_pair(EnforcerBlockerType::BLOCKER, 2)}) { + const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, params.first); + if (!mv->is_model_part() || custom_facets.indices.empty()) + continue; + + const Transform3f tr = print_object.trafo().cast() * mv->get_matrix().cast(); + for (size_t facet_idx = 0; facet_idx < custom_facets.indices.size(); ++facet_idx) { + float min_z = std::numeric_limits::max(); + float max_z = std::numeric_limits::lowest(); + + std::array facet; + Points projected_facet(3); + for (int p_idx = 0; p_idx < 3; ++p_idx) { + facet[p_idx] = tr * custom_facets.vertices[custom_facets.indices[facet_idx](p_idx)]; + max_z = std::max(max_z, facet[p_idx].z()); + min_z = std::min(min_z, facet[p_idx].z()); + } + + // Sort the vertices by z-axis for simplification of projected_facet on slices + std::sort(facet.begin(), facet.end(), [](const Vec3f &p1, const Vec3f &p2) { return p1.z() < p2.z(); }); + + for (int p_idx = 0; p_idx < 3; ++p_idx) { + projected_facet[p_idx] = Point(scale_(facet[p_idx].x()), scale_(facet[p_idx].y())); + projected_facet[p_idx] = projected_facet[p_idx] - print_object.center_offset(); + } + + ExPolygon triangle = ExPolygon(projected_facet); + + // Find lowest slice not below the triangle. + auto first_layer = std::upper_bound(print_object.layers().begin(), print_object.layers().end(), float(min_z - EPSILON), + [](float z, const Layer *l1) { return z < l1->slice_z; }); + auto last_layer = std::upper_bound(print_object.layers().begin(), print_object.layers().end(), float(max_z + EPSILON), + [](float z, const Layer *l1) { return z < l1->slice_z; }); + --last_layer; + + for (auto layer_it = first_layer; layer_it != (last_layer + 1); ++layer_it) { + const Layer *layer = *layer_it; + size_t layer_idx = layer_it - print_object.layers().begin(); + if (facet[0].z() > layer->slice_z || layer->slice_z > facet[2].z()) + continue; + + // https://kandepet.com/3d-printing-slicing-3d-objects/ + float t = (float(layer->slice_z) - facet[0].z()) / (facet[2].z() - facet[0].z()); + Vec3f line_start_f = facet[0] + t * (facet[2] - facet[0]); + Vec3f line_end_f; + + if (facet[1].z() > layer->slice_z) { + // [P0, P2] a [P0, P1] + float t1 = (float(layer->slice_z) - facet[0].z()) / (facet[1].z() - facet[0].z()); + line_end_f = facet[0] + t1 * (facet[1] - facet[0]); + } else if (facet[1].z() <= layer->slice_z) { + // [P0, P2] a [P1, P2] + float t2 = (float(layer->slice_z) - facet[1].z()) / (facet[2].z() - facet[1].z()); + line_end_f = facet[1] + t2 * (facet[2] - facet[1]); + } + + Point line_start(scale_(line_start_f.x()), scale_(line_start_f.y())); + Point line_end(scale_(line_end_f.x()), scale_(line_end_f.y())); + line_start -= print_object.center_offset(); + line_end -= print_object.center_offset(); + + std::vector painted_line_tmp; + PaintedLineVisitor visitor(edge_grids[layer_idx], painted_line_tmp); + visitor.reset(); + visitor.line_to_test.a = line_start; + visitor.line_to_test.b = line_end; + visitor.color = params.second; + edge_grids[layer_idx].visit_cells_intersecting_line(line_start, line_end, visitor); + + if (!painted_line_tmp.empty()) + painted_lines[layer_idx].insert(painted_lines[layer_idx].end(), painted_line_tmp.begin(), painted_line_tmp.end()); + } + } + } + } + + tbb::parallel_for(tbb::blocked_range(0, print_object.layers().size()), [&](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + // for(size_t layer_idx = 0; layer_idx < print_object.layers().size(); ++layer_idx) { + BOOST_LOG_TRIVIAL(debug) << "MMU segmentation of layer: " << layer_idx; + auto comp = [&edge_grids, &layer_idx](const PaintedLine &first, const PaintedLine &second) { + Point first_start_p = *(edge_grids[layer_idx].contours()[first.contour_idx].begin() + first.line_idx); + + return first.contour_idx < second.contour_idx || + (first.contour_idx == second.contour_idx && + (first.line_idx < second.line_idx || + (first.line_idx == second.line_idx && + Line(first_start_p, first.projected_line.a).length() < Line(first_start_p, second.projected_line.a).length()))); + }; + + std::sort(painted_lines[layer_idx].begin(), painted_lines[layer_idx].end(), comp); + std::vector &painted_lines_single = painted_lines[layer_idx]; + + if (!painted_lines_single.empty()) { + Polygons original_polygons; + for (const Slic3r::EdgeGrid::Contour &contour : edge_grids[layer_idx].contours()) { + Points points; + for (const Point &point : contour) points.emplace_back(point); + original_polygons.emplace_back(points); + } + + std::vector> color_poly = colorize_polygons(original_polygons, painted_lines_single); + MMU_Graph graph = build_graph(layer_idx, color_poly); + remove_multiple_edges_in_vertices(graph, color_poly); + graph.remove_nodes_with_one_arc(); + std::vector> segmentation = extract_colored_segments(graph); + for (const std::pair ®ion : segmentation) + segmented_regions[layer_idx].emplace_back(region); + } + } + }); // end of parallel_for + + if (auto w = print_object.print()->config().mmu_segmented_region_max_width; w > 0.f) + cut_segmented_layers(layers, segmented_regions, float(-scale_(w))); + +// return segmented_regions; + std::vector> top_and_bottom_layers = mmu_segmentation_top_and_bottom_layers(print_object); + std::vector>> segmented_regions_merged = merge_segmented_layers(segmented_regions, top_and_bottom_layers); + return segmented_regions_merged; +} + +} // namespace Slic3r diff --git a/src/libslic3r/MultiMaterialSegmentation.hpp b/src/libslic3r/MultiMaterialSegmentation.hpp new file mode 100644 index 0000000000..213744f15c --- /dev/null +++ b/src/libslic3r/MultiMaterialSegmentation.hpp @@ -0,0 +1,18 @@ +#ifndef slic3r_MultiMaterialSegmentation_hpp_ +#define slic3r_MultiMaterialSegmentation_hpp_ + +#include +#include + +namespace Slic3r { + + +class PrintObject; +class ExPolygon; + +// Returns MMU segmentation based on painting in MMU segmentation gizmo +std::vector>> multi_material_segmentation_by_painting(const PrintObject &print_object); + +} // namespace Slic3r + +#endif // slic3r_MultiMaterialSegmentation_hpp_ diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 6903fc6087..91f86d0105 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -240,11 +240,6 @@ public: // Helpers to project custom facets on slices void project_and_append_custom_facets(bool seam, EnforcerBlockerType type, std::vector& expolys) const; - // Returns MMU segmentation based on painting in MMU segmentation gizmo - std::vector>> mmu_segmentation_by_painting(); - // Returns MMU segmentation of top and bottom layers based on painting in MMU segmentation gizmo - std::vector> mmu_segmentation_top_and_bottom_layers(); - private: // to be called from Print only. friend class Print; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index b59db7733f..f85568ba59 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -6,6 +6,7 @@ #include "Geometry.hpp" #include "I18N.hpp" #include "Layer.hpp" +#include "MultiMaterialSegmentation.hpp" #include "SupportMaterial.hpp" #include "Surface.hpp" #include "Slicing.hpp" @@ -13,25 +14,16 @@ #include "Utils.hpp" #include "Fill/FillAdaptive.hpp" #include "Format/STL.hpp" -#include "EdgeGrid.hpp" -#include "VoronoiVisualUtils.hpp" -#include "VoronoiOffset.hpp" #include #include #include -#include #include #include #include -#include -#include -#include -#include - //! macro used to mark string used at localization, //! return same string #define L(s) Slic3r::I18N::translate(s) @@ -52,31 +44,6 @@ #include #endif -namespace Slic3r { -struct ColoredLine { - Line line; - int color; - int poly_idx = -1; - int local_line_idx = -1; -}; -} - -#include -namespace boost { namespace polygon { -template <> -struct geometry_concept { typedef segment_concept type; }; - -template <> -struct segment_traits { - typedef coord_t coordinate_type; - typedef Slic3r::Point point_type; - - static inline point_type get(const Slic3r::ColoredLine& line, direction_1d dir) { - return dir.to_int() ? line.line.b : line.line.a; - } -}; -} } - namespace Slic3r { // Constructor is called from the main thread, therefore all Model / ModelObject / ModelIntance data are valid. @@ -1949,7 +1916,7 @@ void PrintObject::_slice(const std::vector &layer_height_profile) } size_t region_count_before_change = this->region_volumes.size(); - std::vector>> segmented_regions = this->mmu_segmentation_by_painting(); + std::vector>> segmented_regions = multi_material_segmentation_by_painting(*this); // Skip region with default extruder for (size_t region_idx = 1; region_idx < 3; ++region_idx) { std::vector c_layers(m_layers.size()); @@ -3122,1454 +3089,4 @@ const Layer *PrintObject::get_first_layer_bellow_printz(coordf_t print_z, coordf return (it == m_layers.begin()) ? nullptr : *(--it); } -// --------------------MMU_START---------------------- -// Assumes that is at most same projected_l length or below than projection_l -static bool project_line_on_line(const Line &projection_l, const Line &projected_l, Line *new_projected) -{ - const Vec2d v1 = (projection_l.b - projection_l.a).cast(); - const Vec2d va = (projected_l.a - projection_l.a).cast(); - const Vec2d vb = (projected_l.b - projection_l.a).cast(); - const double l2 = v1.squaredNorm(); // avoid a sqrt - if (l2 == 0.0) - return false; - double t1 = va.dot(v1) / l2; - double t2 = vb.dot(v1) / l2; - t1 = std::clamp(t1, 0., 1.); - t2 = std::clamp(t2, 0., 1.); - assert(t1 >= 0.); - assert(t2 >= 0.); - assert(t1 <= 1.); - assert(t2 <= 1.); - - Point p1 = (projection_l.a.cast() + t1 * v1).cast(); - Point p2 = (projection_l.a.cast() + t2 * v1).cast(); - *new_projected = Line(p1, p2); - return true; -} - -struct PaintedLine -{ - size_t contour_idx; - size_t line_idx; - Line projected_line; - int color = 1; -}; - -struct PaintedLineVisitor -{ - PaintedLineVisitor(const EdgeGrid::Grid &grid, std::vector &painted_lines) : grid(grid), painted_lines(painted_lines) - { - painted_lines_set.reserve(painted_lines.capacity()); - } - - void reset() { painted_lines_set.clear(); } - - bool operator()(coord_t iy, coord_t ix) - { - // Called with a row and column of the grid cell, which is intersected by a line. - auto cell_data_range = grid.cell_data_range(iy, ix); - for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++it_contour_and_segment) { - Line grid_line = grid.line(*it_contour_and_segment); - - const Vec2d v1 = (line_to_test.b - line_to_test.a).cast().normalized(); - const Vec2d v2 = (grid_line.b - grid_line.a).cast().normalized(); - double angle = ::acos(clamp(-1.0, 1.0, v1.dot(v2))); - double angle_deg = Geometry::rad2deg(angle); - // When lines have too different length, it is necessary to normalize them - if ((angle_deg >= 0 && angle_deg <= 30) || (angle_deg >= 150)) { - Line line_to_test_projected; - project_line_on_line(grid_line, line_to_test, &line_to_test_projected); - - if (painted_lines_set.find(*it_contour_and_segment) == painted_lines_set.end()) { - if (Line(grid_line.a, line_to_test_projected.a).length() > Line(grid_line.a, line_to_test_projected.b).length()) { - line_to_test_projected.reverse(); - } - - double dist_1 = grid_line.distance_to(line_to_test.a); - double dist_2 = grid_line.distance_to(line_to_test.b); - double dist_3 = line_to_test.distance_to(grid_line.a); - double dist_4 = line_to_test.distance_to(grid_line.b); - double total_dist = std::min(std::min(dist_1, dist_2), std::min(dist_3, dist_4)); - - if (total_dist > 50 * SCALED_EPSILON) - continue; - - painted_lines.push_back({it_contour_and_segment->first, it_contour_and_segment->second, line_to_test_projected, this->color}); - painted_lines_set.insert(*it_contour_and_segment); - } - } - } - // Continue traversing the grid along the edge. - return true; - } - - const EdgeGrid::Grid &grid; - std::vector &painted_lines; - Line line_to_test; - std::unordered_set, boost::hash>> painted_lines_set; - int color = -1; -}; - -static std::vector to_colored_lines(const Polygon &polygon, int color) -{ - std::vector lines; - lines.reserve(polygon.points.size()); - if (polygon.points.size() > 2) { - for (auto it = polygon.points.begin(); it != polygon.points.end() - 1; ++it) - lines.push_back({Line(*it, *(it + 1)), color}); - lines.push_back({Line(polygon.points.back(), polygon.points.front()), color}); - } - return lines; -} - -static Polygon colored_points_to_polygon(const std::vector &lines) -{ - Points out; - for (const ColoredLine &l : lines) - out.emplace_back(l.line.a); - return Polygon(out); -} - -static Polygons colored_points_to_polygon(const std::vector> &lines) -{ - Polygons out; - for (const std::vector &l : lines) - out.emplace_back(colored_points_to_polygon(l)); - return out; -} - -inline std::vector to_lines(const std::vector> &c_lines) -{ - size_t n_lines = 0; - for (const auto &c_line : c_lines) - n_lines += c_line.size(); - std::vector lines; - lines.reserve(n_lines); - for (const auto &c_line : c_lines) - lines.insert(lines.end(), c_line.begin(), c_line.end()); - return lines; -} - -// Double vertex equal to a coord_t point after conversion to double. -template -inline bool vertex_equal_to_point(const VertexType &vertex, const Point &ipt) -{ - // Convert ipt to doubles, force the 80bit FPU temporary to 64bit and then compare. - // This should work with any settings of math compiler switches and the C++ compiler - // shall understand the memcpies as type punning and it shall optimize them out. - using ulp_cmp_type = boost::polygon::detail::ulp_comparison; - ulp_cmp_type ulp_cmp; - static constexpr int ULPS = boost::polygon::voronoi_diagram_traits::vertex_equality_predicate_type::ULPS; - return ulp_cmp(vertex.x(), double(ipt.x()), ULPS) == ulp_cmp_type::EQUAL && - ulp_cmp(vertex.y(), double(ipt.y()), ULPS) == ulp_cmp_type::EQUAL; -} - -bool vertex_equal_to_point(const Voronoi::VD::vertex_type *vertex, const Point &ipt) { - return vertex_equal_to_point(*vertex, ipt); -} - -static std::vector> get_segments(const std::vector &polygon) -{ - std::vector> segments; - - size_t segment_end = 0; - while (segment_end + 1 < polygon.size() && polygon[segment_end].color == polygon[segment_end + 1].color) - segment_end++; - - if (segment_end == polygon.size() - 1) - return {std::make_pair(0, polygon.size() - 1)}; - - size_t first_different_color = (segment_end + 1) % polygon.size(); - for (size_t line_offset_idx = 0; line_offset_idx < polygon.size(); ++line_offset_idx) { - size_t start_s = (first_different_color + line_offset_idx) % polygon.size(); - size_t end_s = start_s; - - while (line_offset_idx + 1 < polygon.size() && polygon[start_s].color == polygon[(first_different_color + line_offset_idx + 1) % polygon.size()].color) { - end_s = (first_different_color + line_offset_idx + 1) % polygon.size(); - line_offset_idx++; - } - segments.emplace_back(start_s, end_s); - } - return segments; -} - -static std::vector>> get_all_segments(const std::vector> &color_poly) -{ - std::vector>> all_segments(color_poly.size()); - for (size_t poly_idx = 0; poly_idx < color_poly.size(); ++poly_idx) { - const std::vector &c_polygon = color_poly[poly_idx]; - all_segments[poly_idx] = get_segments(c_polygon); - } - return all_segments; -} - -static std::vector colorize_line(const Line & line_to_process, - const size_t start_idx, - const size_t end_idx, - std::vector &painted_lines) -{ - std::vector internal_painted; - for (size_t line_idx = start_idx; line_idx <= end_idx; ++line_idx) { internal_painted.emplace_back(painted_lines[line_idx]); } - const int filter_eps_value = scale_(0.1f); - std::vector filtered_lines; - filtered_lines.emplace_back(internal_painted.front()); - for (size_t line_idx = 1; line_idx < internal_painted.size(); ++line_idx) { - PaintedLine &prev = filtered_lines.back(); - PaintedLine &curr = internal_painted[line_idx]; - - double prev_length = prev.projected_line.length(); - double curr_dist_start = (curr.projected_line.a - prev.projected_line.a).cast().norm(); - double dist_between_lines = curr_dist_start - prev_length; - - if (dist_between_lines >= 0) { - if (prev.color == curr.color) { - if (dist_between_lines <= filter_eps_value) { - prev.projected_line.b = curr.projected_line.b; - } else { - filtered_lines.emplace_back(curr); - } - } else { - filtered_lines.emplace_back(curr); - } - } else { - double curr_dist_end = (curr.projected_line.b - prev.projected_line.a).cast().norm(); - if (curr_dist_end <= prev_length) { - } else { - if (prev.color == curr.color) { - prev.projected_line.b = curr.projected_line.b; - } else { - curr.projected_line.a = prev.projected_line.b; - filtered_lines.emplace_back(curr); - } - } - } - } - - std::vector final_lines; - double dist_to_start = (filtered_lines.front().projected_line.a - line_to_process.a).cast().norm(); - if (dist_to_start <= filter_eps_value) { - filtered_lines.front().projected_line.a = line_to_process.a; - final_lines.push_back({filtered_lines.front().projected_line, filtered_lines.front().color}); - } else { - final_lines.push_back({Line(line_to_process.a, filtered_lines.front().projected_line.a), 0}); - final_lines.push_back({filtered_lines.front().projected_line, filtered_lines.front().color}); - } - - for (size_t line_idx = 1; line_idx < filtered_lines.size(); ++line_idx) { - ColoredLine &prev = final_lines.back(); - PaintedLine &curr = filtered_lines[line_idx]; - - double line_dist = (curr.projected_line.a - prev.line.b).cast().norm(); - if (line_dist <= filter_eps_value) { - if (prev.color == curr.color) { - prev.line.b = curr.projected_line.b; - } else { - prev.line.b = curr.projected_line.a; - final_lines.push_back({curr.projected_line, curr.color}); - } - } else { - final_lines.push_back({Line(prev.line.b, curr.projected_line.a), 0}); - final_lines.push_back({curr.projected_line, curr.color}); - } - } - - double dist_to_end = (final_lines.back().line.b - line_to_process.b).cast().norm(); - if (dist_to_end <= filter_eps_value) - final_lines.back().line.b = line_to_process.b; - else - final_lines.push_back({Line(final_lines.back().line.b, line_to_process.b), 0}); - - for (size_t line_idx = 1; line_idx < final_lines.size(); ++line_idx) - assert(final_lines[line_idx - 1].line.b == final_lines[line_idx].line.a); - - for (size_t line_idx = 2; line_idx < final_lines.size(); ++line_idx) { - const ColoredLine &line_0 = final_lines[line_idx - 2]; - ColoredLine & line_1 = final_lines[line_idx - 1]; - const ColoredLine &line_2 = final_lines[line_idx - 0]; - - if (line_0.color == line_2.color && line_0.color != line_1.color) - if (line_1.line.length() <= scale_(0.2)) line_1.color = line_0.color; - } - - std::vector colored_lines_simpl; - colored_lines_simpl.emplace_back(final_lines.front()); - for (size_t line_idx = 1; line_idx < final_lines.size(); ++line_idx) { - const ColoredLine &line_0 = final_lines[line_idx]; - - if (colored_lines_simpl.back().color == line_0.color) - colored_lines_simpl.back().line.b = line_0.line.b; - else - colored_lines_simpl.emplace_back(line_0); - } - - final_lines = colored_lines_simpl; - - if (final_lines.size() > 1) { - if (final_lines.front().color != final_lines[1].color && final_lines.front().line.length() <= scale_(0.2)) { - final_lines[1].line.a = final_lines.front().line.a; - final_lines.erase(final_lines.begin()); - } - } - - if (final_lines.size() > 1) { - if (final_lines.back().color != final_lines[final_lines.size() - 2].color && final_lines.back().line.length() <= scale_(0.2)) { - final_lines[final_lines.size() - 2].line.b = final_lines.back().line.b; - final_lines.pop_back(); - } - } - - return final_lines; -} - -static std::vector colorize_polygon(const Polygon &poly, const size_t start_idx, const size_t end_idx, std::vector &painted_lines) -{ - std::vector new_lines; - Lines lines = poly.lines(); - - for (size_t idx = 0; idx < painted_lines[start_idx].line_idx; ++idx) - new_lines.emplace_back(ColoredLine{lines[idx], 0}); - - for (size_t first_idx = start_idx; first_idx <= end_idx; ++first_idx) { - size_t second_idx = first_idx; - while (second_idx <= end_idx && painted_lines[first_idx].line_idx == painted_lines[second_idx].line_idx) ++second_idx; - --second_idx; - - assert(painted_lines[first_idx].line_idx == painted_lines[second_idx].line_idx); - std::vector lines_c_line = colorize_line(lines[painted_lines[first_idx].line_idx], first_idx, second_idx, painted_lines); - new_lines.insert(new_lines.end(), lines_c_line.begin(), lines_c_line.end()); - - if (second_idx + 1 <= end_idx) - for (size_t idx = painted_lines[second_idx].line_idx + 1; idx < painted_lines[second_idx + 1].line_idx; ++idx) - new_lines.emplace_back(ColoredLine{lines[idx], 0}); - - first_idx = second_idx; - } - - for (size_t idx = painted_lines[end_idx].line_idx + 1; idx < poly.size(); ++idx) - new_lines.emplace_back(ColoredLine{lines[idx], 0}); - - for (size_t line_idx = 2; line_idx < new_lines.size(); ++line_idx) { - const ColoredLine &line_0 = new_lines[line_idx - 2]; - ColoredLine & line_1 = new_lines[line_idx - 1]; - const ColoredLine &line_2 = new_lines[line_idx - 0]; - - if (line_0.color == line_2.color && line_0.color != line_1.color && line_0.color >= 1) { - if (line_1.line.length() <= scale_(0.5)) line_1.color = line_0.color; - } - } - - for (size_t line_idx = 3; line_idx < new_lines.size(); ++line_idx) { - const ColoredLine &line_0 = new_lines[line_idx - 3]; - ColoredLine & line_1 = new_lines[line_idx - 2]; - ColoredLine & line_2 = new_lines[line_idx - 1]; - const ColoredLine &line_3 = new_lines[line_idx - 0]; - - if (line_0.color == line_3.color && (line_0.color != line_1.color || line_0.color != line_2.color) && line_0.color >= 1 && line_3.color >= 1) { - if ((line_1.line.length() + line_2.line.length()) <= scale_(0.5)) { - line_1.color = line_0.color; - line_2.color = line_0.color; - } - } - } - - std::vector> segments = get_segments(new_lines); - auto segment_length = [&new_lines](const std::pair &segment) { - double total_length = 0; - for (size_t seg_start_idx = segment.first; seg_start_idx != segment.second; seg_start_idx = (seg_start_idx + 1 < new_lines.size()) ? seg_start_idx + 1 : 0) - total_length += new_lines[seg_start_idx].line.length(); - total_length += new_lines[segment.second].line.length(); - return total_length; - }; - - for (size_t pair_idx = 1; pair_idx < segments.size(); ++pair_idx) { - int color0 = new_lines[segments[pair_idx - 1].first].color; - int color1 = new_lines[segments[pair_idx - 0].first].color; - - double seg0l = segment_length(segments[pair_idx - 1]); - double seg1l = segment_length(segments[pair_idx - 0]); - - if (color0 != color1 && seg0l >= scale_(0.1) && seg1l <= scale_(0.2)) { - for (size_t seg_start_idx = segments[pair_idx].first; seg_start_idx != segments[pair_idx].second; seg_start_idx = (seg_start_idx + 1 < new_lines.size()) ? seg_start_idx + 1 : 0) - new_lines[seg_start_idx].color = color0; - new_lines[segments[pair_idx].second].color = color0; - } - } - - segments = get_segments(new_lines); - for (size_t pair_idx = 1; pair_idx < segments.size(); ++pair_idx) { - int color0 = new_lines[segments[pair_idx - 1].first].color; - int color1 = new_lines[segments[pair_idx - 0].first].color; - double seg1l = segment_length(segments[pair_idx - 0]); - - if (color0 >= 1 && color0 != color1 && seg1l <= scale_(0.2)) { - for (size_t seg_start_idx = segments[pair_idx].first; seg_start_idx != segments[pair_idx].second; seg_start_idx = (seg_start_idx + 1 < new_lines.size()) ? seg_start_idx + 1 : 0) - new_lines[seg_start_idx].color = color0; - new_lines[segments[pair_idx].second].color = color0; - } - } - - for (size_t pair_idx = 2; pair_idx < segments.size(); ++pair_idx) { - int color0 = new_lines[segments[pair_idx - 2].first].color; - int color1 = new_lines[segments[pair_idx - 1].first].color; - int color2 = new_lines[segments[pair_idx - 0].first].color; - - if (color0 > 0 && color0 == color2 && color0 != color1 && segment_length(segments[pair_idx - 1]) <= scale_(0.5)) { - for (size_t seg_start_idx = segments[pair_idx].first; seg_start_idx != segments[pair_idx].second; seg_start_idx = (seg_start_idx + 1 < new_lines.size()) ? seg_start_idx + 1 : 0) - new_lines[seg_start_idx].color = color0; - new_lines[segments[pair_idx].second].color = color0; - } - } - - return new_lines; -} - -static std::vector> colorize_polygons(const Polygons &polygons, std::vector &painted_lines) -{ - const size_t start_idx = 0; - const size_t end_idx = painted_lines.size() - 1; - - std::vector> new_polygons; - - for (size_t idx = 0; idx < painted_lines[start_idx].contour_idx; ++idx) - new_polygons.emplace_back(to_colored_lines(polygons[idx], 0)); - - for (size_t first_idx = start_idx; first_idx <= end_idx; ++first_idx) { - size_t second_idx = first_idx; - while (second_idx <= end_idx && painted_lines[first_idx].contour_idx == painted_lines[second_idx].contour_idx) - ++second_idx; - --second_idx; - - assert(painted_lines[first_idx].contour_idx == painted_lines[second_idx].contour_idx); - std::vector polygon_c = colorize_polygon(polygons[painted_lines[first_idx].contour_idx], first_idx, second_idx, painted_lines); - new_polygons.emplace_back(polygon_c); - - if (second_idx + 1 <= end_idx) - for (size_t idx = painted_lines[second_idx].contour_idx + 1; idx < painted_lines[second_idx + 1].contour_idx; ++idx) - new_polygons.emplace_back(to_colored_lines(polygons[idx], 0)); - - first_idx = second_idx; - } - - for (size_t idx = painted_lines[end_idx].contour_idx + 1; idx < polygons.size(); ++idx) - new_polygons.emplace_back(to_colored_lines(polygons[idx], 0)); - - return new_polygons; -} - -using boost::polygon::voronoi_diagram; - -struct MMU_Graph -{ - enum class ARC_TYPE { BORDER, NON_BORDER }; - - struct Arc - { - size_t from_idx; - size_t to_idx; - int color; - ARC_TYPE type; - bool used{false}; - - bool operator==(const Arc &rhs) const { return (from_idx == rhs.from_idx) && (to_idx == rhs.to_idx) && (color == rhs.color) && (type == rhs.type); } - bool operator!=(const Arc &rhs) const { return !operator==(rhs); } - }; - - struct Node - { - Point point; - std::list neighbours; - - void remove_edge(const size_t to_idx) - { - for (auto arc_it = this->neighbours.begin(); arc_it != this->neighbours.end(); ++arc_it) { - if (arc_it->to_idx == to_idx) { - assert(arc_it->type != ARC_TYPE::BORDER); - this->neighbours.erase(arc_it); - break; - } - } - } - }; - - std::vector nodes; - std::vector arcs; - size_t all_border_points{}; - - std::vector polygon_idx_offset; - std::vector polygon_sizes; - - void remove_edge(const size_t from_idx, const size_t to_idx) - { - nodes[from_idx].remove_edge(to_idx); - nodes[to_idx].remove_edge(from_idx); - } - - size_t get_global_index(const size_t poly_idx, const size_t point_idx) const { return polygon_idx_offset[poly_idx] + point_idx; } - - void append_edge(const size_t &from_idx, const size_t &to_idx, int color = -1, ARC_TYPE type = ARC_TYPE::NON_BORDER) - { - // Don't append duplicate edges between the same nodes. - for (const MMU_Graph::Arc &arc : this->nodes[from_idx].neighbours) - if (arc.to_idx == to_idx) - return; - for (const MMU_Graph::Arc &arc : this->nodes[to_idx].neighbours) - if (arc.to_idx == to_idx) - return; - - this->nodes[from_idx].neighbours.push_back({from_idx, to_idx, color, type}); - this->nodes[to_idx].neighbours.push_back({to_idx, from_idx, color, type}); - this->arcs.push_back({from_idx, to_idx, color, type}); - this->arcs.push_back({to_idx, from_idx, color, type}); - } - - // Ignoring arcs in the opposite direction - MMU_Graph::Arc get_arc(size_t idx) { return this->arcs[idx * 2]; } - - size_t nodes_count() const { return this->nodes.size(); } - - void remove_nodes_with_one_arc() - { - std::queue update_queue; - for (const MMU_Graph::Node &node : this->nodes) - if (node.neighbours.size() == 1) update_queue.emplace(&node - &this->nodes.front()); - - while (!update_queue.empty()) { - size_t node_from_idx = update_queue.front(); - MMU_Graph::Node &node_from = this->nodes[update_queue.front()]; - update_queue.pop(); - if (node_from.neighbours.empty()) - continue; - - assert(node_from.neighbours.size() == 1); - size_t node_to_idx = node_from.neighbours.front().to_idx; - MMU_Graph::Node &node_to = this->nodes[node_to_idx]; - this->remove_edge(node_from_idx, node_to_idx); - if (node_to.neighbours.size() == 1) - update_queue.emplace(node_to_idx); - } - } - - void add_contours(const std::vector> &color_poly) - { - this->all_border_points = nodes.size(); - this->polygon_sizes = std::vector(color_poly.size()); - for (size_t polygon_idx = 0; polygon_idx < color_poly.size(); ++polygon_idx) - this->polygon_sizes[polygon_idx] = color_poly[polygon_idx].size(); - this->polygon_idx_offset = std::vector(color_poly.size()); - this->polygon_idx_offset[0] = 0; - for (size_t polygon_idx = 1; polygon_idx < color_poly.size(); ++polygon_idx) { - this->polygon_idx_offset[polygon_idx] = this->polygon_idx_offset[polygon_idx - 1] + color_poly[polygon_idx - 1].size(); - } - - size_t poly_idx = 0; - for (const std::vector &color_lines : color_poly) { - size_t line_idx = 0; - for (const ColoredLine &color_line : color_lines) { - size_t from_idx = this->get_global_index(poly_idx, line_idx); - size_t to_idx = this->get_global_index(poly_idx, (line_idx + 1) % color_lines.size()); - this->append_edge(from_idx, to_idx, color_line.color, ARC_TYPE::BORDER); - ++line_idx; - } - ++poly_idx; - } - } - - // Nodes 0..all_border_points are only one with are on countour. Other vertexis are consider as not on coouter. So we check if base on attach index - inline bool is_vertex_on_contour(const Voronoi::VD::vertex_type *vertex) const - { - assert(vertex != nullptr); - return vertex->color() < this->all_border_points; - } - - inline bool is_edge_attach_to_contour(const voronoi_diagram::const_edge_iterator &edge_iterator) const - { - return this->is_vertex_on_contour(edge_iterator->vertex0()) || this->is_vertex_on_contour(edge_iterator->vertex1()); - } - - inline bool is_edge_connecting_two_contour_vertices(const voronoi_diagram::const_edge_iterator &edge_iterator) const - { - return this->is_vertex_on_contour(edge_iterator->vertex0()) && this->is_vertex_on_contour(edge_iterator->vertex1()); - } -}; - -namespace bg = boost::geometry; -namespace bgm = boost::geometry::model; -namespace bgi = boost::geometry::index; - -// float is needed because for coord_t bgi::intersects throws "bad numeric conversion: positive overflow" -using rtree_point_t = bgm::point; -using rtree_t = bgi::rtree, bgi::rstar<16, 4>>; - -static inline rtree_point_t mk_rtree_point(const Point &pt) { return rtree_point_t(float(pt.x()), float(pt.y())); } - -static inline Point mk_point(const Voronoi::VD::vertex_type *point) { return Point(coord_t(point->x()), coord_t(point->y())); } - -static inline Point mk_point(const Voronoi::Internal::point_type &point) { return Point(coord_t(point.x()), coord_t(point.y())); } - -static inline Point mk_point(const voronoi_diagram::vertex_type &point) { return Point(coord_t(point.x()), coord_t(point.y())); } - -static inline void mark_processed(const voronoi_diagram::const_edge_iterator &edge_iterator) -{ - edge_iterator->color(true); - edge_iterator->twin()->color(true); -} - -// Return true, if "p" is closer to line.a, then line.b -static inline bool is_point_closer_to_beginning_of_line(const Line &line, const Point &p) -{ - return (p - line.a).cast().squaredNorm() < (p - line.b).cast().squaredNorm(); -} - -static inline bool has_same_color(const ColoredLine &cl1, const ColoredLine &cl2) { return cl1.color == cl2.color; } - -// Determines if the line points from the point between two contour lines is pointing inside polygon or outside. -static inline bool points_inside(const Line &contour_first, const Line &contour_second, const Point &new_point) -{ - // Used in points_inside for decision if line leading thought the common point of two lines is pointing inside polygon or outside - auto three_points_inward_normal = [](const Point &left, const Point &middle, const Point &right) -> Vec2d { - assert(left != middle); - assert(middle != right); - return (perp(Point(middle - left)).cast().normalized() + perp(Point(right - middle)).cast().normalized()).normalized(); - }; - - assert(contour_first.b == contour_second.a); - Vec2d inward_normal = three_points_inward_normal(contour_first.a, contour_first.b, contour_second.b); - Vec2d edge_norm = (new_point - contour_first.b).cast().normalized(); - double side = inward_normal.dot(edge_norm); - // assert(side != 0.); - return side > 0.; -} - -static inline bool line_intersection_with_epsilon(const Line &line_to_extend, const Line &other, Point *intersection) -{ - Line extended_line = line_to_extend; - extended_line.extend(15 * SCALED_EPSILON); - return extended_line.intersection(other, intersection); -} - -// For every ColoredLine in lines_colored_out, assign the index of the polygon to which belongs and also the index of this line inside of the polygon. -static inline void init_polygon_indices(const MMU_Graph &graph, - const std::vector> &color_poly, - std::vector &lines_colored_out) -{ - size_t poly_idx = 0; - for (const std::vector &color_lines : color_poly) { - size_t line_idx = 0; - for (size_t color_line_idx = 0; color_line_idx < color_lines.size(); ++color_line_idx) { - size_t from_idx = graph.get_global_index(poly_idx, line_idx); - lines_colored_out[from_idx].poly_idx = int(poly_idx); - lines_colored_out[from_idx].local_line_idx = int(line_idx); - ++line_idx; - } - ++poly_idx; - } -} - -static MMU_Graph build_graph(size_t layer_idx, const std::vector> &color_poly) -{ - Geometry::VoronoiDiagram vd; - std::vector lines_colored = to_lines(color_poly); - Polygons color_poly_tmp = colored_points_to_polygon(color_poly); - const Points points = to_points(color_poly_tmp); - const Lines lines = to_lines(color_poly_tmp); - - boost::polygon::construct_voronoi(lines_colored.begin(), lines_colored.end(), &vd); - MMU_Graph graph; - for (const Point &point : points) - graph.nodes.push_back({point}); - - graph.add_contours(color_poly); - init_polygon_indices(graph, color_poly, lines_colored); - - assert(graph.nodes.size() == lines_colored.size()); - - // All Voronoi vertices are post-processes to merge very close vertices to single. Witch Eliminates issues with intersection edges. - // Also, Voronoi vertices outside of the bounding of input polygons are throw away by marking them. - auto append_voronoi_vertices_to_graph = [&graph, &color_poly_tmp, &vd]() -> void { - auto is_equal_points = [](const Point &p1, const Point &p2) { return p1 == p2 || (p1 - p2).cast().norm() <= 3 * SCALED_EPSILON; }; - - BoundingBox bbox = get_extents(color_poly_tmp); - bbox.offset(SCALED_EPSILON); - // EdgeGrid is used for vertices near to contour and rtree for other vertices - // FIXME Lukas H.: Get rid of EdgeGrid and rtree. Use only one structure for both cases. - EdgeGrid::Grid grid; - grid.set_bbox(bbox); - grid.create(color_poly_tmp, coord_t(scale_(10.))); - rtree_t rtree; - for (const voronoi_diagram::vertex_type &vertex : vd.vertices()) { - vertex.color(-1); - Point vertex_point = mk_point(vertex); - - const Point &first_point = graph.nodes[graph.get_arc(vertex.incident_edge()->cell()->source_index()).from_idx].point; - const Point &second_point = graph.nodes[graph.get_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx].point; - - if (vertex_equal_to_point(&vertex, first_point)) { - assert(vertex.color() != vertex.incident_edge()->cell()->source_index()); - assert(vertex.color() != vertex.incident_edge()->twin()->cell()->source_index()); - vertex.color(graph.get_arc(vertex.incident_edge()->cell()->source_index()).from_idx); - } else if (vertex_equal_to_point(&vertex, second_point)) { - assert(vertex.color() != vertex.incident_edge()->cell()->source_index()); - assert(vertex.color() != vertex.incident_edge()->twin()->cell()->source_index()); - vertex.color(graph.get_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx); - } else if (bbox.contains(vertex_point)) { - EdgeGrid::Grid::ClosestPointResult cp = grid.closest_point_signed_distance(vertex_point, coord_t(3 * SCALED_EPSILON)); - if (cp.valid()) { - size_t global_idx = graph.get_global_index(cp.contour_idx, cp.start_point_idx); - size_t global_idx_next = graph.get_global_index(cp.contour_idx, (cp.start_point_idx + 1) % color_poly_tmp[cp.contour_idx].points.size()); - vertex.color(is_equal_points(vertex_point, graph.nodes[global_idx].point) ? global_idx : global_idx_next); - } else { - if (rtree.empty()) { - rtree.insert(std::make_pair(mk_rtree_point(vertex_point), graph.nodes_count())); - vertex.color(graph.nodes_count()); - graph.nodes.push_back({vertex_point}); - } else { - std::vector> closest; - rtree.query(bgi::nearest(mk_rtree_point(vertex_point), 1), std::back_inserter(closest)); - assert(!closest.empty()); - rtree_point_t r_point = closest.front().first; - Point closest_p(bg::get<0>(r_point), bg::get<1>(r_point)); - if (Line(vertex_point, closest_p).length() > 3 * SCALED_EPSILON) { - rtree.insert(std::make_pair(mk_rtree_point(vertex_point), graph.nodes_count())); - vertex.color(graph.nodes_count()); - graph.nodes.push_back({vertex_point}); - } else { - vertex.color(closest.front().second); - } - } - } - } - } - }; - - append_voronoi_vertices_to_graph(); - - auto get_prev_contour_line = [&lines_colored, &color_poly, &graph](const voronoi_diagram::const_edge_iterator &edge_it) -> ColoredLine { - size_t contour_line_local_idx = lines_colored[edge_it->cell()->source_index()].local_line_idx; - size_t contour_line_size = color_poly[lines_colored[edge_it->cell()->source_index()].poly_idx].size(); - size_t contour_prev_idx = graph.get_global_index(lines_colored[edge_it->cell()->source_index()].poly_idx, - (contour_line_local_idx > 0) ? contour_line_local_idx - 1 : contour_line_size - 1); - return lines_colored[contour_prev_idx]; - }; - - auto get_next_contour_line = [&lines_colored, &color_poly, &graph](const voronoi_diagram::const_edge_iterator &edge_it) -> ColoredLine { - size_t contour_line_local_idx = lines_colored[edge_it->cell()->source_index()].local_line_idx; - size_t contour_line_size = color_poly[lines_colored[edge_it->cell()->source_index()].poly_idx].size(); - size_t contour_next_idx = graph.get_global_index(lines_colored[edge_it->cell()->source_index()].poly_idx, - (contour_line_local_idx + 1) % contour_line_size); - return lines_colored[contour_next_idx]; - }; - - BoundingBox bbox = get_extents(color_poly_tmp); - bbox.offset(scale_(10.)); - const double bbox_dim_max = double(std::max(bbox.size().x(), bbox.size().y())); - - // Make a copy of the input segments with the double type. - std::vector segments; - for (const Line &line : lines) - segments.emplace_back(Voronoi::Internal::point_type(double(line.a(0)), double(line.a(1))), - Voronoi::Internal::point_type(double(line.b(0)), double(line.b(1)))); - - for (auto edge_it = vd.edges().begin(); edge_it != vd.edges().end(); ++edge_it) { - // Skip second half-edge - if (edge_it->cell()->source_index() > edge_it->twin()->cell()->source_index() || edge_it->color()) - continue; - - if (edge_it->is_infinite()) { - // Infinite edge is leading through a point on the counter, but there are no Voronoi vertices. - // So we could fix this case by computing the intersection between the contour line and infinity edge. - std::vector samples; - Voronoi::Internal::clip_infinite_edge(points, segments, *edge_it, bbox_dim_max, &samples); - if (samples.empty()) - continue; - - const Line edge_line(mk_point(samples[0]), mk_point(samples[1])); - const ColoredLine &contour_line = lines_colored[edge_it->cell()->source_index()]; - Point contour_intersection; - - if (line_intersection_with_epsilon(contour_line.line, edge_line, &contour_intersection)) { - const MMU_Graph::Arc &graph_arc = graph.get_arc(edge_it->cell()->source_index()); - const size_t from_idx = (edge_it->vertex1() != nullptr) ? edge_it->vertex1()->color() : edge_it->vertex0()->color(); - size_t to_idx = ((contour_line.line.a - contour_intersection).cast().squaredNorm() < - (contour_line.line.b - contour_intersection).cast().squaredNorm()) ? - graph_arc.from_idx : - graph_arc.to_idx; - if (from_idx != to_idx && from_idx < graph.nodes_count() && to_idx < graph.nodes_count()) { - graph.append_edge(from_idx, to_idx); - mark_processed(edge_it); - } - } - } else if (edge_it->is_finite()) { - const Point v0 = mk_point(edge_it->vertex0()); - const Point v1 = mk_point(edge_it->vertex1()); - const size_t from_idx = edge_it->vertex0()->color(); - const size_t to_idx = edge_it->vertex1()->color(); - - // Both points are on contour, so skip them. In cases of duplicate Voronoi vertices, skip edges between the same two points. - if (graph.is_edge_connecting_two_contour_vertices(edge_it) || (edge_it->vertex0()->color() == edge_it->vertex1()->color())) continue; - - const Line edge_line(v0, v1); - const Line contour_line = lines_colored[edge_it->cell()->source_index()].line; - const ColoredLine colored_line = lines_colored[edge_it->cell()->source_index()]; - const ColoredLine contour_line_prev = get_prev_contour_line(edge_it); - const ColoredLine contour_line_next = get_next_contour_line(edge_it); - - Point intersection; - if (edge_it->vertex0()->color() >= graph.nodes_count() || edge_it->vertex1()->color() >= graph.nodes_count()) { -// if(edge_it->vertex0()->color() < graph.nodes_count() && !graph.is_vertex_on_contour(edge_it->vertex0())) { -// -// } - if (edge_it->vertex1()->color() < graph.nodes_count() && !graph.is_vertex_on_contour(edge_it->vertex1())) { - Line contour_line_twin = lines_colored[edge_it->twin()->cell()->source_index()].line; - if (line_intersection_with_epsilon(contour_line_twin, edge_line, &intersection)) { - const MMU_Graph::Arc &graph_arc = graph.get_arc(edge_it->twin()->cell()->source_index()); - const size_t to_idx_l = is_point_closer_to_beginning_of_line(contour_line_twin, intersection) ? graph_arc.from_idx : - graph_arc.to_idx; - graph.append_edge(edge_it->vertex1()->color(), to_idx_l); - } else if (line_intersection_with_epsilon(contour_line, edge_line, &intersection)) { - const MMU_Graph::Arc &graph_arc = graph.get_arc(edge_it->cell()->source_index()); - const size_t to_idx_l = is_point_closer_to_beginning_of_line(contour_line, intersection) ? graph_arc.from_idx : graph_arc.to_idx; - graph.append_edge(edge_it->vertex1()->color(), to_idx_l); - } - mark_processed(edge_it); - } - } else if (graph.is_edge_attach_to_contour(edge_it)) { - mark_processed(edge_it); - // Skip edges witch connection two points on a contour - if (graph.is_edge_connecting_two_contour_vertices(edge_it)) - continue; - - if (graph.is_vertex_on_contour(edge_it->vertex0())) { - if (is_point_closer_to_beginning_of_line(contour_line, v0)) { - if (!has_same_color(contour_line_prev, colored_line) && points_inside(contour_line_prev.line, contour_line, v1)) { - graph.append_edge(from_idx, to_idx); - } - } else { - if (!has_same_color(contour_line_next, colored_line) && points_inside(contour_line, contour_line_next.line, v1)) { - graph.append_edge(from_idx, to_idx); - } - } - } else { - assert(graph.is_vertex_on_contour(edge_it->vertex1())); - if (is_point_closer_to_beginning_of_line(contour_line, v1)) { - if (!has_same_color(contour_line_prev, colored_line) && points_inside(contour_line_prev.line, contour_line, v0)) { - graph.append_edge(from_idx, to_idx); - } - } else { - if (!has_same_color(contour_line_next, colored_line) && points_inside(contour_line, contour_line_next.line, v0)) { - graph.append_edge(from_idx, to_idx); - } - } - } - } else if (line_intersection_with_epsilon(contour_line, edge_line, &intersection)) { - mark_processed(edge_it); - Point real_v0 = graph.nodes[edge_it->vertex0()->color()].point; - Point real_v1 = graph.nodes[edge_it->vertex1()->color()].point; - - if (is_point_closer_to_beginning_of_line(contour_line, intersection)) { - Line first_part(intersection, real_v0); - Line second_part(intersection, real_v1); - - if (!has_same_color(contour_line_prev, colored_line)) { - if (points_inside(contour_line_prev.line, contour_line, first_part.b)) { - graph.append_edge(edge_it->vertex0()->color(), graph.get_arc(edge_it->cell()->source_index()).from_idx); - } - if (points_inside(contour_line_prev.line, contour_line, second_part.b)) { - graph.append_edge(edge_it->vertex1()->color(), graph.get_arc(edge_it->cell()->source_index()).from_idx); - } - } - } else { - const size_t int_point_idx = graph.get_arc(edge_it->cell()->source_index()).to_idx; - const Point int_point = graph.nodes[int_point_idx].point; - - const Line first_part(int_point, real_v0); - const Line second_part(int_point, real_v1); - - if (!has_same_color(contour_line_next, colored_line)) { - if (points_inside(contour_line, contour_line_next.line, first_part.b)) { - graph.append_edge(edge_it->vertex0()->color(), int_point_idx); - } - if (points_inside(contour_line, contour_line_next.line, second_part.b)) { - graph.append_edge(edge_it->vertex1()->color(), int_point_idx); - } - } - } - } - } - } - - for (auto edge_it = vd.edges().begin(); edge_it != vd.edges().end(); ++edge_it) { - // Skip second half-edge and processed edges - if (edge_it->cell()->source_index() > edge_it->twin()->cell()->source_index() || edge_it->color()) - continue; - - if (edge_it->is_finite() && !bool(edge_it->color()) && edge_it->vertex0()->color() < graph.nodes_count() && - edge_it->vertex1()->color() < graph.nodes_count()) { - // Skip cases, when the edge is between two same vertices, which is in cases two near vertices were merged together. - if (edge_it->vertex0()->color() == edge_it->vertex1()->color()) - continue; - - size_t from_idx = edge_it->vertex0()->color(); - size_t to_idx = edge_it->vertex1()->color(); - graph.append_edge(from_idx, to_idx); - } - mark_processed(edge_it); - } - - graph.remove_nodes_with_one_arc(); - return graph; -} - -static inline Polygon to_polygon(const Lines &lines) -{ - Polygon poly_out; - poly_out.points.reserve(lines.size()); - for (const Line &line : lines) - poly_out.points.emplace_back(line.a); - return poly_out; -} - -// Returns list of polygons and assigned colors. -// It iterates through all nodes on the border between two different colors, and from this point, -// start selection always left most edges for every node to construct CCW polygons. -// Assumes that graph is planar (without self-intersection edges) -static std::vector> extract_colored_segments(MMU_Graph &graph) -{ - // When there is no next arc, then is returned original_arc or edge with is marked as used - auto get_next = [&graph](const Line &process_line, MMU_Graph::Arc &original_arc) -> MMU_Graph::Arc & { - std::vector> sorted_arcs; - for (MMU_Graph::Arc &arc : graph.nodes[original_arc.to_idx].neighbours) { - if (graph.nodes[arc.to_idx].point == process_line.a || arc.used) - continue; - - assert(original_arc.to_idx == arc.from_idx); - Vec2d process_line_vec_n = (process_line.a - process_line.b).cast().normalized(); - Vec2d neighbour_line_vec_n = (graph.nodes[arc.to_idx].point - graph.nodes[arc.from_idx].point).cast().normalized(); - - double angle = ::acos(clamp(-1.0, 1.0, neighbour_line_vec_n.dot(process_line_vec_n))); - if (Slic3r::cross2(neighbour_line_vec_n, process_line_vec_n) < 0.0) - angle = 2.0 * (double) PI - angle; - - sorted_arcs.emplace_back(&arc, angle); - } - - std::sort(sorted_arcs.begin(), sorted_arcs.end(), - [](std::pair &l, std::pair &r) -> bool { return l.second < r.second; }); - - // Try to return left most edge witch is unused - for (auto &sorted_arc : sorted_arcs) - if (!sorted_arc.first->used) - return *sorted_arc.first; - - if (sorted_arcs.empty()) - return original_arc; - - return *(sorted_arcs.front().first); - }; - - std::vector> polygons_segments; - for (MMU_Graph::Node &node : graph.nodes) - for (MMU_Graph::Arc &arc : node.neighbours) - arc.used = false; - - for (size_t node_idx = 0; node_idx < graph.all_border_points; ++node_idx) { - MMU_Graph::Node &node = graph.nodes[node_idx]; - - for (MMU_Graph::Arc &arc : node.neighbours) { - if (arc.type == MMU_Graph::ARC_TYPE::NON_BORDER || arc.used) continue; - - Line process_line(node.point, graph.nodes[arc.to_idx].point); - arc.used = true; - - Lines face_lines; - face_lines.emplace_back(process_line); - Point start_p = process_line.a; - - Line p_vec = process_line; - MMU_Graph::Arc *p_arc = &arc; - do { - MMU_Graph::Arc &next = get_next(p_vec, *p_arc); - face_lines.emplace_back(Line(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point)); - if (next.used) break; - - next.used = true; - p_vec = Line(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point); - p_arc = &next; - } while (graph.nodes[p_arc->to_idx].point != start_p); - - Polygon poly = to_polygon(face_lines); - if (poly.is_counter_clockwise() && poly.is_valid()) - polygons_segments.emplace_back(poly, arc.color); - } - } - return polygons_segments; -} - -// Used in remove_multiple_edges_in_vertices() -// Returns length of edge with is connected to contour. To this length is include other edges with follows it if they are almost straight (with the -// tolerance of 15) And also if node between two subsequent edges is connected only to these two edges. -static inline double compute_edge_length(MMU_Graph &graph, size_t start_idx, MMU_Graph::Arc &start_edge) -{ - for (MMU_Graph::Node &node : graph.nodes) - for (MMU_Graph::Arc &arc : node.neighbours) - arc.used = false; - - start_edge.used = true; - MMU_Graph::Arc *arc = &start_edge; - size_t idx = start_idx; - double line_total_length = Line(graph.nodes[idx].point, graph.nodes[arc->to_idx].point).length(); - while (graph.nodes[arc->to_idx].neighbours.size() == 2) { - bool found = false; - for (MMU_Graph::Arc &arc_n : graph.nodes[arc->to_idx].neighbours) { - if (arc_n.type == MMU_Graph::ARC_TYPE::NON_BORDER && !arc_n.used && arc_n.to_idx != idx) { - Line first_line(graph.nodes[idx].point, graph.nodes[arc->to_idx].point); - Line second_line(graph.nodes[arc->to_idx].point, graph.nodes[arc_n.to_idx].point); - - Vec2d first_line_vec = (first_line.a - first_line.b).cast(); - Vec2d second_line_vec = (second_line.b - second_line.a).cast(); - Vec2d first_line_vec_n = first_line_vec.normalized(); - Vec2d second_line_vec_n = second_line_vec.normalized(); - double angle = ::acos(clamp(-1.0, 1.0, first_line_vec_n.dot(second_line_vec_n))); - if (Slic3r::cross2(first_line_vec_n, second_line_vec_n) < 0.0) - angle = 2.0 * (double) PI - angle; - - if (std::abs(angle - PI) >= (PI / 12)) - continue; - - idx = arc->to_idx; - arc = &arc_n; - - line_total_length += Line(graph.nodes[idx].point, graph.nodes[arc->to_idx].point).length(); - arc_n.used = true; - found = true; - break; - } - } - if (!found) - break; - } - - return line_total_length; -} - -// Used for fixing double Voronoi edges for concave parts of the polygon. -static void remove_multiple_edges_in_vertices(MMU_Graph &graph, const std::vector> &color_poly) -{ - std::vector>> colored_segments = get_all_segments(color_poly); - for (const std::vector> &colored_segment_p : colored_segments) { - size_t poly_idx = &colored_segment_p - &colored_segments.front(); - for (const std::pair &colored_segment : colored_segment_p) { - size_t first_idx = graph.get_global_index(poly_idx, colored_segment.first); - size_t second_idx = graph.get_global_index(poly_idx, (colored_segment.second + 1) % graph.polygon_sizes[poly_idx]); - Line seg_line(graph.nodes[first_idx].point, graph.nodes[second_idx].point); - - if (graph.nodes[first_idx].neighbours.size() >= 3) { - std::vector> arc_to_check; - for (MMU_Graph::Arc &n_arc : graph.nodes[first_idx].neighbours) { - if (n_arc.type == MMU_Graph::ARC_TYPE::NON_BORDER) { - double total_len = compute_edge_length(graph, first_idx, n_arc); - arc_to_check.emplace_back(&n_arc, total_len); - } - } - std::sort(arc_to_check.begin(), arc_to_check.end(), - [](std::pair &l, std::pair &r) -> bool { return l.second > r.second; }); - - while (arc_to_check.size() > 1) { - graph.remove_edge(first_idx, arc_to_check.back().first->to_idx); - arc_to_check.pop_back(); - } - } - } - } -} - -static void cut_segmented_layers(const LayerPtrs &layers, std::vector>> &segmented_regions, const float cut_width) { - tbb::parallel_for(tbb::blocked_range(0, segmented_regions.size()),[&](const tbb::blocked_range& range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { - std::vector> segmented_regions_cuts; - for (const std::pair &colored_expoly : segmented_regions[layer_idx]) { - ExPolygons cut_colored_expoly = diff_ex({colored_expoly.first}, offset_ex(layers[layer_idx]->lslices, cut_width)); - for (const ExPolygon &expoly : cut_colored_expoly) { - segmented_regions_cuts.emplace_back(expoly, colored_expoly.second); - } - } - segmented_regions[layer_idx] = segmented_regions_cuts; - } - }); // end of parallel_for -} - -std::vector> PrintObject::mmu_segmentation_top_and_bottom_layers() -{ - std::vector> triangles_by_color(3); - triangles_by_color.assign(3, std::vector(m_layers.size())); - for (const ModelVolume *mv : this->model_object()->volumes) { - for (const auto ¶ms : {std::make_pair(EnforcerBlockerType::NONE, 0), std::make_pair(EnforcerBlockerType::ENFORCER, 1), - std::make_pair(EnforcerBlockerType::BLOCKER, 2)}) { - const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, params.first); - if (!mv->is_model_part() || custom_facets.indices.empty()) - continue; - - const Transform3f tr = this->trafo().cast() * mv->get_matrix().cast(); - for (size_t facet_idx = 0; facet_idx < custom_facets.indices.size(); ++facet_idx) { - float min_z = std::numeric_limits::max(); - float max_z = std::numeric_limits::lowest(); - - std::array facet; - Points projected_facet(3); - for (int p_idx = 0; p_idx < 3; ++p_idx) { - facet[p_idx] = tr * custom_facets.vertices[custom_facets.indices[facet_idx](p_idx)]; - max_z = std::max(max_z, facet[p_idx].z()); - min_z = std::min(min_z, facet[p_idx].z()); - } - - // Sort the vertices by z-axis for simplification of projected_facet on slices - std::sort(facet.begin(), facet.end(), [](const Vec3f &p1, const Vec3f &p2) { return p1.z() < p2.z(); }); - - for (int p_idx = 0; p_idx < 3; ++p_idx) { - projected_facet[p_idx] = Point(scale_(facet[p_idx].x()), scale_(facet[p_idx].y())); - projected_facet[p_idx] = projected_facet[p_idx] - this->center_offset(); - } - - ExPolygon triangle = ExPolygon(projected_facet); - - // Find lowest slice not below the triangle. - auto first_layer = std::upper_bound(m_layers.begin(), m_layers.end(), float(min_z - EPSILON), - [](float z, const Layer *l1) { return z < l1->slice_z + l1->height * 0.5; }); - auto last_layer = std::upper_bound(m_layers.begin(), m_layers.end(), float(max_z - EPSILON), - [](float z, const Layer *l1) { return z < l1->slice_z + l1->height * 0.5; }); - - if (last_layer == m_layers.end()) - --last_layer; - - if (first_layer == m_layers.end() || (first_layer != m_layers.begin() && facet[0].z() < (*first_layer)->print_z - EPSILON)) - --first_layer; - - for (auto layer_it = first_layer; (layer_it != (last_layer + 1) && layer_it != m_layers.end()); ++layer_it) { - size_t layer_idx = layer_it - m_layers.begin(); - triangles_by_color[params.second][layer_idx].emplace_back(triangle); - } - } - } - } - - auto get_extrusion_width = [&m_layers = std::as_const(m_layers)](const size_t layer_idx) -> float { - auto extrusion_width_it = std::max_element(m_layers[layer_idx]->m_regions.begin(), m_layers[layer_idx]->m_regions.end(), - [](const LayerRegion *l1, const LayerRegion *l2) { - return l1->region()->config().perimeter_extrusion_width < - l2->region()->config().perimeter_extrusion_width; - }); - assert(extrusion_width_it != m_layers[layer_idx]->m_regions.end()); - return float((*extrusion_width_it)->region()->config().perimeter_extrusion_width); - }; - - auto get_top_solid_layers = [&m_layers = std::as_const(m_layers)](const size_t layer_idx) -> int { - auto top_solid_layer_it = std::max_element(m_layers[layer_idx]->m_regions.begin(), m_layers[layer_idx]->m_regions.end(), - [](const LayerRegion *l1, const LayerRegion *l2) { - return l1->region()->config().top_solid_layers < l2->region()->config().top_solid_layers; - }); - assert(top_solid_layer_it != m_layers[layer_idx]->m_regions.end()); - return (*top_solid_layer_it)->region()->config().top_solid_layers; - }; - - auto get_bottom_solid_layers = [&m_layers = std::as_const(m_layers)](const size_t layer_idx) -> int { - auto top_bottom_layer_it = std::max_element(m_layers[layer_idx]->m_regions.begin(), m_layers[layer_idx]->m_regions.end(), - [](const LayerRegion *l1, const LayerRegion *l2) { - return l1->region()->config().bottom_solid_layers < l2->region()->config().bottom_solid_layers; - }); - assert(top_bottom_layer_it != m_layers[layer_idx]->m_regions.end()); - return (*top_bottom_layer_it)->region()->config().bottom_solid_layers; - }; - - std::vector top_layers(m_layers.size()); - top_layers.back() = m_layers.back()->lslices; - tbb::parallel_for(tbb::blocked_range(1, m_layers.size()), [&](const tbb::blocked_range &range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { - float extrusion_width = 0.1f * float(scale_(get_extrusion_width(layer_idx))); - top_layers[layer_idx - 1] = diff_ex(m_layers[layer_idx - 1]->lslices, offset_ex(m_layers[layer_idx]->lslices, extrusion_width)); - } - }); // end of parallel_for - - std::vector bottom_layers(m_layers.size()); - bottom_layers.front() = m_layers.front()->lslices; - tbb::parallel_for(tbb::blocked_range(0, m_layers.size() - 1), [&](const tbb::blocked_range &range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { - float extrusion_width = 0.1f * float(scale_(get_extrusion_width(layer_idx))); - bottom_layers[layer_idx + 1] = diff_ex(m_layers[layer_idx + 1]->lslices, offset_ex(m_layers[layer_idx]->lslices, extrusion_width)); - } - }); // end of parallel_for - - tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [&](const tbb::blocked_range &range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { - float extrusion_width = 0.1f * float(scale_(get_extrusion_width(layer_idx))); - for (std::vector &triangles : triangles_by_color) { - if (!triangles[layer_idx].empty() && (!top_layers[layer_idx].empty() || !bottom_layers[layer_idx].empty())) { - ExPolygons connected = union_ex(offset_ex(triangles[layer_idx], float(10 * SCALED_EPSILON))); - triangles[layer_idx] = union_ex(offset_ex(offset_ex(connected, -extrusion_width / 1), extrusion_width / 1)); - } else { - triangles[layer_idx].clear(); - } - } - } - }); // end of parallel_for - - std::vector> triangles_by_color_bottom(3); - std::vector> triangles_by_color_top(3); - triangles_by_color_bottom.assign(3, std::vector(m_layers.size())); - triangles_by_color_top.assign(3, std::vector(m_layers.size())); - - for (size_t layer_idx = 0; layer_idx < this->layers().size(); ++layer_idx) { - BOOST_LOG_TRIVIAL(debug) << "MMU segmentation of top layer: " << layer_idx; - float extrusion_width = scale_(get_extrusion_width(layer_idx)); - int top_solid_layers = get_top_solid_layers(layer_idx); - ExPolygons top_expolygon = top_layers[layer_idx]; - if (top_expolygon.empty()) - continue; - - for (size_t color_idx = 0; color_idx < triangles_by_color.size(); ++color_idx) { - if (triangles_by_color[color_idx][layer_idx].empty()) - continue; - ExPolygons intersection_poly = intersection_ex(triangles_by_color[color_idx][layer_idx], top_expolygon); - if (!intersection_poly.empty()) { - triangles_by_color_top[color_idx][layer_idx].insert(triangles_by_color_top[color_idx][layer_idx].end(), intersection_poly.begin(), - intersection_poly.end()); - for (int last_idx = int(layer_idx) - 1; last_idx >= std::max(int(layer_idx - top_solid_layers), int(0)); --last_idx) { - float offset_value = float(layer_idx - last_idx) * (-1.0f) * extrusion_width; - if (offset_ex(top_expolygon, offset_value).empty()) - continue; - ExPolygons layer_slices_trimmed = m_layers[last_idx]->lslices; - - for (int last_idx_1 = last_idx; last_idx_1 < int(layer_idx); ++last_idx_1) { - layer_slices_trimmed = intersection_ex(layer_slices_trimmed, m_layers[last_idx_1 + 1]->lslices); - } - - ExPolygons offset_e = offset_ex(layer_slices_trimmed, offset_value); - ExPolygons intersection_poly_2 = intersection_ex(triangles_by_color_top[color_idx][layer_idx], offset_e); - triangles_by_color_top[color_idx][last_idx].insert(triangles_by_color_top[color_idx][last_idx].end(), intersection_poly_2.begin(), - intersection_poly_2.end()); - } - } - } - } - - for (size_t layer_idx = 0; layer_idx < this->layers().size(); ++layer_idx) { - BOOST_LOG_TRIVIAL(debug) << "MMU segmentation of bottom layer: " << layer_idx; - float extrusion_width = scale_(get_extrusion_width(layer_idx)); - int bottom_solid_layers = get_bottom_solid_layers(layer_idx); - ExPolygons bottom_expolygon = bottom_layers[layer_idx]; - if (bottom_expolygon.empty()) - continue; - - for (size_t color_idx = 0; color_idx < triangles_by_color.size(); ++color_idx) { - if (triangles_by_color[color_idx][layer_idx].empty()) - continue; - - ExPolygons intersection_poly = intersection_ex(triangles_by_color[color_idx][layer_idx], bottom_expolygon); - if (!intersection_poly.empty()) { - triangles_by_color_bottom[color_idx][layer_idx].insert(triangles_by_color_bottom[color_idx][layer_idx].end(), intersection_poly.begin(), - intersection_poly.end()); - for (size_t last_idx = layer_idx + 1; last_idx < std::min(layer_idx + bottom_solid_layers, m_layers.size()); ++last_idx) { - float offset_value = float(last_idx - layer_idx) * (-1.0f) * extrusion_width; - if (offset_ex(bottom_expolygon, offset_value).empty()) - continue; - ExPolygons layer_slices_trimmed = m_layers[last_idx]->lslices; - - for (int last_idx_1 = int(last_idx); last_idx_1 > int(layer_idx); --last_idx_1) { - layer_slices_trimmed = intersection_ex(layer_slices_trimmed, offset_ex(m_layers[last_idx_1 - 1]->lslices, offset_value)); - } - - ExPolygons offset_e = offset_ex(layer_slices_trimmed, offset_value); - ExPolygons intersection_poly_2 = intersection_ex(triangles_by_color_bottom[color_idx][layer_idx], offset_e); - triangles_by_color_bottom[color_idx][last_idx].insert(triangles_by_color_bottom[color_idx][last_idx].end(), intersection_poly_2.begin(), - intersection_poly_2.end()); - } - } - } - } - - std::vector> triangles_by_color_merged(3); - triangles_by_color_merged.assign(3, std::vector(m_layers.size())); - for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++layer_idx) { - for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) { - triangles_by_color_merged[color_idx][layer_idx].insert(triangles_by_color_merged[color_idx][layer_idx].end(), - triangles_by_color_bottom[color_idx][layer_idx].begin(), - triangles_by_color_bottom[color_idx][layer_idx].end()); - triangles_by_color_merged[color_idx][layer_idx].insert(triangles_by_color_merged[color_idx][layer_idx].end(), - triangles_by_color_top[color_idx][layer_idx].begin(), - triangles_by_color_top[color_idx][layer_idx].end()); - triangles_by_color_merged[color_idx][layer_idx] = union_ex(triangles_by_color_merged[color_idx][layer_idx]); - } - - // Cut all colors for cases when two colors are overlapping - for (size_t color_idx = 1; color_idx < triangles_by_color_merged.size(); ++color_idx) { - triangles_by_color_merged[color_idx][layer_idx] = diff_ex(triangles_by_color_merged[color_idx][layer_idx], - triangles_by_color_merged[color_idx - 1][layer_idx]); - } - } - - return triangles_by_color_merged; -} - -static std::vector>> merge_segmented_layers( - const std::vector>> &segmented_regions, const std::vector> &top_and_bottom_layers) -{ - std::vector>> segmented_regions_merged(segmented_regions.size()); - - tbb::parallel_for(tbb::blocked_range(0, segmented_regions.size()), [&](const tbb::blocked_range &range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { - BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - merging region: " << layer_idx; - for (const std::pair &colored_expoly : segmented_regions[layer_idx]) { - ExPolygons cut_colored_expoly = {colored_expoly.first}; - for (const std::vector &top_and_bottom_layer : top_and_bottom_layers) - cut_colored_expoly = diff_ex(cut_colored_expoly, top_and_bottom_layer[layer_idx]); - for (ExPolygon &ex_poly : cut_colored_expoly) - segmented_regions_merged[layer_idx].emplace_back(ex_poly, colored_expoly.second); - - } - - for (size_t color_idx = 0; color_idx < top_and_bottom_layers.size(); ++color_idx) { - ExPolygons top_and_bottom_expoly = top_and_bottom_layers[color_idx][layer_idx]; - for (const ExPolygon &expoly : top_and_bottom_expoly) { segmented_regions_merged[layer_idx].emplace_back(expoly, color_idx); } - } - } - }); // end of parallel_for - - return segmented_regions_merged; -} - -std::vector>> PrintObject::mmu_segmentation_by_painting() -{ - std::vector>> segmented_regions(this->layers().size()); - std::vector> painted_lines(this->layers().size()); - std::vector edge_grids(this->layers().size()); - - for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++layer_idx) { - const Layer *layer = m_layers[layer_idx]; - BoundingBox bbox(get_extents(layer->lslices)); - bbox.offset(SCALED_EPSILON); - edge_grids[layer_idx].set_bbox(bbox); - edge_grids[layer_idx].create(layer->lslices, coord_t(scale_(10.))); - } - - for (const ModelVolume *mv : this->model_object()->volumes) { - for (const auto ¶ms : {std::make_pair(EnforcerBlockerType::ENFORCER, 1), std::make_pair(EnforcerBlockerType::BLOCKER, 2)}) { - const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, params.first); - if (!mv->is_model_part() || custom_facets.indices.empty()) - continue; - - const Transform3f tr = this->trafo().cast() * mv->get_matrix().cast(); - for (size_t facet_idx = 0; facet_idx < custom_facets.indices.size(); ++facet_idx) { - float min_z = std::numeric_limits::max(); - float max_z = std::numeric_limits::lowest(); - - std::array facet; - Points projected_facet(3); - for (int p_idx = 0; p_idx < 3; ++p_idx) { - facet[p_idx] = tr * custom_facets.vertices[custom_facets.indices[facet_idx](p_idx)]; - max_z = std::max(max_z, facet[p_idx].z()); - min_z = std::min(min_z, facet[p_idx].z()); - } - - // Sort the vertices by z-axis for simplification of projected_facet on slices - std::sort(facet.begin(), facet.end(), [](const Vec3f &p1, const Vec3f &p2) { return p1.z() < p2.z(); }); - - for (int p_idx = 0; p_idx < 3; ++p_idx) { - projected_facet[p_idx] = Point(scale_(facet[p_idx].x()), scale_(facet[p_idx].y())); - projected_facet[p_idx] = projected_facet[p_idx] - this->center_offset(); - } - - ExPolygon triangle = ExPolygon(projected_facet); - - // Find lowest slice not below the triangle. - auto first_layer = std::upper_bound(this->layers().begin(), this->layers().end(), float(min_z - EPSILON), - [](float z, const Layer *l1) { return z < l1->slice_z; }); - auto last_layer = std::upper_bound(this->layers().begin(), this->layers().end(), float(max_z + EPSILON), - [](float z, const Layer *l1) { return z < l1->slice_z; }); - --last_layer; - - for (auto layer_it = first_layer; layer_it != (last_layer + 1); ++layer_it) { - const Layer *layer = *layer_it; - size_t layer_idx = layer_it - this->layers().begin(); - if (facet[0].z() > layer->slice_z || layer->slice_z > facet[2].z()) - continue; - - // https://kandepet.com/3d-printing-slicing-3d-objects/ - float t = (float(layer->slice_z) - facet[0].z()) / (facet[2].z() - facet[0].z()); - Vec3f line_start_f = facet[0] + t * (facet[2] - facet[0]); - Vec3f line_end_f; - - if (facet[1].z() > layer->slice_z) { - // [P0, P2] a [P0, P1] - float t1 = (float(layer->slice_z) - facet[0].z()) / (facet[1].z() - facet[0].z()); - line_end_f = facet[0] + t1 * (facet[1] - facet[0]); - } else if (facet[1].z() <= layer->slice_z) { - // [P0, P2] a [P1, P2] - float t2 = (float(layer->slice_z) - facet[1].z()) / (facet[2].z() - facet[1].z()); - line_end_f = facet[1] + t2 * (facet[2] - facet[1]); - } - - Point line_start(scale_(line_start_f.x()), scale_(line_start_f.y())); - Point line_end(scale_(line_end_f.x()), scale_(line_end_f.y())); - line_start -= this->center_offset(); - line_end -= this->center_offset(); - - std::vector painted_line_tmp; - PaintedLineVisitor visitor(edge_grids[layer_idx], painted_line_tmp); - visitor.reset(); - visitor.line_to_test.a = line_start; - visitor.line_to_test.b = line_end; - visitor.color = params.second; - edge_grids[layer_idx].visit_cells_intersecting_line(line_start, line_end, visitor); - - if (!painted_line_tmp.empty()) - painted_lines[layer_idx].insert(painted_lines[layer_idx].end(), painted_line_tmp.begin(), painted_line_tmp.end()); - } - } - } - } - - tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [&](const tbb::blocked_range &range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { - // for(size_t layer_idx = 0; layer_idx < this->layers().size(); ++layer_idx) { - BOOST_LOG_TRIVIAL(debug) << "MMU segmentation of layer: " << layer_idx; - auto comp = [&edge_grids, &layer_idx](const PaintedLine &first, const PaintedLine &second) { - Point first_start_p = *(edge_grids[layer_idx].contours()[first.contour_idx].begin() + first.line_idx); - - return first.contour_idx < second.contour_idx || - (first.contour_idx == second.contour_idx && - (first.line_idx < second.line_idx || - (first.line_idx == second.line_idx && - Line(first_start_p, first.projected_line.a).length() < Line(first_start_p, second.projected_line.a).length()))); - }; - - std::sort(painted_lines[layer_idx].begin(), painted_lines[layer_idx].end(), comp); - std::vector &painted_lines_single = painted_lines[layer_idx]; - - if (!painted_lines_single.empty()) { - Polygons original_polygons; - for (const Slic3r::EdgeGrid::Contour &contour : edge_grids[layer_idx].contours()) { - Points points; - for (const Point &point : contour) points.emplace_back(point); - original_polygons.emplace_back(points); - } - - std::vector> color_poly = colorize_polygons(original_polygons, painted_lines_single); - MMU_Graph graph = build_graph(layer_idx, color_poly); - remove_multiple_edges_in_vertices(graph, color_poly); - graph.remove_nodes_with_one_arc(); - std::vector> segmentation = extract_colored_segments(graph); - for (const std::pair ®ion : segmentation) - segmented_regions[layer_idx].emplace_back(region); - } - } - }); // end of parallel_for - - if(m_print->config().mmu_segmented_region_max_width > 0.f) - cut_segmented_layers(m_layers, segmented_regions, float(-scale_(m_print->config().mmu_segmented_region_max_width))); - -// return segmented_regions; - std::vector> top_and_bottom_layers = mmu_segmentation_top_and_bottom_layers(); - std::vector>> segmented_regions_merged = merge_segmented_layers(segmented_regions, top_and_bottom_layers); - return segmented_regions_merged; -} -// --------------------MMU_END---------------------- - } // namespace Slic3r From 38bb7d2950bfc58df10909ccea2fc8f752c6eed5 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 22 Apr 2021 11:41:26 +0200 Subject: [PATCH 10/26] 1) New methods PrintObject::num_regions() and ::has_region() to make the code more readable and to highlight where PrintObject::region_volumes are actually set and consumed. 2) Replaced Slic3r::clamp() with std::clamp(). They differ in the order of their parameters, thus hopefully no new bugs were introduced. 3) Some refactoring of MultiMaterialSegmentation for efficiency. --- src/libslic3r/ClipperUtils.cpp | 2 +- src/libslic3r/ElephantFootCompensation.cpp | 2 +- src/libslic3r/ExtrusionSimulator.cpp | 28 ++--- src/libslic3r/Fill/Fill3DHoneycomb.cpp | 4 +- src/libslic3r/Fill/FillGyroid.cpp | 2 +- src/libslic3r/GCode.cpp | 2 +- .../GCode/AvoidCrossingPerimeters.cpp | 2 +- src/libslic3r/GCode/CoolingBuffer.cpp | 4 +- src/libslic3r/GCode/ToolOrdering.cpp | 6 +- src/libslic3r/MultiMaterialSegmentation.cpp | 117 ++++++++---------- src/libslic3r/Print.cpp | 8 +- src/libslic3r/Print.hpp | 3 + src/libslic3r/PrintObject.cpp | 66 +++++----- src/libslic3r/Slicing.cpp | 30 ++--- src/libslic3r/SupportMaterial.cpp | 4 +- src/libslic3r/VoronoiOffset.cpp | 4 +- src/libslic3r/libslic3r.h | 12 +- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 2 +- 19 files changed, 139 insertions(+), 161 deletions(-) diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index cd243dfb1b..459560ce17 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -1039,7 +1039,7 @@ ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::v Vec2d nnext = perp(ptnext - pt).normalized(); double delta = deltas[i]; - double sin_a = clamp(-1., 1., cross2(nprev, nnext)); + double sin_a = std::clamp(cross2(nprev, nnext), -1., 1.); double convex = sin_a * delta; if (convex <= - sin_min_parallel) { // Concave corner. diff --git a/src/libslic3r/ElephantFootCompensation.cpp b/src/libslic3r/ElephantFootCompensation.cpp index 0895e16d68..0adff1ba4e 100644 --- a/src/libslic3r/ElephantFootCompensation.cpp +++ b/src/libslic3r/ElephantFootCompensation.cpp @@ -270,7 +270,7 @@ std::vector contour_distance2(const EdgeGrid::Grid &grid, const size_t id const Vec2d v = (segment.second - segment.first).cast(); const Vec2d va = (this->point - segment.first).cast(); const double l2 = v.squaredNorm(); // avoid a sqrt - const double t = (l2 == 0.0) ? 0. : clamp(0., 1., va.dot(v) / l2); + const double t = (l2 == 0.0) ? 0. : std::clamp(va.dot(v) / l2, 0., 1.); // Closest point from this->point to the segment. const Vec2d foot = segment.first.cast() + t * v; const Vec2d bisector = foot - this->point.cast(); diff --git a/src/libslic3r/ExtrusionSimulator.cpp b/src/libslic3r/ExtrusionSimulator.cpp index 8c2ab037f7..6b1f76abea 100644 --- a/src/libslic3r/ExtrusionSimulator.cpp +++ b/src/libslic3r/ExtrusionSimulator.cpp @@ -550,10 +550,10 @@ void gcode_paint_layer( boost::geometry::expand(bboxLine, rect[2]); boost::geometry::expand(bboxLine, rect[3]); B2i bboxLinei( - V2i(clamp(0, nc-1, int(floor(bboxLine.min_corner().x()))), - clamp(0, nr-1, int(floor(bboxLine.min_corner().y())))), - V2i(clamp(0, nc-1, int(ceil (bboxLine.max_corner().x()))), - clamp(0, nr-1, int(ceil (bboxLine.max_corner().y()))))); + V2i(std::clamp(int(floor(bboxLine.min_corner().x())), 0, nc-1), + std::clamp(int(floor(bboxLine.min_corner().y())), 0, nr-1)), + V2i(std::clamp(int(ceil(bboxLine.max_corner().x())), 0, nc-1), + std::clamp(int(ceil(bboxLine.max_corner().y())), 0, nr-1))); // printf("bboxLinei %d,%d %d,%d\n", bboxLinei.min_corner().x(), bboxLinei.min_corner().y(), bboxLinei.max_corner().x(), bboxLinei.max_corner().y()); #ifdef _DEBUG float area = polyArea(rect, 4); @@ -597,10 +597,10 @@ void gcode_paint_bitmap( boost::geometry::expand(bboxLine, rect[2]); boost::geometry::expand(bboxLine, rect[3]); B2i bboxLinei( - V2i(clamp(0, nc-1, int(floor(bboxLine.min_corner().x()))), - clamp(0, nr-1, int(floor(bboxLine.min_corner().y())))), - V2i(clamp(0, nc-1, int(ceil (bboxLine.max_corner().x()))), - clamp(0, nr-1, int(ceil (bboxLine.max_corner().y()))))); + V2i(std::clamp(int(floor(bboxLine.min_corner().x())), 0, nc-1), + std::clamp(int(floor(bboxLine.min_corner().y())), 0, nr-1)), + V2i(std::clamp(int(ceil(bboxLine.max_corner().x())), 0, nc-1), + std::clamp(int(ceil(bboxLine.max_corner().y())), 0, nr-1))); // printf("bboxLinei %d,%d %d,%d\n", bboxLinei.min_corner().x(), bboxLinei.min_corner().y(), bboxLinei.max_corner().x(), bboxLinei.max_corner().y()); for (int j = bboxLinei.min_corner().y(); j + 1 < bboxLinei.max_corner().y(); ++ j) { for (int i = bboxLinei.min_corner().x(); i + 1 < bboxLinei.max_corner().x(); ++i) { @@ -664,10 +664,10 @@ void gcode_spread_points( const float height_target = it->height; B2f bbox(center - V2f(radius, radius), center + V2f(radius, radius)); B2i bboxi( - V2i(clamp(0, nc-1, int(floor(bbox.min_corner().x()))), - clamp(0, nr-1, int(floor(bbox.min_corner().y())))), - V2i(clamp(0, nc-1, int(ceil (bbox.max_corner().x()))), - clamp(0, nr-1, int(ceil (bbox.max_corner().y()))))); + V2i(std::clamp(int(floor(bbox.min_corner().x())), 0, nc-1), + std::clamp(int(floor(bbox.min_corner().y())), 0, nr-1)), + V2i(std::clamp(int(ceil(bbox.max_corner().x())), 0, nc-1), + std::clamp(int(ceil(bbox.max_corner().y())), 0, nr-1))); /* // Fill in the spans, at which the circle intersects the rows. int row_first = bboxi.min_corner().y(); @@ -758,7 +758,7 @@ void gcode_spread_points( area_circle_total += area; if (cell.area < area) cell.area = area; - cell.fraction_covered = clamp(0.f, 1.f, (cell.area > 0) ? (area / cell.area) : 0); + cell.fraction_covered = std::clamp((cell.area > 0) ? (area / cell.area) : 0, 0.f, 1.f); if (cell.fraction_covered == 0) { -- n_cells; continue; @@ -1018,7 +1018,7 @@ void ExtrusionSimulator::evaluate_accumulator(ExtrusionSimulationType simulation float p = mask[r][c]; #endif int idx = int(floor(p * float(pimpl->color_gradient.size()) + 0.5f)); - V3uc clr = pimpl->color_gradient[clamp(0, int(pimpl->color_gradient.size()-1), idx)]; + V3uc clr = pimpl->color_gradient[std::clamp(idx, 0, int(pimpl->color_gradient.size()-1))]; *ptr ++ = clr.get<0>(); *ptr ++ = clr.get<1>(); *ptr ++ = clr.get<2>(); diff --git a/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/src/libslic3r/Fill/Fill3DHoneycomb.cpp index 95c26fbad4..9d4a34b6e8 100644 --- a/src/libslic3r/Fill/Fill3DHoneycomb.cpp +++ b/src/libslic3r/Fill/Fill3DHoneycomb.cpp @@ -55,8 +55,8 @@ static std::vector perpendPoints(const coordf_t offset, const size_t b static inline void trim(Pointfs &pts, coordf_t minX, coordf_t minY, coordf_t maxX, coordf_t maxY) { for (Vec2d &pt : pts) { - pt(0) = clamp(minX, maxX, pt(0)); - pt(1) = clamp(minY, maxY, pt(1)); + pt.x() = std::clamp(pt.x(), minX, maxX); + pt.y() = std::clamp(pt.y(), minY, maxY); } } diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index ff2d049cfd..6917c5d6e1 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -53,7 +53,7 @@ static inline Polyline make_wave( polyline.points.reserve(points.size()); for (auto& point : points) { point(1) += offset; - point(1) = clamp(0., height, double(point(1))); + point(1) = std::clamp(double(point.y()), 0., height); if (vertical) std::swap(point(0), point(1)); polyline.points.emplace_back((point * scaleFactor).cast()); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index a799408109..1108b71bd0 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -796,7 +796,7 @@ namespace DoExport { // get the minimum cross-section used in the print std::vector mm3_per_mm; for (auto object : print.objects()) { - for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < object->num_regions(); ++ region_id) { const PrintRegion* region = print.regions()[region_id]; for (auto layer : object->layers()) { const LayerRegion* layerm = layer->regions()[region_id]; diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp index e00284fe1e..49de854f2a 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp @@ -620,7 +620,7 @@ static std::vector contour_distance(const EdgeGrid::Grid &grid, const Vec2d v = (segment.second - segment.first).cast(); const Vec2d va = (this->point - segment.first).cast(); const double l2 = v.squaredNorm(); // avoid a sqrt - const double t = (l2 == 0.0) ? 0. : clamp(0., 1., va.dot(v) / l2); + const double t = (l2 == 0.0) ? 0. : std::clamp(va.dot(v) / l2, 0., 1.); // Closest point from this->point to the segment. const Vec2d foot = segment.first.cast() + t * v; const Vec2d bisector = foot - this->point.cast(); diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 7f48aae808..df25838754 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -722,8 +722,8 @@ std::string CoolingBuffer::apply_layer_cooldown( if (int(layer_id) >= disable_fan_first_layers && int(layer_id) + 1 < full_fan_speed_layer) { // Ramp up the fan speed from disable_fan_first_layers to full_fan_speed_layer. float factor = float(int(layer_id + 1) - disable_fan_first_layers) / float(full_fan_speed_layer - disable_fan_first_layers); - fan_speed_new = clamp(0, 255, int(float(fan_speed_new ) * factor + 0.5f)); - bridge_fan_speed = clamp(0, 255, int(float(bridge_fan_speed) * factor + 0.5f)); + fan_speed_new = std::clamp(int(float(fan_speed_new) * factor + 0.5f), 0, 255); + bridge_fan_speed = std::clamp(int(float(bridge_fan_speed) * factor + 0.5f), 0, 255); } #undef EXTRUDER_CONFIG bridge_fan_control = bridge_fan_speed > fan_speed_new; diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index c45e260158..aaa35aebe9 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -223,7 +223,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto layer_tools.extruder_override = extruder_override; // What extruders are required to print this object layer? - for (size_t region_id = 0; region_id < object.region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < object.num_regions(); ++ region_id) { const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr; if (layerm == nullptr) continue; @@ -689,7 +689,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int // iterate through copies (aka PrintObject instances) first, so that we mark neighbouring infills to minimize travel moves for (unsigned int copy = 0; copy < num_of_copies; ++copy) { - for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < object->num_regions(); ++ region_id) { const auto& region = *object->print()->regions()[region_id]; if (!region.config().wipe_into_infill && !object->config().wipe_into_objects) @@ -762,7 +762,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) size_t num_of_copies = object->instances().size(); for (size_t copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves - for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < object->num_regions(); ++ region_id) { const auto& region = *object->print()->regions()[region_id]; if (!region.config().wipe_into_infill && !object->config().wipe_into_objects) diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index d4840a363a..865e0aaa4d 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -63,8 +63,8 @@ static bool project_line_on_line(const Line &projection_l, const Line &projected assert(t1 <= 1.); assert(t2 <= 1.); - Point p1 = (projection_l.a.cast() + t1 * v1).cast(); - Point p2 = (projection_l.a.cast() + t2 * v1).cast(); + Point p1 = projection_l.a + (t1 * v1).cast(); + Point p2 = projection_l.a + (t2 * v1).cast(); *new_projected = Line(p1, p2); return true; } @@ -74,7 +74,7 @@ struct PaintedLine size_t contour_idx; size_t line_idx; Line projected_line; - int color = 1; + int color; }; struct PaintedLineVisitor @@ -89,35 +89,31 @@ struct PaintedLineVisitor bool operator()(coord_t iy, coord_t ix) { // Called with a row and column of the grid cell, which is intersected by a line. - auto cell_data_range = grid.cell_data_range(iy, ix); + auto cell_data_range = grid.cell_data_range(iy, ix); + const Vec2d v1 = line_to_test.vector().cast(); for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++it_contour_and_segment) { - Line grid_line = grid.line(*it_contour_and_segment); - - const Vec2d v1 = (line_to_test.b - line_to_test.a).cast().normalized(); - const Vec2d v2 = (grid_line.b - grid_line.a).cast().normalized(); - double angle = ::acos(clamp(-1.0, 1.0, v1.dot(v2))); - double angle_deg = Geometry::rad2deg(angle); + Line grid_line = grid.line(*it_contour_and_segment); + const Vec2d v2 = grid_line.vector().cast(); // When lines have too different length, it is necessary to normalize them - if ((angle_deg >= 0 && angle_deg <= 30) || (angle_deg >= 150)) { - Line line_to_test_projected; - project_line_on_line(grid_line, line_to_test, &line_to_test_projected); - + if (Slic3r::sqr(v1.dot(v2)) > cos_threshold2 * v1.squaredNorm() * v2.squaredNorm()) { + // The two vectors are nearly collinear (their mutual angle is lower than 30 degrees) if (painted_lines_set.find(*it_contour_and_segment) == painted_lines_set.end()) { - if (Line(grid_line.a, line_to_test_projected.a).length() > Line(grid_line.a, line_to_test_projected.b).length()) { - line_to_test_projected.reverse(); - } - double dist_1 = grid_line.distance_to(line_to_test.a); double dist_2 = grid_line.distance_to(line_to_test.b); double dist_3 = line_to_test.distance_to(grid_line.a); double dist_4 = line_to_test.distance_to(grid_line.b); double total_dist = std::min(std::min(dist_1, dist_2), std::min(dist_3, dist_4)); - if (total_dist > 50 * SCALED_EPSILON) - continue; + if (total_dist < 50 * SCALED_EPSILON) { + Line line_to_test_projected; + project_line_on_line(grid_line, line_to_test, &line_to_test_projected); - painted_lines.push_back({it_contour_and_segment->first, it_contour_and_segment->second, line_to_test_projected, this->color}); - painted_lines_set.insert(*it_contour_and_segment); + if (Line(grid_line.a, line_to_test_projected.a).length() > Line(grid_line.a, line_to_test_projected.b).length()) { + line_to_test_projected.reverse(); + } + painted_lines.push_back({it_contour_and_segment->first, it_contour_and_segment->second, line_to_test_projected, this->color}); + painted_lines_set.insert(*it_contour_and_segment); + } } } } @@ -130,13 +126,15 @@ struct PaintedLineVisitor Line line_to_test; std::unordered_set, boost::hash>> painted_lines_set; int color = -1; + + static inline const double cos_threshold2 = Slic3r::sqr(cos(M_PI * 30. / 180.)); }; static std::vector to_colored_lines(const Polygon &polygon, int color) { std::vector lines; - lines.reserve(polygon.points.size()); if (polygon.points.size() > 2) { + lines.reserve(polygon.points.size()); for (auto it = polygon.points.begin(); it != polygon.points.end() - 1; ++it) lines.push_back({Line(*it, *(it + 1)), color}); lines.push_back({Line(polygon.points.back(), polygon.points.front()), color}); @@ -146,10 +144,11 @@ static std::vector to_colored_lines(const Polygon &polygon, int col static Polygon colored_points_to_polygon(const std::vector &lines) { - Points out; + Polygon out; + out.points.reserve(lines.size()); for (const ColoredLine &l : lines) - out.emplace_back(l.line.a); - return Polygon(out); + out.points.emplace_back(l.line.a); + return out; } static Polygons colored_points_to_polygon(const std::vector> &lines) @@ -160,7 +159,8 @@ static Polygons colored_points_to_polygon(const std::vector to_lines(const std::vector> &c_lines) +// Flatten the vector of vectors into a vector. +static inline std::vector to_lines(const std::vector> &c_lines) { size_t n_lines = 0; for (const auto &c_line : c_lines) @@ -173,8 +173,7 @@ inline std::vector to_lines(const std::vector -inline bool vertex_equal_to_point(const VertexType &vertex, const Point &ipt) +static bool vertex_equal_to_point(const Voronoi::VD::vertex_type &vertex, const Point &ipt) { // Convert ipt to doubles, force the 80bit FPU temporary to 64bit and then compare. // This should work with any settings of math compiler switches and the C++ compiler @@ -186,7 +185,7 @@ inline bool vertex_equal_to_point(const VertexType &vertex, const Point &ipt) ulp_cmp(vertex.y(), double(ipt.y()), ULPS) == ulp_cmp_type::EQUAL; } -bool vertex_equal_to_point(const Voronoi::VD::vertex_type *vertex, const Point &ipt) { +static inline bool vertex_equal_to_point(const Voronoi::VD::vertex_type *vertex, const Point &ipt) { return vertex_equal_to_point(*vertex, ipt); } @@ -967,7 +966,7 @@ static std::vector> extract_colored_segments(MMU_Grap Vec2d process_line_vec_n = (process_line.a - process_line.b).cast().normalized(); Vec2d neighbour_line_vec_n = (graph.nodes[arc.to_idx].point - graph.nodes[arc.from_idx].point).cast().normalized(); - double angle = ::acos(clamp(-1.0, 1.0, neighbour_line_vec_n.dot(process_line_vec_n))); + double angle = ::acos(std::clamp(neighbour_line_vec_n.dot(process_line_vec_n), -1.0, 1.0)); if (Slic3r::cross2(neighbour_line_vec_n, process_line_vec_n) < 0.0) angle = 2.0 * (double) PI - angle; @@ -1050,7 +1049,7 @@ static inline double compute_edge_length(MMU_Graph &graph, size_t start_idx, MMU Vec2d second_line_vec = (second_line.b - second_line.a).cast(); Vec2d first_line_vec_n = first_line_vec.normalized(); Vec2d second_line_vec_n = second_line_vec.normalized(); - double angle = ::acos(clamp(-1.0, 1.0, first_line_vec_n.dot(second_line_vec_n))); + double angle = ::acos(std::clamp(first_line_vec_n.dot(second_line_vec_n), -1.0, 1.0)); if (Slic3r::cross2(first_line_vec_n, second_line_vec_n) < 0.0) angle = 2.0 * (double) PI - angle; @@ -1276,9 +1275,9 @@ static inline std::vector> mmu_segmentation_top_and_bott for (size_t layer_idx = 0; layer_idx < print_object.layers().size(); ++layer_idx) { BOOST_LOG_TRIVIAL(debug) << "MMU segmentation of bottom layer: " << layer_idx; - float extrusion_width = scale_(get_extrusion_width(layer_idx)); - int bottom_solid_layers = get_bottom_solid_layers(layer_idx); - ExPolygons bottom_expolygon = bottom_layers[layer_idx]; + float extrusion_width = scale_(get_extrusion_width(layer_idx)); + int bottom_solid_layers = get_bottom_solid_layers(layer_idx); + const ExPolygons &bottom_expolygon = bottom_layers[layer_idx]; if (bottom_expolygon.empty()) continue; @@ -1301,8 +1300,7 @@ static inline std::vector> mmu_segmentation_top_and_bott ExPolygons offset_e = offset_ex(layer_slices_trimmed, offset_value); ExPolygons intersection_poly_2 = intersection_ex(triangles_by_color_bottom[color_idx][layer_idx], offset_e); - triangles_by_color_bottom[color_idx][last_idx].insert(triangles_by_color_bottom[color_idx][last_idx].end(), intersection_poly_2.begin(), - intersection_poly_2.end()); + append(triangles_by_color_bottom[color_idx][last_idx], std::move(intersection_poly_2)); } } } @@ -1312,13 +1310,10 @@ static inline std::vector> mmu_segmentation_top_and_bott triangles_by_color_merged.assign(3, std::vector(layers.size())); for (size_t layer_idx = 0; layer_idx < layers.size(); ++layer_idx) { for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) { - triangles_by_color_merged[color_idx][layer_idx].insert(triangles_by_color_merged[color_idx][layer_idx].end(), - triangles_by_color_bottom[color_idx][layer_idx].begin(), - triangles_by_color_bottom[color_idx][layer_idx].end()); - triangles_by_color_merged[color_idx][layer_idx].insert(triangles_by_color_merged[color_idx][layer_idx].end(), - triangles_by_color_top[color_idx][layer_idx].begin(), - triangles_by_color_top[color_idx][layer_idx].end()); - triangles_by_color_merged[color_idx][layer_idx] = union_ex(triangles_by_color_merged[color_idx][layer_idx]); + auto &self = triangles_by_color_merged[color_idx][layer_idx]; + append(self, std::move(triangles_by_color_bottom[color_idx][layer_idx])); + append(self, std::move(triangles_by_color_top[color_idx][layer_idx])); + self = union_ex(self); } // Cut all colors for cases when two colors are overlapping @@ -1332,7 +1327,7 @@ static inline std::vector> mmu_segmentation_top_and_bott } static std::vector>> merge_segmented_layers( - const std::vector>> &segmented_regions, const std::vector> &top_and_bottom_layers) + const std::vector>> &segmented_regions, std::vector> &&top_and_bottom_layers) { std::vector>> segmented_regions_merged(segmented_regions.size()); @@ -1344,14 +1339,12 @@ static std::vector>> merge_segmented_la for (const std::vector &top_and_bottom_layer : top_and_bottom_layers) cut_colored_expoly = diff_ex(cut_colored_expoly, top_and_bottom_layer[layer_idx]); for (ExPolygon &ex_poly : cut_colored_expoly) - segmented_regions_merged[layer_idx].emplace_back(ex_poly, colored_expoly.second); - + segmented_regions_merged[layer_idx].emplace_back(std::move(ex_poly), colored_expoly.second); } - for (size_t color_idx = 0; color_idx < top_and_bottom_layers.size(); ++color_idx) { - ExPolygons top_and_bottom_expoly = top_and_bottom_layers[color_idx][layer_idx]; - for (const ExPolygon &expoly : top_and_bottom_expoly) { segmented_regions_merged[layer_idx].emplace_back(expoly, color_idx); } - } + for (size_t color_idx = 0; color_idx < top_and_bottom_layers.size(); ++color_idx) + for (ExPolygon &expoly : top_and_bottom_layers[color_idx][layer_idx]) + segmented_regions_merged[layer_idx].emplace_back(std::move(expoly), color_idx); } }); // end of parallel_for @@ -1385,7 +1378,6 @@ std::vector>> multi_material_segmentati float max_z = std::numeric_limits::lowest(); std::array facet; - Points projected_facet(3); for (int p_idx = 0; p_idx < 3; ++p_idx) { facet[p_idx] = tr * custom_facets.vertices[custom_facets.indices[facet_idx](p_idx)]; max_z = std::max(max_z, facet[p_idx].z()); @@ -1395,13 +1387,6 @@ std::vector>> multi_material_segmentati // Sort the vertices by z-axis for simplification of projected_facet on slices std::sort(facet.begin(), facet.end(), [](const Vec3f &p1, const Vec3f &p2) { return p1.z() < p2.z(); }); - for (int p_idx = 0; p_idx < 3; ++p_idx) { - projected_facet[p_idx] = Point(scale_(facet[p_idx].x()), scale_(facet[p_idx].y())); - projected_facet[p_idx] = projected_facet[p_idx] - print_object.center_offset(); - } - - ExPolygon triangle = ExPolygon(projected_facet); - // Find lowest slice not below the triangle. auto first_layer = std::upper_bound(print_object.layers().begin(), print_object.layers().end(), float(min_z - EPSILON), [](float z, const Layer *l1) { return z < l1->slice_z; }); @@ -1443,8 +1428,7 @@ std::vector>> multi_material_segmentati visitor.color = params.second; edge_grids[layer_idx].visit_cells_intersecting_line(line_start, line_end, visitor); - if (!painted_line_tmp.empty()) - painted_lines[layer_idx].insert(painted_lines[layer_idx].end(), painted_line_tmp.begin(), painted_line_tmp.end()); + append(painted_lines[layer_idx], std::move(painted_line_tmp)); } } } @@ -1454,7 +1438,7 @@ std::vector>> multi_material_segmentati for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { // for(size_t layer_idx = 0; layer_idx < print_object.layers().size(); ++layer_idx) { BOOST_LOG_TRIVIAL(debug) << "MMU segmentation of layer: " << layer_idx; - auto comp = [&edge_grids, &layer_idx](const PaintedLine &first, const PaintedLine &second) { + auto comp = [&edge_grids, layer_idx](const PaintedLine &first, const PaintedLine &second) { Point first_start_p = *(edge_grids[layer_idx].contours()[first.contour_idx].begin() + first.line_idx); return first.contour_idx < second.contour_idx || @@ -1469,11 +1453,8 @@ std::vector>> multi_material_segmentati if (!painted_lines_single.empty()) { Polygons original_polygons; - for (const Slic3r::EdgeGrid::Contour &contour : edge_grids[layer_idx].contours()) { - Points points; - for (const Point &point : contour) points.emplace_back(point); - original_polygons.emplace_back(points); - } + for (const Slic3r::EdgeGrid::Contour &contour : edge_grids[layer_idx].contours()) + original_polygons.emplace_back(Points(contour.begin(), contour.end())); std::vector> color_poly = colorize_polygons(original_polygons, painted_lines_single); MMU_Graph graph = build_graph(layer_idx, color_poly); @@ -1491,7 +1472,7 @@ std::vector>> multi_material_segmentati // return segmented_regions; std::vector> top_and_bottom_layers = mmu_segmentation_top_and_bottom_layers(print_object); - std::vector>> segmented_regions_merged = merge_segmented_layers(segmented_regions, top_and_bottom_layers); + std::vector>> segmented_regions_merged = merge_segmented_layers(segmented_regions, std::move(top_and_bottom_layers)); return segmented_regions_merged; } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index c8ab9b2c27..66dc5de5d7 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1066,7 +1066,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ assert(it_status->status != ModelObjectStatus::Deleted); layer_ranges = &it_status->layer_ranges; } - if (region_id < print_object->region_volumes.size()) { + if (region_id < print_object->num_regions()) { for (const std::pair &volume_and_range : print_object->region_volumes[region_id]) { const ModelVolume &volume = *print_object->model_object()->volumes[volume_and_range.second]; const DynamicPrintConfig *layer_range_config = layer_ranges->config(volume_and_range.first); @@ -1106,7 +1106,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ if (! diff.empty()) { // Stop the background process before assigning new configuration to the regions. for (PrintObject *print_object : m_objects) - if (region_id < print_object->region_volumes.size() && ! print_object->region_volumes[region_id].empty()) + if (print_object->has_region(region_id)) update_apply_status(print_object->invalidate_state_by_config_options(region.config(), this_region_config, diff)); region.config_apply_only(this_region_config, diff, false); } @@ -1169,7 +1169,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ region_id = regions_in_object[idx_region_in_object ++]; // Assign volume to a region. if (fresh) { - if ((size_t)region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty()) + if (! print_object.has_region(size_t(region_id))) ++ m_regions[region_id]->m_refcnt; print_object.add_region_volume(region_id, volume_id, it_range->first); } @@ -1506,7 +1506,7 @@ std::string Print::validate(std::string* warning) const if ((object->has_support() || object->has_raft()) && ! validate_extrusion_width(object->config(), "support_material_extrusion_width", layer_height, err_msg)) return err_msg; for (const char *opt_key : { "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", "top_infill_extrusion_width" }) - for (size_t i = 0; i < object->region_volumes.size(); ++ i) + for (size_t i = 0; i < object->num_regions(); ++ i) if (! object->region_volumes[i].empty() && ! validate_extrusion_width(this->get_region(i)->config(), opt_key, layer_height, err_msg)) return err_msg; } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 91f86d0105..70debefb67 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -177,6 +177,8 @@ public: bool has_brim() const { return this->config().brim_type != btNoBrim && this->config().brim_width.value > 0.; } + size_t num_regions() const { return this->region_volumes.size(); } + bool has_region(size_t i) const { return i < this->region_volumes.size() && ! this->region_volumes[i].empty(); } // adds region_id, too, if necessary void add_region_volume(unsigned int region_id, int volume_id, const t_layer_height_range &layer_range) { @@ -471,6 +473,7 @@ public: return (it == m_objects.end()) ? nullptr : *it; } ConstPrintRegionPtrsAdaptor regions() const { return ConstPrintRegionPtrsAdaptor(&m_regions); } + size_t num_regions() const { return m_regions.size(); } // How many of PrintObject::copies() over all print objects are there? // If zero, then the print is empty and the print shall not be executed. unsigned int num_object_instances() const; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index f85568ba59..a1d8f65bfe 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -174,7 +174,7 @@ void PrintObject::make_perimeters() // but we don't generate any extra perimeter if fill density is zero, as they would be floating // inside the object - infill_only_where_needed should be the method of choice for printing // hollow objects - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->num_regions(); ++ region_id) { const PrintRegion ®ion = *m_print->regions()[region_id]; if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2) continue; @@ -295,7 +295,7 @@ void PrintObject::prepare_infill() // Debugging output. #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->num_regions(); ++ region_id) { for (const Layer *layer : m_layers) { LayerRegion *layerm = layer->m_regions[region_id]; layerm->export_region_slices_to_svg_debug("6_discover_vertical_shells-final"); @@ -314,7 +314,7 @@ void PrintObject::prepare_infill() m_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->num_regions(); ++ region_id) { for (const Layer *layer : m_layers) { LayerRegion *layerm = layer->m_regions[region_id]; layerm->export_region_slices_to_svg_debug("7_discover_horizontal_shells-final"); @@ -333,7 +333,7 @@ void PrintObject::prepare_infill() m_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->num_regions(); ++ region_id) { for (const Layer *layer : m_layers) { LayerRegion *layerm = layer->m_regions[region_id]; layerm->export_region_slices_to_svg_debug("8_clip_surfaces-final"); @@ -352,7 +352,7 @@ void PrintObject::prepare_infill() m_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->num_regions(); ++ region_id) { for (const Layer *layer : m_layers) { LayerRegion *layerm = layer->m_regions[region_id]; layerm->export_region_slices_to_svg_debug("9_prepare_infill-final"); @@ -744,7 +744,7 @@ bool PrintObject::invalidate_all_steps() static const PrintRegion* first_printing_region(const PrintObject &print_object) { - for (size_t idx_region = 0; idx_region < print_object.region_volumes.size(); ++ idx_region) + for (size_t idx_region = 0; idx_region < print_object.num_regions(); ++ idx_region) if (!print_object.region_volumes.empty()) return print_object.print()->regions()[idx_region]; return nullptr; @@ -772,7 +772,7 @@ void PrintObject::detect_surfaces_type() bool interface_shells = ! spiral_vase && m_config.interface_shells.value; size_t num_layers = spiral_vase ? std::min(size_t(first_printing_region(*this)->config().bottom_solid_layers), m_layers.size()) : m_layers.size(); - for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) { + for (size_t idx_region = 0; idx_region < this->num_regions(); ++ idx_region) { BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " in parallel - start"; #ifdef SLIC3R_DEBUG_SLICE_PROCESSING for (Layer *layer : m_layers) @@ -971,7 +971,7 @@ void PrintObject::process_external_surfaces() // Is there any printing region, that has zero infill? If so, then we don't want the expansion to be performed over the complete voids, but only // over voids, which are supported by the layer below. bool has_voids = false; - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) + for (size_t region_id = 0; region_id < this->num_regions(); ++ region_id) if (! this->region_volumes.empty() && this->print()->regions()[region_id]->config().fill_density == 0) { has_voids = true; break; @@ -1021,7 +1021,7 @@ void PrintObject::process_external_surfaces() BOOST_LOG_TRIVIAL(debug) << "Collecting surfaces covered with extrusions in parallel - end"; } - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++region_id) { + for (size_t region_id = 0; region_id < this->num_regions(); ++region_id) { BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - start"; tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), @@ -1069,13 +1069,13 @@ void PrintObject::discover_vertical_shells() num_extra_layers(config.bottom_solid_layers, config.bottom_solid_min_thickness) > 0; }; std::vector cache_top_botom_regions(num_layers, DiscoverVerticalShellsCacheEntry()); - bool top_bottom_surfaces_all_regions = this->region_volumes.size() > 1 && ! m_config.interface_shells.value; + bool top_bottom_surfaces_all_regions = this->num_regions() > 1 && ! m_config.interface_shells.value; if (top_bottom_surfaces_all_regions) { // This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness // is calculated over all materials. // Is the "ensure vertical wall thickness" applicable to any region? bool has_extra_layers = false; - for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++idx_region) { + for (size_t idx_region = 0; idx_region < this->num_regions(); ++idx_region) { const PrintRegionConfig &config = m_print->get_region(idx_region)->config(); if (config.ensure_vertical_shell_thickness.value && has_extra_layers_fn(config)) { has_extra_layers = true; @@ -1092,7 +1092,7 @@ void PrintObject::discover_vertical_shells() tbb::blocked_range(0, num_layers, grain_size), [this, &cache_top_botom_regions](const tbb::blocked_range& range) { const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; - const size_t num_regions = this->region_volumes.size(); + const size_t num_regions = this->num_regions(); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { m_print->throw_if_canceled(); const Layer &layer = *m_layers[idx_layer]; @@ -1153,7 +1153,7 @@ void PrintObject::discover_vertical_shells() BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom"; } - for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) { + for (size_t idx_region = 0; idx_region < this->num_regions(); ++ idx_region) { PROFILE_BLOCK(discover_vertical_shells_region); const PrintRegion ®ion = *m_print->get_region(idx_region); @@ -1458,7 +1458,7 @@ void PrintObject::bridge_over_infill() { BOOST_LOG_TRIVIAL(info) << "Bridge over infill..." << log_memory_info(); - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->num_regions(); ++ region_id) { const PrintRegion ®ion = *m_print->regions()[region_id]; // skip bridging in case there are no voids @@ -1685,9 +1685,9 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full std::vector PrintObject::object_extruders() const { std::vector extruders; - extruders.reserve(this->region_volumes.size() * 3); - for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) - if (! this->region_volumes[idx_region].empty()) + extruders.reserve(this->num_regions() * 3); + for (size_t idx_region = 0; idx_region < this->num_regions(); ++ idx_region) + if (this->has_region(idx_region)) m_print->get_region(idx_region)->collect_object_printing_extruders(extruders); sort_remove_duplicates(extruders); return extruders; @@ -1756,7 +1756,7 @@ void PrintObject::_slice(const std::vector &layer_height_profile) layer->lower_layer = prev; } // Make sure all layers contain layer region objects for all regions. - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) + for (size_t region_id = 0; region_id < this->num_regions(); ++ region_id) layer->add_region(this->print()->get_region(region_id)); prev = layer; } @@ -1767,7 +1767,7 @@ void PrintObject::_slice(const std::vector &layer_height_profile) bool has_z_ranges = false; size_t num_volumes = 0; size_t num_modifiers = 0; - for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) { + for (int region_id = 0; region_id < (int)this->num_regions(); ++ region_id) { int last_volume_id = -1; for (const std::pair &volume_and_range : this->region_volumes[region_id]) { const int volume_id = volume_and_range.second; @@ -1799,7 +1799,7 @@ void PrintObject::_slice(const std::vector &layer_height_profile) if (! has_z_ranges && (! m_config.clip_multipart_objects.value || all_volumes_single_region >= 0)) { // Cheap path: Slice regions without mutual clipping. // The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region. - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->num_regions(); ++ region_id) { BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id; // slicing in parallel size_t slicing_mode_normal_below_layer = 0; @@ -1832,7 +1832,7 @@ void PrintObject::_slice(const std::vector &layer_height_profile) }; std::vector sliced_volumes; sliced_volumes.reserve(num_volumes); - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->num_regions(); ++ region_id) { const std::vector> &volumes_and_ranges = this->region_volumes[region_id]; for (size_t i = 0; i < volumes_and_ranges.size(); ) { int volume_id = volumes_and_ranges[i].second; @@ -1884,7 +1884,7 @@ void PrintObject::_slice(const std::vector &layer_height_profile) } } // Collect and union volumes of a single region. - for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) { + for (int region_id = 0; region_id < (int)this->num_regions(); ++ region_id) { ExPolygons expolygons; size_t num_volumes = 0; for (SlicedVolume &sliced_volume : sliced_volumes) @@ -1915,7 +1915,7 @@ void PrintObject::_slice(const std::vector &layer_height_profile) this->m_layers[layer_idx]->lslices = union_ex(ex_polygons); } - size_t region_count_before_change = this->region_volumes.size(); + size_t region_count_before_change = this->num_regions(); std::vector>> segmented_regions = multi_material_segmentation_by_painting(*this); // Skip region with default extruder for (size_t region_idx = 1; region_idx < 3; ++region_idx) { @@ -1944,15 +1944,15 @@ void PrintObject::_slice(const std::vector &layer_height_profile) for (size_t i_layer = 0; i_layer < m_layers.size(); i_layer += 1) { Layer *layer = m_layers[i_layer]; // Make sure all layers contain layer region objects for all regions. - for (size_t region_id = region_count_before_change; region_id < this->region_volumes.size(); ++ region_id) + for (size_t region_id = region_count_before_change; region_id < this->num_regions(); ++ region_id) layer->add_region(this->print()->get_region(region_id)); } // --------------------MMU_SEGMENTATION_END---------------------- // Slice all modifier volumes. - if (this->region_volumes.size() > 1) { - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + if (this->num_regions() > 1) { + for (size_t region_id = 0; region_id < this->num_regions(); ++ region_id) { BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id; // slicing in parallel std::vector expolygons_by_layer = this->slice_modifiers(region_id, slice_zs); @@ -1965,7 +1965,7 @@ void PrintObject::_slice(const std::vector &layer_height_profile) tbb::blocked_range(0, m_layers.size()), [this, &expolygons_by_layer, region_id](const tbb::blocked_range& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { - for (size_t other_region_id = 0; other_region_id < this->region_volumes.size(); ++ other_region_id) { + for (size_t other_region_id = 0; other_region_id < this->num_regions(); ++ other_region_id) { if (region_id == other_region_id) continue; Layer *layer = m_layers[layer_id]; @@ -2106,7 +2106,7 @@ end: std::vector PrintObject::slice_region(size_t region_id, const std::vector &z, SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below) const { std::vector volumes; - if (region_id < this->region_volumes.size()) { + if (region_id < this->num_regions()) { for (const std::pair &volume_and_range : this->region_volumes[region_id]) { const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second]; if (volume->is_model_part()) @@ -2120,7 +2120,7 @@ std::vector PrintObject::slice_region(size_t region_id, const std::v std::vector PrintObject::slice_modifiers(size_t region_id, const std::vector &slice_zs) const { std::vector out; - if (region_id < this->region_volumes.size()) + if (region_id < this->num_regions()) { std::vector> volume_ranges; const std::vector> &volumes_and_ranges = this->region_volumes[region_id]; @@ -2174,7 +2174,7 @@ std::vector PrintObject::slice_modifiers(size_t region_id, const std } else { // Some modifier in this region was split to layer spans. std::vector merge; - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->num_regions(); ++ region_id) { const std::vector> &volumes_and_ranges = this->region_volumes[region_id]; for (size_t i = 0; i < volumes_and_ranges.size(); ) { int volume_id = volumes_and_ranges[i].second; @@ -2544,7 +2544,7 @@ void PrintObject::discover_horizontal_shells() { BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()"; - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->num_regions(); ++ region_id) { for (size_t i = 0; i < m_layers.size(); ++ i) { m_print->throw_if_canceled(); Layer *layer = m_layers[i]; @@ -2730,7 +2730,7 @@ void PrintObject::discover_horizontal_shells() } // for each region #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->num_regions(); ++ region_id) { for (const Layer *layer : m_layers) { const LayerRegion *layerm = layer->m_regions[region_id]; layerm->export_region_slices_to_svg_debug("5_discover_horizontal_shells"); @@ -2746,7 +2746,7 @@ void PrintObject::discover_horizontal_shells() void PrintObject::combine_infill() { // Work on each region separately. - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { + for (size_t region_id = 0; region_id < this->num_regions(); ++ region_id) { const PrintRegion *region = this->print()->regions()[region_id]; const size_t every = region->config().infill_every_layers.value; if (every < 2 || region->config().fill_density == 0.) diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index d0b1e9ce26..87ffdf8503 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -298,7 +298,7 @@ std::vector layer_height_profile_adaptive(const SlicingParameters& slici if (z_gap > 0.0) { layer_height_profile.push_back(slicing_params.object_print_z_height()); - layer_height_profile.push_back(clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, z_gap)); + layer_height_profile.push_back(std::clamp(z_gap, slicing_params.min_layer_height, slicing_params.max_layer_height)); } return layer_height_profile; @@ -376,7 +376,7 @@ std::vector smooth_height_profile(const std::vector& profile, co } } - height = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, (weight_total != 0.0) ? height /= weight_total : hi); + height = std::clamp((weight_total != 0.0) ? height /= weight_total : hi, slicing_params.min_layer_height, slicing_params.max_layer_height); if (smoothing_params.keep_min) height = std::min(height, hi); } @@ -502,7 +502,7 @@ void adjust_layer_height_profile( assert(false); break; } - height = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, height); + height = std::clamp(height, slicing_params.min_layer_height, slicing_params.max_layer_height); if (zz == z_span_variable.second) { // This is the last point of the profile. if (profile_new[profile_new.size() - 2] + EPSILON > zz) { @@ -670,11 +670,11 @@ int generate_layer_height_texture( assert(mid <= slicing_params.object_print_z_height()); coordf_t h = hi - lo; hi = std::min(hi, slicing_params.object_print_z_height()); - int cell_first = clamp(0, ncells-1, int(ceil(lo * z_to_cell))); - int cell_last = clamp(0, ncells-1, int(floor(hi * z_to_cell))); + int cell_first = std::clamp(int(ceil(lo * z_to_cell)), 0, ncells-1); + int cell_last = std::clamp(int(floor(hi * z_to_cell)), 0, ncells-1); for (int cell = cell_first; cell <= cell_last; ++ cell) { coordf_t idxf = (0.5 * hscale + (h - slicing_params.layer_height)) * coordf_t(palette_raw.size()-1) / hscale; - int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf))); + int idx1 = std::clamp(int(floor(idxf)), 0, int(palette_raw.size() - 1)); int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1); coordf_t t = idxf - coordf_t(idx1); const Vec3crd &color1 = palette_raw[idx1]; @@ -693,9 +693,9 @@ int generate_layer_height_texture( assert(row >= 0 && row < rows); assert(col >= 0 && col < cols); unsigned char *ptr = (unsigned char*)data + (row * cols + col) * 4; - ptr[0] = (unsigned char)clamp(0, 255, int(floor(color(0) + 0.5))); - ptr[1] = (unsigned char)clamp(0, 255, int(floor(color(1) + 0.5))); - ptr[2] = (unsigned char)clamp(0, 255, int(floor(color(2) + 0.5))); + ptr[0] = (unsigned char)std::clamp(int(floor(color(0) + 0.5)), 0, 255); + ptr[1] = (unsigned char)std::clamp(int(floor(color(1) + 0.5)), 0, 255); + ptr[2] = (unsigned char)std::clamp(int(floor(color(2) + 0.5)), 0, 255); ptr[3] = 255; if (col == 0 && row > 0) { // Duplicate the first value in a row as a last value of the preceding row. @@ -706,11 +706,11 @@ int generate_layer_height_texture( } } if (level_of_detail_2nd_level) { - cell_first = clamp(0, ncells1-1, int(ceil(lo * z_to_cell1))); - cell_last = clamp(0, ncells1-1, int(floor(hi * z_to_cell1))); + cell_first = std::clamp(int(ceil(lo * z_to_cell1)), 0, ncells1-1); + cell_last = std::clamp(int(floor(hi * z_to_cell1)), 0, ncells1-1); for (int cell = cell_first; cell <= cell_last; ++ cell) { coordf_t idxf = (0.5 * hscale + (h - slicing_params.layer_height)) * coordf_t(palette_raw.size()-1) / hscale; - int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf))); + int idx1 = std::clamp(int(floor(idxf)), 0, int(palette_raw.size() - 1)); int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1); coordf_t t = idxf - coordf_t(idx1); const Vec3crd &color1 = palette_raw[idx1]; @@ -725,9 +725,9 @@ int generate_layer_height_texture( assert(row >= 0 && row < rows/2); assert(col >= 0 && col < cols/2); unsigned char *ptr = data1 + (row * cols1 + col) * 4; - ptr[0] = (unsigned char)clamp(0, 255, int(floor(color(0) + 0.5))); - ptr[1] = (unsigned char)clamp(0, 255, int(floor(color(1) + 0.5))); - ptr[2] = (unsigned char)clamp(0, 255, int(floor(color(2) + 0.5))); + ptr[0] = (unsigned char)std::clamp(int(floor(color(0) + 0.5)), 0, 255); + ptr[1] = (unsigned char)std::clamp(int(floor(color(1) + 0.5)), 0, 255); + ptr[2] = (unsigned char)std::clamp(int(floor(color(2) + 0.5)), 0, 255); ptr[3] = 255; if (col == 0 && row > 0) { // Duplicate the first value in a row as a last value of the preceding row. diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 08cd04b909..ca0b058f54 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -347,8 +347,8 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object coordf_t external_perimeter_width = 0.; size_t num_nonempty_regions = 0; coordf_t bridge_flow_ratio = 0; - for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) - if (! object->region_volumes[region_id].empty()) { + for (size_t region_id = 0; region_id < object->num_regions(); ++ region_id) + if (object->has_region(region_id)) { ++ num_nonempty_regions; const PrintRegion ®ion = *object->print()->get_region(region_id); external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(*object, frExternalPerimeter, slicing_params.layer_height).width())); diff --git a/src/libslic3r/VoronoiOffset.cpp b/src/libslic3r/VoronoiOffset.cpp index 2108388f50..e8d13a6ad0 100644 --- a/src/libslic3r/VoronoiOffset.cpp +++ b/src/libslic3r/VoronoiOffset.cpp @@ -41,7 +41,7 @@ namespace detail { // Degenerate to a single closest point. t = - b / (2. * a); assert(t >= - EPSILON && t <= 1. + EPSILON); - return Slic3r::clamp(0., 1., t); + return std::clamp(t, 0., 1.); } else { u = sqrt(u); out.first = 2; @@ -1142,7 +1142,7 @@ std::vector edge_offset_contour_intersections( #endif // NDEBUG if (! bisector || (dmin != dmax && offset_distance >= dmin)) { double t = (offset_distance - dmin) / (dmax - dmin); - t = clamp(0., 1., t); + t = std::clamp(t, 0., 1.); if (d1 < d0) { out[edge_idx2] = Slic3r::lerp(vertex_point(v1), vertex_point(v0), t); // mark visited diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index efdecbac8f..5bea6d092c 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -239,26 +239,20 @@ template inline bool one_of(const T& v, const std::initializer_list< { return contains(il, v); } template -static inline T sqr(T x) +constexpr inline T sqr(T x) { return x * x; } -template -static inline T clamp(const T low, const T high, const T value) -{ - return std::max(low, std::min(high, value)); -} - template -static inline T lerp(const T& a, const T& b, Number t) +constexpr inline T lerp(const T& a, const T& b, Number t) { assert((t >= Number(-EPSILON)) && (t <= Number(1) + Number(EPSILON))); return (Number(1) - t) * a + t * b; } template -static inline bool is_approx(Number value, Number test_value) +constexpr inline bool is_approx(Number value, Number test_value) { return std::fabs(double(value) - double(test_value)) < double(EPSILON); } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 105eeab06b..3a9fc526d6 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -271,7 +271,7 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const ImGui::SameLine(); float widget_align = ImGui::GetCursorPosX(); ImGui::PushItemWidth(imgui.get_style_scaling() * 120.0f); - m_adaptive_quality = clamp(0.0f, 1.f, m_adaptive_quality); + m_adaptive_quality = std::clamp(m_adaptive_quality, 0.0f, 1.f); ImGui::SliderFloat("", &m_adaptive_quality, 0.0f, 1.f, "%.2f"); ImGui::Separator(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 6b6905e4d2..63cb051aa9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -110,7 +110,7 @@ void GLGizmoRotate::on_update(const UpdateData& data) Vec2d orig_dir = Vec2d::UnitX(); Vec2d new_dir = mouse_pos.normalized(); - double theta = ::acos(clamp(-1.0, 1.0, new_dir.dot(orig_dir))); + double theta = ::acos(std::clamp(new_dir.dot(orig_dir), -1.0, 1.0)); if (cross2(orig_dir, new_dir) < 0.0) theta = 2.0 * (double)PI - theta; From 1b144e80bdb66c96d2d5f6e6fc2ff3b2af5348fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 21 Apr 2021 07:18:02 +0200 Subject: [PATCH 11/26] Added hiding of MMU segmentation gizmo when it is selected printer with only one extruder. --- src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 7 ++++++- src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index e26b0341da..6b23e76237 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -7,6 +7,7 @@ #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/ImGuiWrapper.hpp" #include "slic3r/GUI/Plater.hpp" +#include "libslic3r/PresetBundle.hpp" #include @@ -32,7 +33,11 @@ std::string GLGizmoMmuSegmentation::on_get_name() const return (_L("MMU painting") + " [N]").ToUTF8().data(); } - +bool GLGizmoMmuSegmentation::on_is_selectable() const +{ + return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF + && wxGetApp().get_mode() != comSimple && wxGetApp().extruders_cnt() > 1); +} bool GLGizmoMmuSegmentation::on_init() { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp index 9511b56a4a..2833a35eba 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp @@ -19,6 +19,8 @@ protected: void on_render_input_window(float x, float y, float bottom_limit) override; std::string on_get_name() const override; + bool on_is_selectable() const override; + private: bool on_init() override; From db55bd706ee22cf08e6b6288521595af2e9864e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 27 Apr 2021 00:36:22 +0200 Subject: [PATCH 12/26] Fixed case in MMU segmentation when the infinity-edge in the Voronoi diagram has vertex0(), and vertex1() equals nullptr. --- src/libslic3r/MultiMaterialSegmentation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index 865e0aaa4d..c3f2fed13d 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -795,7 +795,7 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vectorcell()->source_index() > edge_it->twin()->cell()->source_index() || edge_it->color()) continue; - if (edge_it->is_infinite()) { + if (edge_it->is_infinite() && (edge_it->vertex0() != nullptr || edge_it->vertex1() != nullptr)) { // Infinite edge is leading through a point on the counter, but there are no Voronoi vertices. // So we could fix this case by computing the intersection between the contour line and infinity edge. std::vector samples; From 8a19cf9d642e7c4d1eba9f0ef8191a8e1691ae3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 27 Apr 2021 00:37:49 +0200 Subject: [PATCH 13/26] Extended support of MMU segmentation backed for more than three colors. Serialization and deserialization in TriangleSelector were extended to support up to 16 materials (3 unused states left for possible later extension). These changes also affect the encoding of data from custom supports and seams, but it is backward compatible with the previous encoding. And for custom supports and seams, it is produced exactly the same data encoding as before. --- src/libslic3r/MultiMaterialSegmentation.cpp | 31 +++++++------- src/libslic3r/PrintObject.cpp | 3 +- src/libslic3r/TriangleSelector.cpp | 47 ++++++++++++++------- 3 files changed, 50 insertions(+), 31 deletions(-) diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index c3f2fed13d..aa25361083 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1121,13 +1121,13 @@ static void cut_segmented_layers(const ConstLayerPtrsAdaptor layers, std::vector // Returns MMU segmentation of top and bottom layers based on painting in MMU segmentation gizmo static inline std::vector> mmu_segmentation_top_and_bottom_layers(const PrintObject &print_object) { + const size_t num_extruders = print_object.print()->config().nozzle_diameter.size(); const ConstLayerPtrsAdaptor layers = print_object.layers(); - std::vector> triangles_by_color(3); - triangles_by_color.assign(3, std::vector(layers.size())); + std::vector> triangles_by_color(num_extruders); + triangles_by_color.assign(num_extruders, std::vector(layers.size())); for (const ModelVolume *mv : print_object.model_object()->volumes) { - for (const auto ¶ms : {std::make_pair(EnforcerBlockerType::NONE, 0), std::make_pair(EnforcerBlockerType::ENFORCER, 1), - std::make_pair(EnforcerBlockerType::BLOCKER, 2)}) { - const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, params.first); + for (size_t extruder_idx = 0; extruder_idx < num_extruders; ++extruder_idx) { + const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, EnforcerBlockerType(extruder_idx)); if (!mv->is_model_part() || custom_facets.indices.empty()) continue; @@ -1168,7 +1168,7 @@ static inline std::vector> mmu_segmentation_top_and_bott for (auto layer_it = first_layer; (layer_it != (last_layer + 1) && layer_it != layers.end()); ++layer_it) { size_t layer_idx = layer_it - layers.begin(); - triangles_by_color[params.second][layer_idx].emplace_back(triangle); + triangles_by_color[extruder_idx][layer_idx].emplace_back(triangle); } } } @@ -1234,10 +1234,10 @@ static inline std::vector> mmu_segmentation_top_and_bott } }); // end of parallel_for - std::vector> triangles_by_color_bottom(3); - std::vector> triangles_by_color_top(3); - triangles_by_color_bottom.assign(3, std::vector(layers.size())); - triangles_by_color_top.assign(3, std::vector(layers.size())); + std::vector> triangles_by_color_bottom(num_extruders); + std::vector> triangles_by_color_top(num_extruders); + triangles_by_color_bottom.assign(num_extruders, std::vector(layers.size())); + triangles_by_color_top.assign(num_extruders, std::vector(layers.size())); for (size_t layer_idx = 0; layer_idx < print_object.layers().size(); ++layer_idx) { BOOST_LOG_TRIVIAL(debug) << "MMU segmentation of top layer: " << layer_idx; @@ -1306,8 +1306,8 @@ static inline std::vector> mmu_segmentation_top_and_bott } } - std::vector> triangles_by_color_merged(3); - triangles_by_color_merged.assign(3, std::vector(layers.size())); + std::vector> triangles_by_color_merged(num_extruders); + triangles_by_color_merged.assign(num_extruders, std::vector(layers.size())); for (size_t layer_idx = 0; layer_idx < layers.size(); ++layer_idx) { for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) { auto &self = triangles_by_color_merged[color_idx][layer_idx]; @@ -1367,8 +1367,9 @@ std::vector>> multi_material_segmentati } for (const ModelVolume *mv : print_object.model_object()->volumes) { - for (const auto ¶ms : {std::make_pair(EnforcerBlockerType::ENFORCER, 1), std::make_pair(EnforcerBlockerType::BLOCKER, 2)}) { - const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, params.first); + const size_t num_extruders = print_object.print()->config().nozzle_diameter.size(); + for (size_t extruder_idx = 1; extruder_idx < num_extruders; ++extruder_idx) { + const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, EnforcerBlockerType(extruder_idx)); if (!mv->is_model_part() || custom_facets.indices.empty()) continue; @@ -1425,7 +1426,7 @@ std::vector>> multi_material_segmentati visitor.reset(); visitor.line_to_test.a = line_start; visitor.line_to_test.b = line_end; - visitor.color = params.second; + visitor.color = extruder_idx; edge_grids[layer_idx].visit_cells_intersecting_line(line_start, line_end, visitor); append(painted_lines[layer_idx], std::move(painted_line_tmp)); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index a1d8f65bfe..1c5c4f0216 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1918,7 +1918,8 @@ void PrintObject::_slice(const std::vector &layer_height_profile) size_t region_count_before_change = this->num_regions(); std::vector>> segmented_regions = multi_material_segmentation_by_painting(*this); // Skip region with default extruder - for (size_t region_idx = 1; region_idx < 3; ++region_idx) { + const size_t num_extruders = this->print()->config().nozzle_diameter.size(); + for (size_t region_idx = 1; region_idx < num_extruders; ++region_idx) { std::vector c_layers(m_layers.size()); for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++layer_idx) { for(const std::pair &colored_polygon : segmented_regions[layer_idx]) { diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 7570075e56..ad69f6c7f9 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -550,8 +550,9 @@ indexed_triangle_set TriangleSelector::get_facets(EnforcerBlockerType state) con std::map> TriangleSelector::serialize() const { // Each original triangle of the mesh is assigned a number encoding its state - // or how it is split. Each triangle is encoded by 4 bits (xxyy): - // leaf triangle: xx = EnforcerBlockerType, yy = 0 + // or how it is split. Each triangle is encoded by 4 bits (xxyy) or 8 bits (zzzzxxyy): + // leaf triangle: xx = EnforcerBlockerType (Only values 0, 1, and 2. Value 3 is used as an indicator for additional 4 bits.), yy = 0 + // leaf triangle: xx = 0b11, yy = 0b00, zzzz = EnforcerBlockerType (subtracted by 3) // non-leaf: xx = special side, yy = number of split sides // These are bitwise appended and formed into one 64-bit integer. @@ -594,9 +595,17 @@ std::map> TriangleSelector::serialize() const serialize_recursive(tr.children[child_idx]); } else { // In case this is leaf, we better save information about its state. - assert(int(tr.get_state()) <= 3); - data.push_back(int(tr.get_state()) & 0b01); - data.push_back(int(tr.get_state()) & 0b10); + assert(int(tr.get_state()) <= 15); + if (3 <= int(tr.get_state()) && int(tr.get_state()) <= 15) { + data.insert(data.end(), {true, true}); + for (size_t bit_idx = 0; bit_idx < 4; ++bit_idx) { + size_t bit_mask = 0b0001 << bit_idx; + data.push_back(int(tr.get_state()) - 3 & bit_mask); + } + } else { + data.push_back(int(tr.get_state()) & 0b01); + data.push_back(int(tr.get_state()) & 0b10); + } ++stored_triangles; } }; @@ -614,7 +623,7 @@ void TriangleSelector::deserialize(const std::map> data) for (const auto& [triangle_id, code] : data) { assert(triangle_id < int(m_triangles.size())); assert(! code.empty()); - int processed_triangles = 0; + int processed_nibbles = 0; struct ProcessingInfo { int facet_id = 0; int processed_children = 0; @@ -626,18 +635,26 @@ void TriangleSelector::deserialize(const std::map> data) while (true) { // Read next triangle info. - int next_code = 0; - for (int i=3; i>=0; --i) { - next_code = next_code << 1; - next_code |= int(code[4 * processed_triangles + i]); - } - ++processed_triangles; + std::array next_code{}; + for(size_t nibble_idx = 0; nibble_idx < 2; ++nibble_idx) { + assert(nibble_idx < 2); + if(nibble_idx >= 1 && (next_code[0] >> 2) != 0b11) + break; - int num_of_split_sides = (next_code & 0b11); + for (int i = 3; i >= 0; --i) { + next_code[nibble_idx] = next_code[nibble_idx] << 1; + next_code[nibble_idx] |= int(code[4 * processed_nibbles + i]); + } + + ++processed_nibbles; + } + + int num_of_split_sides = (next_code[0] & 0b11); int num_of_children = num_of_split_sides != 0 ? num_of_split_sides + 1 : 0; bool is_split = num_of_children != 0; - EnforcerBlockerType state = EnforcerBlockerType(next_code >> 2); - int special_side = (next_code >> 2); + // Value of the second nibble was subtracted by 3, so it is added back. + EnforcerBlockerType state = EnforcerBlockerType(next_code[0] >> 2 == 0b11 ? next_code[1] + 3 : next_code[0] >> 2); + int special_side = (next_code[0] >> 2); // Take care of the first iteration separately, so handling of the others is simpler. if (parents.empty()) { From be1b4ce18cfe3f07ea1ff773cdc9d999cf1b980c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 27 Apr 2021 00:41:20 +0200 Subject: [PATCH 14/26] Fixed compiler warnings --- src/libslic3r/TriangleSelector.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index ad69f6c7f9..911dede4cb 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -599,8 +599,8 @@ std::map> TriangleSelector::serialize() const if (3 <= int(tr.get_state()) && int(tr.get_state()) <= 15) { data.insert(data.end(), {true, true}); for (size_t bit_idx = 0; bit_idx < 4; ++bit_idx) { - size_t bit_mask = 0b0001 << bit_idx; - data.push_back(int(tr.get_state()) - 3 & bit_mask); + size_t bit_mask = uint64_t(0b0001) << bit_idx; + data.push_back((int(tr.get_state()) - 3) & bit_mask); } } else { data.push_back(int(tr.get_state()) & 0b01); From 576c5b78e992f839d96abdc06863ef0830b1acea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 27 Apr 2021 06:48:09 +0200 Subject: [PATCH 15/26] Added seed fill for MMU segmentation --- src/libslic3r/TriangleSelector.cpp | 68 +++++- src/libslic3r/TriangleSelector.hpp | 32 ++- .../GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 204 ++++++------------ .../GUI/Gizmos/GLGizmoMmuSegmentation.hpp | 3 - src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 82 ++++++- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 4 + src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 6 +- 8 files changed, 228 insertions(+), 174 deletions(-) diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 911dede4cb..f5907511bd 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -36,7 +36,7 @@ void TriangleSelector::Triangle::set_division(int sides_to_split, int special_si void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, const Vec3f& source, float radius, CursorType cursor_type, EnforcerBlockerType new_state, - const Transform3d& trafo) + const Transform3d& trafo, bool triangle_splitting) { assert(facet_start < m_orig_size_indices); @@ -59,7 +59,7 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, while (facet_idx < int(facets_to_check.size())) { int facet = facets_to_check[facet_idx]; if (! visited[facet]) { - if (select_triangle(facet, new_state)) { + if (select_triangle(facet, new_state, false, triangle_splitting)) { // add neighboring facets to list to be proccessed later for (int n=0; n<3; ++n) { int neighbor_idx = m_mesh->stl.neighbors_start[facet].neighbor[n]; @@ -73,13 +73,56 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, } } +void TriangleSelector::seed_fill_select_triangles(const Vec3f& hit, int facet_start, float seed_fill_angle) +{ + this->seed_fill_unselect_all_triangles(); + std::vector visited(m_triangles.size(), false); + std::queue facet_queue; + facet_queue.push(facet_start); + + // Check if neighbour_facet_idx is satisfies angle in seed_fill_angle and append it to facet_queue if it do. + auto check_angle_and_append = [this, &facet_queue](const size_t facet_idx, const size_t neighbour_facet_idx, const float seed_fill_angle) -> void { + double dot_product = m_triangles[neighbour_facet_idx].normal.dot(m_triangles[facet_idx].normal); + dot_product = std::clamp(dot_product, 0., 1.); + double facet_angle_limit = cos(Geometry::deg2rad(seed_fill_angle)); + if ((dot_product + EPSILON) >= facet_angle_limit) + facet_queue.push(neighbour_facet_idx); + }; + + while(!facet_queue.empty()) { + size_t current_facet = facet_queue.front(); + facet_queue.pop(); + + if (!visited[current_facet]) { + if (!m_triangles[current_facet].is_split()) + m_triangles[current_facet].select_by_seed_fill(); + + if (m_triangles[current_facet].is_split()) + for (int split_triangle_idx = 0; split_triangle_idx <= m_triangles[current_facet].number_of_split_sides(); ++split_triangle_idx) { + assert(split_triangle_idx < int(m_triangles[current_facet].children.size())); + assert(m_triangles[current_facet].children[split_triangle_idx] < int(m_triangles.size())); + + if (!visited[m_triangles[current_facet].children[split_triangle_idx]]) + check_angle_and_append(current_facet, m_triangles[current_facet].children[split_triangle_idx], seed_fill_angle); + } + + if (int(current_facet) < m_orig_size_indices) + for (int neighbor_idx : m_mesh->stl.neighbors_start[current_facet].neighbor) { + assert(neighbor_idx >= 0); + if (neighbor_idx >= 0 && !visited[neighbor_idx]) + check_angle_and_append(current_facet, neighbor_idx, seed_fill_angle); + } + } + visited[current_facet] = true; + } +} // Selects either the whole triangle (discarding any children it had), or divides // the triangle recursively, selecting just subtriangles truly inside the circle. // This is done by an actual recursive call. Returns false if the triangle is // outside the cursor. -bool TriangleSelector::select_triangle(int facet_idx, EnforcerBlockerType type, bool recursive_call) +bool TriangleSelector::select_triangle(int facet_idx, EnforcerBlockerType type, bool recursive_call, bool triangle_splitting) { assert(facet_idx < int(m_triangles.size())); @@ -108,7 +151,10 @@ bool TriangleSelector::select_triangle(int facet_idx, EnforcerBlockerType type, return true; } - split_triangle(facet_idx); + if(triangle_splitting) + split_triangle(facet_idx); + else if(!m_triangles[facet_idx].is_split()) + m_triangles[facet_idx].set_state(type); tr = &m_triangles[facet_idx]; // might have been invalidated @@ -118,7 +164,7 @@ bool TriangleSelector::select_triangle(int facet_idx, EnforcerBlockerType type, assert(i < int(tr->children.size())); assert(tr->children[i] < int(m_triangles.size())); - select_triangle(tr->children[i], type, true); + select_triangle(tr->children[i], type, true, triangle_splitting); tr = &m_triangles[facet_idx]; // might have been invalidated } } @@ -710,6 +756,18 @@ void TriangleSelector::deserialize(const std::map> data) } } +void TriangleSelector::seed_fill_unselect_all_triangles() { + for (Triangle &triangle : m_triangles) + if (!triangle.is_split()) + triangle.unselect_by_seed_fill(); +} + +void TriangleSelector::seed_fill_apply_on_triangles(EnforcerBlockerType new_state) +{ + for (Triangle &triangle : m_triangles) + if (!triangle.is_split() && triangle.is_selected_by_seed_fill()) + triangle.set_state(new_state); +} TriangleSelector::Cursor::Cursor( const Vec3f& center_, const Vec3f& source_, float radius_world, diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index 9d15900704..1a420c547b 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -29,13 +29,18 @@ public: explicit TriangleSelector(const TriangleMesh& mesh); // Select all triangles fully inside the circle, subdivide where needed. - void select_patch(const Vec3f& hit, // point where to start - int facet_start, // facet that point belongs to - const Vec3f& source, // camera position (mesh coords) - float radius, // radius of the cursor - CursorType type, // current type of cursor + void select_patch(const Vec3f &hit, // point where to start + int facet_start, // facet that point belongs to + const Vec3f &source, // camera position (mesh coords) + float radius, // radius of the cursor + CursorType type, // current type of cursor EnforcerBlockerType new_state, // enforcer or blocker? - const Transform3d& trafo); // matrix to get from mesh to world + const Transform3d &trafo, // matrix to get from mesh to world + bool triangle_splitting); // If triangles will be split base on the cursor or not + + void seed_fill_select_triangles(const Vec3f &hit, // point where to start + int facet_start, // facet that point belongs to + float seed_fill_angle); // the maximal angle between two facets to be painted by the same color // Get facets currently in the given state. indexed_triangle_set get_facets(EnforcerBlockerType state) const; @@ -56,6 +61,11 @@ public: // Load serialized data. Assumes that correct mesh is loaded. void deserialize(const std::map> data); + // For all triangles, remove the flag indicating that the triangle was selected by seed fill. + void seed_fill_unselect_all_triangles(); + + // For all triangles selected by seed fill, set new EnforcerBlockerType and remove flag indicating that triangle was selected by seed fill. + void seed_fill_apply_on_triangles(EnforcerBlockerType new_state); protected: // Triangle and info about how it's split. @@ -90,6 +100,12 @@ protected: void set_state(EnforcerBlockerType type) { assert(! is_split()); state = type; } EnforcerBlockerType get_state() const { assert(! is_split()); return state; } + // Set if the triangle has been selected or unselected by seed fill. + void select_by_seed_fill() { assert(! is_split()); m_selected_by_seed_fill = true; } + void unselect_by_seed_fill() { assert(! is_split()); m_selected_by_seed_fill = false; } + // Get if the triangle has been selected or not by seed fill. + bool is_selected_by_seed_fill() const { assert(! is_split()); return m_selected_by_seed_fill; } + // Get info on how it's split. bool is_split() const { return number_of_split_sides() != 0; } int number_of_split_sides() const { return number_of_splits; } @@ -101,6 +117,7 @@ protected: int number_of_splits; int special_side_idx; EnforcerBlockerType state; + bool m_selected_by_seed_fill = false; // How many children were spawned during last split? // Is not reset on remerging the triangle. @@ -153,8 +170,7 @@ protected: float m_old_cursor_radius_sqr; // Private functions: - bool select_triangle(int facet_idx, EnforcerBlockerType type, - bool recursive_call = false); + bool select_triangle(int facet_idx, EnforcerBlockerType type, bool recursive_call = false, bool triangle_splitting = true); int vertices_inside(int facet_idx) const; bool faces_camera(int facet) const; void undivide_triangle(int facet_idx); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index 6b23e76237..cc5d672bb6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -13,20 +13,15 @@ #include -namespace Slic3r { - -namespace GUI { - - +namespace Slic3r::GUI { void GLGizmoMmuSegmentation::on_shutdown() { - m_angle_threshold_deg = 0.f; +// m_seed_fill_angle = 0.f; +// m_seed_fill_enabled = false; m_parent.use_slope(false); } - - std::string GLGizmoMmuSegmentation::on_get_name() const { // FIXME Lukas H.: Discuss and change shortcut @@ -44,28 +39,24 @@ bool GLGizmoMmuSegmentation::on_init() // FIXME Lukas H.: Discuss and change shortcut m_shortcut_key = WXK_CONTROL_N; - m_desc["clipping_of_view"] = _L("Clipping of view") + ": "; - m_desc["reset_direction"] = _L("Reset direction"); - m_desc["cursor_size"] = _L("Brush size") + ": "; - m_desc["cursor_type"] = _L("Brush shape") + ": "; - m_desc["enforce_caption"] = _L("Left mouse button") + ": "; - m_desc["enforce"] = _L("Enforce supports"); - m_desc["block_caption"] = _L("Right mouse button") + ": "; - m_desc["block"] = _L("Block supports"); - m_desc["remove_caption"] = _L("Shift + Left mouse button") + ": "; - m_desc["remove"] = _L("Remove selection"); - m_desc["remove_all"] = _L("Remove all selection"); - m_desc["circle"] = _L("Circle"); - m_desc["sphere"] = _L("Sphere"); - m_desc["highlight_by_angle"] = _L("Highlight by angle"); - m_desc["enforce_button"] = _L("Enforce"); - m_desc["cancel"] = _L("Cancel"); + m_desc["reset_direction"] = _L("Reset direction"); + m_desc["clipping_of_view"] = _L("Clipping of view") + ": "; + m_desc["cursor_size"] = _L("Brush size") + ": "; + m_desc["cursor_type"] = _L("Brush shape") + ": "; + m_desc["first_color_caption"] = _L("Left mouse button") + ": "; + m_desc["first_color"] = _L("First color"); + m_desc["second_color_caption"] = _L("Right mouse button") + ": "; + m_desc["second_color"] = _L("Second color"); + m_desc["remove_caption"] = _L("Shift + Left mouse button") + ": "; + m_desc["remove"] = _L("Remove painted color"); + m_desc["remove_all"] = _L("Remove all painted colors"); + m_desc["circle"] = _L("Circle"); + m_desc["sphere"] = _L("Sphere"); + m_desc["seed_fill_angle"] = _L("Seed fill angle"); return true; } - - void GLGizmoMmuSegmentation::render_painter_gizmo() const { const Selection& selection = m_parent.get_selection(); @@ -81,99 +72,81 @@ void GLGizmoMmuSegmentation::render_painter_gizmo() const glsafe(::glDisable(GL_BLEND)); } - - void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bottom_limit) { - if (! m_c->selection_info()->model_object()) + if (!m_c->selection_info()->model_object()) return; - const float approx_height = m_imgui->scaled(17.0f); - y = std::min(y, bottom_limit - approx_height); + const float approx_height = m_imgui->scaled(23.0f); + y = std::min(y, bottom_limit - approx_height); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, - m_imgui->calc_text_size(m_desc.at("reset_direction")).x) - + m_imgui->scaled(1.5f); - const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f); - const float autoset_slider_left = m_imgui->calc_text_size(m_desc.at("highlight_by_angle")).x + m_imgui->scaled(1.f); - const float cursor_type_radio_left = m_imgui->calc_text_size(m_desc.at("cursor_type")).x + m_imgui->scaled(1.f); - const float cursor_type_radio_width1 = m_imgui->calc_text_size(m_desc["circle"]).x - + m_imgui->scaled(2.5f); - const float cursor_type_radio_width2 = m_imgui->calc_text_size(m_desc["sphere"]).x - + m_imgui->scaled(2.5f); - const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f); - const float button_enforce_width = m_imgui->calc_text_size(m_desc.at("enforce_button")).x; - const float button_cancel_width = m_imgui->calc_text_size(m_desc.at("cancel")).x; - const float buttons_width = std::max(button_enforce_width, button_cancel_width) + m_imgui->scaled(0.5f); - const float minimal_slider_width = m_imgui->scaled(4.f); + m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); + const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f); + const float autoset_slider_left = m_imgui->calc_text_size(m_desc.at("seed_fill_angle")).x + m_imgui->scaled(1.f); + const float cursor_type_radio_left = m_imgui->calc_text_size(m_desc.at("cursor_type")).x + m_imgui->scaled(1.f); + const float cursor_type_radio_width1 = m_imgui->calc_text_size(m_desc["circle"]).x + m_imgui->scaled(2.5f); + const float cursor_type_radio_width2 = m_imgui->calc_text_size(m_desc["sphere"]).x + m_imgui->scaled(2.5f); + const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f); + const float buttons_width = m_imgui->scaled(0.5f); + const float minimal_slider_width = m_imgui->scaled(4.f); - float caption_max = 0.f; + float caption_max = 0.f; float total_text_max = 0.; - for (const std::string& t : {"enforce", "block", "remove"}) { - caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t+"_caption")).x); + for (const std::string &t : {"first_color", "second_color", "remove"}) { + caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t + "_caption")).x); total_text_max = std::max(total_text_max, caption_max + m_imgui->calc_text_size(m_desc.at(t)).x); } caption_max += m_imgui->scaled(1.f); total_text_max += m_imgui->scaled(1.f); float window_width = minimal_slider_width + std::max(autoset_slider_left, std::max(cursor_slider_left, clipping_slider_left)); - window_width = std::max(window_width, total_text_max); - window_width = std::max(window_width, button_width); - window_width = std::max(window_width, cursor_type_radio_left + cursor_type_radio_width1 + cursor_type_radio_width2); - window_width = std::max(window_width, 2.f * buttons_width + m_imgui->scaled(1.f)); + window_width = std::max(window_width, total_text_max); + window_width = std::max(window_width, button_width); + window_width = std::max(window_width, cursor_type_radio_left + cursor_type_radio_width1 + cursor_type_radio_width2); + window_width = std::max(window_width, 2.f * buttons_width + m_imgui->scaled(1.f)); - auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) { + auto draw_text_with_caption = [this, &caption_max](const wxString &caption, const wxString &text) { m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, caption); ImGui::SameLine(caption_max); m_imgui->text(text); }; - for (const std::string& t : {"enforce", "block", "remove"}) + for (const std::string &t : {"first_color", "second_color", "remove"}) draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t)); m_imgui->text(""); ImGui::Separator(); - m_imgui->text(m_desc["highlight_by_angle"] + ":"); + if (m_imgui->checkbox(_L("Seed fill"), m_seed_fill_enabled)) + if (!m_seed_fill_enabled) + for (auto &triangle_selector : m_triangle_selectors) + triangle_selector->seed_fill_unselect_all_triangles(); + + m_imgui->text(m_desc["seed_fill_angle"] + ":"); ImGui::AlignTextToFramePadding(); - std::string format_str = std::string("%.f") + I18N::translate_utf8("°", - "Degree sign to use in the respective slider in FDM supports gizmo," - "placed after the number with no whitespace in between."); + std::string format_str = std::string("%.f") + I18N::translate_utf8("°", "Degree sign to use in the respective slider in FDM supports gizmo," + "placed after the number with no whitespace in between."); ImGui::SameLine(autoset_slider_left); ImGui::PushItemWidth(window_width - autoset_slider_left); - if (m_imgui->slider_float("", &m_angle_threshold_deg, 0.f, 90.f, format_str.data())) { - m_parent.set_slope_normal_angle(90.f - m_angle_threshold_deg); - if (! m_parent.is_using_slope()) { - m_parent.use_slope(true); - m_parent.set_as_dirty(); - } - } - - m_imgui->disabled_begin(m_angle_threshold_deg == 0.f); - ImGui::NewLine(); - ImGui::SameLine(window_width - 2.f*buttons_width - m_imgui->scaled(0.5f)); - if (m_imgui->button(m_desc["enforce_button"], buttons_width, 0.f)) { - select_facets_by_angle(m_angle_threshold_deg, false); - m_angle_threshold_deg = 0.f; - } - ImGui::SameLine(window_width - buttons_width); - if (m_imgui->button(m_desc["cancel"], buttons_width, 0.f)) { - m_angle_threshold_deg = 0.f; - m_parent.use_slope(false); - } + m_imgui->disabled_begin(!m_seed_fill_enabled); + m_imgui->slider_float("", &m_seed_fill_angle, 0.f, 90.f, format_str.data()); m_imgui->disabled_end(); + ImGui::NewLine(); + ImGui::SameLine(window_width - 2.f * buttons_width - m_imgui->scaled(0.5f)); + ImGui::Separator(); if (m_imgui->button(m_desc.at("remove_all"))) { Plater::TakeSnapshot(wxGetApp().plater(), wxString(_L("Reset selection"))); - ModelObject* mo = m_c->selection_info()->model_object(); - int idx = -1; - for (ModelVolume* mv : mo->volumes) { + ModelObject *mo = m_c->selection_info()->model_object(); + int idx = -1; + for (ModelVolume *mv : mo->volumes) { if (mv->is_model_part()) { ++idx; m_triangle_selectors[idx]->reset(); @@ -184,7 +157,6 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott m_parent.set_as_dirty(); } - const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; ImGui::AlignTextToFramePadding(); @@ -200,7 +172,6 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott ImGui::EndTooltip(); } - ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("cursor_type")); ImGui::SameLine(cursor_type_radio_left + m_imgui->scaled(0.f)); @@ -221,7 +192,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott ImGui::SameLine(cursor_type_radio_left + cursor_type_radio_width2 + m_imgui->scaled(0.f)); ImGui::PushItemWidth(cursor_type_radio_width2); - if (m_imgui->radio_button(m_desc["circle"], ! sphere_sel)) + if (m_imgui->radio_button(m_desc["circle"], !sphere_sel)) sphere_sel = false; if (ImGui::IsItemHovered()) { @@ -232,23 +203,17 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott ImGui::EndTooltip(); } - m_cursor_type = sphere_sel - ? TriangleSelector::CursorType::SPHERE - : TriangleSelector::CursorType::CIRCLE; - - + m_cursor_type = sphere_sel ? TriangleSelector::CursorType::SPHERE : TriangleSelector::CursorType::CIRCLE; + m_imgui->checkbox(_L("Split triangles"), m_triangle_splitting_enabled); ImGui::Separator(); if (m_c->object_clipper()->get_position() == 0.f) { ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("clipping_of_view")); - } - else { + } else { if (m_imgui->button(m_desc.at("reset_direction"))) { - wxGetApp().CallAfter([this](){ - m_c->object_clipper()->set_position(-1., false); - }); + wxGetApp().CallAfter([this]() { m_c->object_clipper()->set_position(-1., false); }); } } @@ -256,7 +221,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott ImGui::PushItemWidth(window_width - clipping_slider_left); float clp_dist = m_c->object_clipper()->get_position(); if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f")) - m_c->object_clipper()->set_position(clp_dist, true); + m_c->object_clipper()->set_position(clp_dist, true); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::PushTextWrapPos(max_tooltip_width); @@ -267,50 +232,6 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott m_imgui->end(); } - - -void GLGizmoMmuSegmentation::select_facets_by_angle(float threshold_deg, bool block) -{ - float threshold = (M_PI/180.)*threshold_deg; - const Selection& selection = m_parent.get_selection(); - const ModelObject* mo = m_c->selection_info()->model_object(); - const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; - - int mesh_id = -1; - for (const ModelVolume* mv : mo->volumes) { - if (! mv->is_model_part()) - continue; - - ++mesh_id; - - const Transform3d trafo_matrix = mi->get_matrix(true) * mv->get_matrix(true); - Vec3f down = (trafo_matrix.inverse() * (-Vec3d::UnitZ())).cast().normalized(); - Vec3f limit = (trafo_matrix.inverse() * Vec3d(std::sin(threshold), 0, -std::cos(threshold))).cast().normalized(); - - float dot_limit = limit.dot(down); - - // Now calculate dot product of vert_direction and facets' normals. - int idx = -1; - for (const stl_facet& facet : mv->mesh().stl.facet_start) { - ++idx; - if (facet.normal.dot(down) > dot_limit) - m_triangle_selectors[mesh_id]->set_facet(idx, - block - ? EnforcerBlockerType::BLOCKER - : EnforcerBlockerType::ENFORCER); - } - } - - activate_internal_undo_redo_stack(true); - - Plater::TakeSnapshot(wxGetApp().plater(), block ? _L("Block supports by angle") - : _L("Add supports by angle")); - update_model_object(); - m_parent.set_as_dirty(); -} - - - void GLGizmoMmuSegmentation::update_model_object() const { bool updated = false; @@ -327,8 +248,6 @@ void GLGizmoMmuSegmentation::update_model_object() const m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } - - void GLGizmoMmuSegmentation::update_from_model_object() { wxBusyCursor wait; @@ -351,13 +270,10 @@ void GLGizmoMmuSegmentation::update_from_model_object() } } - - PainterGizmoType GLGizmoMmuSegmentation::get_painter_type() const { return PainterGizmoType::MMU_SEGMENTATION; } -} // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp index 2833a35eba..b0bfdf65a2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp @@ -31,9 +31,6 @@ private: void on_shutdown() override; PainterGizmoType get_painter_type() const override; - void select_facets_by_angle(float threshold, bool block); - float m_angle_threshold_deg = 0.f; - // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. std::map m_desc; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index c6b5bbc8e6..56f33afdd6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -143,11 +143,12 @@ void GLGizmoPainterBase::render_cursor() const if (m_rr.mesh_id == -1) return; - - if (m_cursor_type == TriangleSelector::SPHERE) - render_cursor_sphere(trafo_matrices[m_rr.mesh_id]); - else - render_cursor_circle(); + if (!m_seed_fill_enabled) { + if (m_cursor_type == TriangleSelector::SPHERE) + render_cursor_sphere(trafo_matrices[m_rr.mesh_id]); + else + render_cursor_circle(); + } } @@ -351,14 +352,50 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast(); assert(m_rr.mesh_id < int(m_triangle_selectors.size())); - m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, m_rr.facet, camera_pos, - m_cursor_radius, m_cursor_type, new_state, trafo_matrix); + if (m_seed_fill_enabled) + m_triangle_selectors[m_rr.mesh_id]->seed_fill_apply_on_triangles(new_state); + else + m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, m_rr.facet, camera_pos, m_cursor_radius, m_cursor_type, + new_state, trafo_matrix, m_triangle_splitting_enabled); m_last_mouse_click = mouse_position; } return true; } + if (action == SLAGizmoEventType::Moving && m_seed_fill_enabled) { + if (m_triangle_selectors.empty()) + return false; + + const Camera & camera = wxGetApp().plater()->get_camera(); + const Selection & selection = m_parent.get_selection(); + const ModelObject * mo = m_c->selection_info()->model_object(); + const ModelInstance *mi = mo->instances[selection.get_instance_idx()]; + const Transform3d & instance_trafo = mi->get_transformation().get_matrix(); + + // Precalculate transformations of individual meshes. + std::vector trafo_matrices; + for (const ModelVolume *mv : mo->volumes) + if (mv->is_model_part()) + trafo_matrices.emplace_back(instance_trafo * mv->get_matrix()); + + // Now "click" into all the prepared points and spill paint around them. + update_raycast_cache(mouse_position, camera, trafo_matrices); + + if (m_rr.mesh_id == -1) { + // Clean selected by seed fill for all triangles + for (auto &triangle_selector : m_triangle_selectors) + triangle_selector->seed_fill_unselect_all_triangles(); + + // In case we have no valid hit, we can return. + return false; + } + + assert(m_rr.mesh_id < int(m_triangle_selectors.size())); + m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, m_rr.facet, m_seed_fill_angle); + return true; + } + if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::RightUp) && m_button_down != Button::None) { // Take snapshot and update ModelVolume data. @@ -521,12 +558,14 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui) { int enf_cnt = 0; int blc_cnt = 0; + int seed_fill_cnt = 0; m_iva_enforcers.release_geometry(); m_iva_blockers.release_geometry(); + m_iva_seed_fill.release_geometry(); for (const Triangle& tr : m_triangles) { - if (! tr.valid || tr.is_split() || tr.get_state() == EnforcerBlockerType::NONE) + if (!tr.valid || tr.is_split() || tr.get_state() == EnforcerBlockerType::NONE || tr.is_selected_by_seed_fill()) continue; GLIndexedVertexArray& va = tr.get_state() == EnforcerBlockerType::ENFORCER @@ -543,17 +582,32 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui) double(tr.normal[0]), double(tr.normal[1]), double(tr.normal[2])); - va.push_triangle(cnt, - cnt+1, - cnt+2); + va.push_triangle(cnt, cnt + 1, cnt + 2); cnt += 3; } + for (const Triangle &tr : m_triangles) { + if (!tr.valid || tr.is_split() || !tr.is_selected_by_seed_fill()) + continue; + + for (int i = 0; i < 3; ++i) + m_iva_seed_fill.push_geometry(double(m_vertices[tr.verts_idxs[i]].v[0]), + double(m_vertices[tr.verts_idxs[i]].v[1]), + double(m_vertices[tr.verts_idxs[i]].v[2]), + double(tr.normal[0]), + double(tr.normal[1]), + double(tr.normal[2])); + m_iva_seed_fill.push_triangle(seed_fill_cnt, seed_fill_cnt + 1, seed_fill_cnt + 2); + seed_fill_cnt += 3; + } + m_iva_enforcers.finalize_geometry(true); m_iva_blockers.finalize_geometry(true); + m_iva_seed_fill.finalize_geometry(true); bool render_enf = m_iva_enforcers.has_VBOs(); bool render_blc = m_iva_blockers.has_VBOs(); + bool render_seed_fill = m_iva_seed_fill.has_VBOs(); auto* shader = wxGetApp().get_shader("gouraud"); if (! shader) @@ -575,6 +629,12 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui) m_iva_blockers.render(); } + if (render_seed_fill) { + std::array color = { 0.f, 1.00f, 0.44f, 1.f }; + shader->set_uniform("uniform_color", color); + m_iva_seed_fill.render(); + } + #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG if (imgui) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index 400934ca34..214ebed592 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -48,6 +48,7 @@ public: private: GLIndexedVertexArray m_iva_enforcers; GLIndexedVertexArray m_iva_blockers; + GLIndexedVertexArray m_iva_seed_fill; std::array m_varrays; }; @@ -95,6 +96,9 @@ protected: TriangleSelector::CursorType m_cursor_type = TriangleSelector::SPHERE; + bool m_triangle_splitting_enabled = true; + bool m_seed_fill_enabled = false; + float m_seed_fill_angle = 0.f; private: bool is_mesh_point_clipped(const Vec3d& point, const Transform3d& trafo) const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 28be1b97f2..6f59dc95ed 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -32,7 +32,8 @@ enum class SLAGizmoEventType : unsigned char { ManualEditing, MouseWheelUp, MouseWheelDown, - ResetClippingPlane + ResetClippingPlane, + Moving }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index b1f38d309b..0bcc1b26be 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -522,9 +522,11 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) bool control_down = evt.CmdDown(); // mouse anywhere - if (evt.Moving()) + if (evt.Moving()) { m_tooltip = update_hover_state(mouse_pos); - else if (evt.LeftUp()) { + if (m_current == MmuSegmentation) + gizmo_event(SLAGizmoEventType::Moving, mouse_pos, evt.ShiftDown(), evt.AltDown()); + } else if (evt.LeftUp()) { if (m_mouse_capture.left) { processed = true; m_mouse_capture.left = false; From d11d15aa1e5b152862ffdc3ae6ca93b5ef8f8364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 30 Apr 2021 15:58:25 +0200 Subject: [PATCH 16/26] Rework of MMU segmentation gizmo to support more than three colors. --- .../GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 381 +++++++++++++++++- .../GUI/Gizmos/GLGizmoMmuSegmentation.hpp | 28 +- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 11 +- 3 files changed, 405 insertions(+), 15 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index cc5d672bb6..f069cd2f72 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -6,13 +6,14 @@ #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/ImGuiWrapper.hpp" +#include "slic3r/GUI/Camera.hpp" #include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/BitmapCache.hpp" #include "libslic3r/PresetBundle.hpp" #include - namespace Slic3r::GUI { void GLGizmoMmuSegmentation::on_shutdown() @@ -34,6 +35,31 @@ bool GLGizmoMmuSegmentation::on_is_selectable() const && wxGetApp().get_mode() != comSimple && wxGetApp().extruders_cnt() > 1); } +static std::vector> get_extruders_colors() +{ + unsigned char rgb_color[3] = {}; + std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); + std::vector> colors_out(colors.size()); + for (const std::string &color : colors) { + Slic3r::GUI::BitmapCache::parse_color(color, rgb_color); + size_t color_idx = &color - &colors.front(); + colors_out[color_idx] = {rgb_color[0], rgb_color[1], rgb_color[2]}; + } + + return colors_out; +} + +static std::vector get_extruders_names() +{ + size_t extruders_count = wxGetApp().extruders_cnt(); + std::vector extruders_out; + extruders_out.reserve(extruders_count); + for (size_t extruder_idx = 1; extruder_idx <= extruders_count; ++extruder_idx) + extruders_out.emplace_back("Extruder " + std::to_string(extruder_idx)); + + return extruders_out; +} + bool GLGizmoMmuSegmentation::on_init() { // FIXME Lukas H.: Discuss and change shortcut @@ -54,6 +80,9 @@ bool GLGizmoMmuSegmentation::on_init() m_desc["sphere"] = _L("Sphere"); m_desc["seed_fill_angle"] = _L("Seed fill angle"); + m_extruders_names = get_extruders_names(); + m_extruders_colors = get_extruders_colors(); + return true; } @@ -72,6 +101,249 @@ void GLGizmoMmuSegmentation::render_painter_gizmo() const glsafe(::glDisable(GL_BLEND)); } +bool GLGizmoMmuSegmentation::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) +{ + if (action == SLAGizmoEventType::MouseWheelUp + || action == SLAGizmoEventType::MouseWheelDown) { + if (control_down) { + double pos = m_c->object_clipper()->get_position(); + pos = action == SLAGizmoEventType::MouseWheelDown + ? std::max(0., pos - 0.01) + : std::min(1., pos + 0.01); + m_c->object_clipper()->set_position(pos, true); + return true; + } + else if (alt_down) { + m_cursor_radius = action == SLAGizmoEventType::MouseWheelDown + ? std::max(m_cursor_radius - CursorRadiusStep, CursorRadiusMin) + : std::min(m_cursor_radius + CursorRadiusStep, CursorRadiusMax); + m_parent.set_as_dirty(); + return true; + } + } + + if (action == SLAGizmoEventType::ResetClippingPlane) { + m_c->object_clipper()->set_position(-1., false); + return true; + } + + if (action == SLAGizmoEventType::LeftDown + || action == SLAGizmoEventType::RightDown + || (action == SLAGizmoEventType::Dragging && m_button_down != Button::None)) { + + if (m_triangle_selectors.empty()) + return false; + + EnforcerBlockerType new_state = EnforcerBlockerType::NONE; + if (! shift_down) { + if (action == SLAGizmoEventType::Dragging) + new_state = m_button_down == Button::Left + ? EnforcerBlockerType(m_first_selected_extruder_idx) + : EnforcerBlockerType(m_second_selected_extruder_idx); + else + new_state = action == SLAGizmoEventType::LeftDown + ? EnforcerBlockerType(m_first_selected_extruder_idx) + : EnforcerBlockerType(m_second_selected_extruder_idx); + } + + const Camera& camera = wxGetApp().plater()->get_camera(); + const Selection& selection = m_parent.get_selection(); + const ModelObject* mo = m_c->selection_info()->model_object(); + const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; + const Transform3d& instance_trafo = mi->get_transformation().get_matrix(); + + // List of mouse positions that will be used as seeds for painting. + std::vector mouse_positions{mouse_position}; + + // In case current mouse position is far from the last one, + // add several positions from between into the list, so there + // are no gaps in the painted region. + { + if (m_last_mouse_click == Vec2d::Zero()) + m_last_mouse_click = mouse_position; + // resolution describes minimal distance limit using circle radius + // as a unit (e.g., 2 would mean the patches will be touching). + double resolution = 0.7; + double diameter_px = resolution * m_cursor_radius * camera.get_zoom(); + int patches_in_between = int(((mouse_position - m_last_mouse_click).norm() - diameter_px) / diameter_px); + if (patches_in_between > 0) { + Vec2d diff = (mouse_position - m_last_mouse_click)/(patches_in_between+1); + for (int i=1; i<=patches_in_between; ++i) + mouse_positions.emplace_back(m_last_mouse_click + i*diff); + } + } + m_last_mouse_click = Vec2d::Zero(); // only actual hits should be saved + + // Precalculate transformations of individual meshes. + std::vector trafo_matrices; + for (const ModelVolume* mv : mo->volumes) { + if (mv->is_model_part()) + trafo_matrices.emplace_back(instance_trafo * mv->get_matrix()); + } + + // Now "click" into all the prepared points and spill paint around them. + for (const Vec2d& mp : mouse_positions) { + update_raycast_cache(mp, camera, trafo_matrices); + + bool dragging_while_painting = (action == SLAGizmoEventType::Dragging && m_button_down != Button::None); + + // The mouse button click detection is enabled when there is a valid hit. + // Missing the object entirely + // shall not capture the mouse. + if (m_rr.mesh_id != -1) { + if (m_button_down == Button::None) + m_button_down = ((action == SLAGizmoEventType::LeftDown) ? Button::Left : Button::Right); + } + + if (m_rr.mesh_id == -1) { + // In case we have no valid hit, we can return. The event will be stopped when + // dragging while painting (to prevent scene rotations and moving the object) + return dragging_while_painting; + } + + const Transform3d& trafo_matrix = trafo_matrices[m_rr.mesh_id]; + + // Calculate direction from camera to the hit (in mesh coords): + Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast(); + + assert(m_rr.mesh_id < int(m_triangle_selectors.size())); + if (m_seed_fill_enabled) + m_triangle_selectors[m_rr.mesh_id]->seed_fill_apply_on_triangles(new_state); + else + m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, m_rr.facet, camera_pos, m_cursor_radius, m_cursor_type, + new_state, trafo_matrix, m_triangle_splitting_enabled); + m_last_mouse_click = mouse_position; + } + + return true; + } + + if (action == SLAGizmoEventType::Moving && m_seed_fill_enabled) { + if (m_triangle_selectors.empty()) + return false; + + const Camera & camera = wxGetApp().plater()->get_camera(); + const Selection & selection = m_parent.get_selection(); + const ModelObject * mo = m_c->selection_info()->model_object(); + const ModelInstance *mi = mo->instances[selection.get_instance_idx()]; + const Transform3d & instance_trafo = mi->get_transformation().get_matrix(); + + // Precalculate transformations of individual meshes. + std::vector trafo_matrices; + for (const ModelVolume *mv : mo->volumes) + if (mv->is_model_part()) + trafo_matrices.emplace_back(instance_trafo * mv->get_matrix()); + + // Now "click" into all the prepared points and spill paint around them. + update_raycast_cache(mouse_position, camera, trafo_matrices); + + if (m_rr.mesh_id == -1) { + // Clean selected by seed fill for all triangles + for (auto &triangle_selector : m_triangle_selectors) + triangle_selector->seed_fill_unselect_all_triangles(); + + // In case we have no valid hit, we can return. + return false; + } + + assert(m_rr.mesh_id < int(m_triangle_selectors.size())); + m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, m_rr.facet, m_seed_fill_angle); + return true; + } + + if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::RightUp) + && m_button_down != Button::None) { + // Take snapshot and update ModelVolume data. + wxString action_name; + if (get_painter_type() == PainterGizmoType::FDM_SUPPORTS) { + if (shift_down) + action_name = _L("Remove selection"); + else { + if (m_button_down == Button::Left) + action_name = _L("Add supports"); + else + action_name = _L("Block supports"); + } + } + if (get_painter_type() == PainterGizmoType::SEAM) { + if (shift_down) + action_name = _L("Remove selection"); + else { + if (m_button_down == Button::Left) + action_name = _L("Enforce seam"); + else + action_name = _L("Block seam"); + } + } + + activate_internal_undo_redo_stack(true); + Plater::TakeSnapshot(wxGetApp().plater(), action_name); + update_model_object(); + + m_button_down = Button::None; + m_last_mouse_click = Vec2d::Zero(); + return true; + } + + return false; +} + +static void render_extruders_combo(const std::string &label, + const std::vector &extruders, + const std::vector> &extruders_colors, + size_t &selection_idx) +{ + assert(!extruders_colors.empty()); + assert(extruders_colors.size() == extruders_colors.size()); + + size_t selection_out = selection_idx; + + // It is necessary to use BeginGroup(). Otherwise, when using SameLine() is called, then other items will be drawn inside the combobox. + ImGui::BeginGroup(); + ImVec2 combo_pos = ImGui::GetCursorScreenPos(); + if (ImGui::BeginCombo(label.c_str(), "")) { + for (size_t extruder_idx = 0; extruder_idx < extruders.size(); ++extruder_idx) { + ImGui::PushID(extruder_idx); + ImVec2 start_position = ImGui::GetCursorScreenPos(); + + if (ImGui::Selectable("", extruder_idx == selection_idx)) + selection_out = extruder_idx; + + ImGui::SameLine(); + ImGuiStyle &style = ImGui::GetStyle(); + float height = ImGui::GetTextLineHeight(); + ImGui::GetWindowDrawList()->AddRectFilled(start_position, ImVec2(start_position.x + height + height / 2, start_position.y + height), + IM_COL32(extruders_colors[extruder_idx][0], extruders_colors[extruder_idx][1], extruders_colors[extruder_idx][2], 255)); + ImGui::GetWindowDrawList()->AddRect(start_position, ImVec2(start_position.x + height + height / 2, start_position.y + height), IM_COL32_BLACK); + + ImGui::SetCursorScreenPos(ImVec2(start_position.x + height + height / 2 + style.FramePadding.x, start_position.y)); + ImGui::Text("%s", extruders[extruder_idx].c_str()); + ImGui::PopID(); + } + + ImGui::EndCombo(); + } + + ImVec2 backup_pos = ImGui::GetCursorScreenPos(); + ImGuiStyle &style = ImGui::GetStyle(); + + ImGui::SetCursorScreenPos(ImVec2(combo_pos.x + style.FramePadding.x, combo_pos.y + style.FramePadding.y)); + ImVec2 p = ImGui::GetCursorScreenPos(); + float height = ImGui::GetTextLineHeight(); + + ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + height + height / 2, p.y + height), + IM_COL32(extruders_colors[selection_idx][0], extruders_colors[selection_idx][1], + extruders_colors[selection_idx][2], 255)); + ImGui::GetWindowDrawList()->AddRect(p, ImVec2(p.x + height + height / 2, p.y + height), IM_COL32_BLACK); + + ImGui::SetCursorScreenPos(ImVec2(p.x + height + height / 2 + style.FramePadding.x, p.y)); + ImGui::Text("%s", extruders[selection_out].c_str()); + ImGui::SetCursorScreenPos(backup_pos); + ImGui::EndGroup(); + + selection_idx = selection_out; +} + void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bottom_limit) { if (!m_c->selection_info()->model_object()) @@ -94,6 +366,9 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f); const float buttons_width = m_imgui->scaled(0.5f); const float minimal_slider_width = m_imgui->scaled(4.f); + const float color_button_width = m_imgui->calc_text_size("").x + m_imgui->scaled(1.75f); + const float combo_label_width = std::max(m_imgui->calc_text_size(m_desc.at("first_color")).x, + m_imgui->calc_text_size(m_desc.at("second_color")).x) + m_imgui->scaled(1.f); float caption_max = 0.f; float total_text_max = 0.; @@ -122,6 +397,30 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott m_imgui->text(""); ImGui::Separator(); + const std::array &select_first_color = m_extruders_colors[m_first_selected_extruder_idx]; + const std::array &select_second_color = m_extruders_colors[m_second_selected_extruder_idx]; + + m_imgui->text(m_desc.at("first_color")); + ImGui::SameLine(combo_label_width); + ImGui::PushItemWidth(window_width - combo_label_width - color_button_width); + render_extruders_combo("##first_color_combo", m_extruders_names, get_extruders_colors(), m_first_selected_extruder_idx); + ImGui::SameLine(); + + ImVec4 first_color = ImVec4(float(select_first_color[0]) / 255.0f, float(select_first_color[1]) / 255.0f, float(select_first_color[2]) / 255.0f, 1.0f); + ImVec4 second_color = ImVec4(float(select_second_color[0]) / 255.0f, float(select_second_color[1]) / 255.0f, float(select_second_color[2]) / 255.0f, 1.0f); + if(ImGui::ColorEdit4("First color##color_picker", (float*)&first_color, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel)) + m_extruders_colors[m_first_selected_extruder_idx] = {uint8_t(first_color.x * 255.0f), uint8_t(first_color.y * 255.0f), uint8_t(first_color.z * 255.0f)}; + + m_imgui->text(m_desc.at("second_color")); + ImGui::SameLine(combo_label_width); + ImGui::PushItemWidth(window_width - combo_label_width - color_button_width); + render_extruders_combo("##second_color_combo", m_extruders_names, get_extruders_colors(), m_second_selected_extruder_idx); + ImGui::SameLine(); + if(ImGui::ColorEdit4("Second color##color_picker", (float*)&second_color, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel)) + m_extruders_colors[m_second_selected_extruder_idx] = {uint8_t(second_color.x * 255.0f), uint8_t(second_color.y * 255.0f), uint8_t(second_color.z * 255.0f)}; + + ImGui::Separator(); + if (m_imgui->checkbox(_L("Seed fill"), m_seed_fill_enabled)) if (!m_seed_fill_enabled) for (auto &triangle_selector : m_triangle_selectors) @@ -134,12 +433,9 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott ImGui::SameLine(autoset_slider_left); ImGui::PushItemWidth(window_width - autoset_slider_left); m_imgui->disabled_begin(!m_seed_fill_enabled); - m_imgui->slider_float("", &m_seed_fill_angle, 0.f, 90.f, format_str.data()); + m_imgui->slider_float("##seed_fill_angle", &m_seed_fill_angle, 0.f, 90.f, format_str.data()); m_imgui->disabled_end(); - ImGui::NewLine(); - ImGui::SameLine(window_width - 2.f * buttons_width - m_imgui->scaled(0.5f)); - ImGui::Separator(); if (m_imgui->button(m_desc.at("remove_all"))) { @@ -265,7 +561,7 @@ void GLGizmoMmuSegmentation::update_from_model_object() // This mesh does not account for the possible Z up SLA offset. const TriangleMesh* mesh = &mv->mesh(); - m_triangle_selectors.emplace_back(std::make_unique(*mesh)); + m_triangle_selectors.emplace_back(std::make_unique(*mesh, wxGetApp().extruders_cnt(), m_extruders_colors)); m_triangle_selectors.back()->deserialize(mv->mmu_segmentation_facets.get_data()); } } @@ -275,5 +571,78 @@ PainterGizmoType GLGizmoMmuSegmentation::get_painter_type() const return PainterGizmoType::MMU_SEGMENTATION; } +void TriangleSelectorMmuGui::render(ImGuiWrapper *imgui) +{ + std::vector color_cnt(m_iva_colors.size()); + int seed_fill_cnt = 0; + for (auto &iva_color : m_iva_colors) + iva_color.release_geometry(); + m_iva_seed_fill.release_geometry(); + + for (size_t color_idx = 0; color_idx < m_iva_colors.size(); ++color_idx) { + for (const Triangle &tr : m_triangles) { + if (!tr.valid || tr.is_split() || /*tr.get_state() == EnforcerBlockerType::NONE ||*/ tr.is_selected_by_seed_fill() || + tr.get_state() != EnforcerBlockerType(color_idx)) + continue; + + for (int i = 0; i < 3; ++i) + m_iva_colors[color_idx].push_geometry(double(m_vertices[tr.verts_idxs[i]].v[0]), + double(m_vertices[tr.verts_idxs[i]].v[1]), + double(m_vertices[tr.verts_idxs[i]].v[2]), + double(tr.normal[0]), + double(tr.normal[1]), + double(tr.normal[2])); + m_iva_colors[color_idx].push_triangle(color_cnt[color_idx], color_cnt[color_idx] + 1, color_cnt[color_idx] + 2); + color_cnt[color_idx] += 3; + } + } + + for (const Triangle &tr : m_triangles) { + if (!tr.valid || tr.is_split() || !tr.is_selected_by_seed_fill()) continue; + + for (int i = 0; i < 3; ++i) + m_iva_seed_fill.push_geometry(double(m_vertices[tr.verts_idxs[i]].v[0]), + double(m_vertices[tr.verts_idxs[i]].v[1]), + double(m_vertices[tr.verts_idxs[i]].v[2]), + double(tr.normal[0]), + double(tr.normal[1]), + double(tr.normal[2])); + m_iva_seed_fill.push_triangle(seed_fill_cnt, seed_fill_cnt + 1, seed_fill_cnt + 2); + seed_fill_cnt += 3; + } + + for (auto &iva_color : m_iva_colors) + iva_color.finalize_geometry(true); + m_iva_seed_fill.finalize_geometry(true); + + std::vector render_colors(m_iva_colors.size()); + for (size_t color_idx = 0; color_idx < m_iva_colors.size(); ++color_idx) + render_colors[color_idx] = m_iva_colors[color_idx].has_VBOs(); + bool render_seed_fill = m_iva_seed_fill.has_VBOs(); + + auto *shader = wxGetApp().get_shader("gouraud"); + if (!shader) return; + + shader->start_using(); + ScopeGuard guard([shader]() { + if (shader) + shader->stop_using(); + }); + shader->set_uniform("slope.actived", false); + + for (size_t color_idx = 0; color_idx < m_iva_colors.size(); ++color_idx) { + if (render_colors[color_idx]) { + std::array color = {m_colors[color_idx][0] / 255.0f, m_colors[color_idx][1] / 255.0f, m_colors[color_idx][2] / 255.0f, 1.f}; + shader->set_uniform("uniform_color", color); + m_iva_colors[color_idx].render(); + } + } + + if (render_seed_fill) { + std::array color = {0.f, 1.00f, 0.44f, 1.f}; + shader->set_uniform("uniform_color", color); + m_iva_seed_fill.render(); + } +} } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp index b0bfdf65a2..0653fd007b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp @@ -3,9 +3,23 @@ #include "GLGizmoPainterBase.hpp" -namespace Slic3r { +namespace Slic3r::GUI { -namespace GUI { +class TriangleSelectorMmuGui : public TriangleSelectorGUI { +public: + explicit TriangleSelectorMmuGui(const TriangleMesh& mesh, size_t extruder_count, const std::vector> &colors) + : TriangleSelectorGUI(mesh), m_colors(colors) { + m_iva_colors = std::vector(extruder_count); + } + + // Render current selection. Transformation matrices are supposed + // to be already set. + virtual void render(ImGuiWrapper* imgui = nullptr); + +private: + const std::vector> &m_colors; + std::vector m_iva_colors; +}; class GLGizmoMmuSegmentation : public GLGizmoPainterBase { @@ -15,12 +29,19 @@ public: void render_painter_gizmo() const override; + virtual bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) override; + protected: void on_render_input_window(float x, float y, float bottom_limit) override; std::string on_get_name() const override; bool on_is_selectable() const override; + size_t m_first_selected_extruder_idx = 0; + size_t m_second_selected_extruder_idx = 1; + std::vector m_extruders_names; + std::vector> m_extruders_colors; + private: bool on_init() override; @@ -36,9 +57,6 @@ private: std::map m_desc; }; - - -} // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index 214ebed592..a7600084bc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -10,7 +10,7 @@ #include - +class GLGizmoMmuSegmentation; namespace Slic3r { @@ -37,7 +37,7 @@ public: // Render current selection. Transformation matrices are supposed // to be already set. - void render(ImGuiWrapper* imgui = nullptr); + virtual void render(ImGuiWrapper* imgui = nullptr); #ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG void render_debug(ImGuiWrapper* imgui); @@ -48,8 +48,9 @@ public: private: GLIndexedVertexArray m_iva_enforcers; GLIndexedVertexArray m_iva_blockers; - GLIndexedVertexArray m_iva_seed_fill; std::array m_varrays; +protected: + GLIndexedVertexArray m_iva_seed_fill; }; @@ -68,7 +69,7 @@ public: GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); ~GLGizmoPainterBase() override {} void set_painter_gizmo_data(const Selection& selection); - bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + virtual bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); // Following function renders the triangles and cursor. Having this separated // from usual on_render method allows to render them before transparent objects, @@ -146,6 +147,8 @@ protected: void on_load(cereal::BinaryInputArchive& ar) override; void on_save(cereal::BinaryOutputArchive& ar) const override {} CommonGizmosDataID on_get_requirements() const override; + + friend class GLGizmoMmuSegmentation; }; From 0a8a3f6d8c22cde6fbaf283e9102b878ef35fa6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 30 Apr 2021 15:40:27 +0200 Subject: [PATCH 17/26] Fixed build on Windows. --- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index a7600084bc..0f99e9b20e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -10,7 +10,6 @@ #include -class GLGizmoMmuSegmentation; namespace Slic3r { @@ -22,6 +21,7 @@ namespace GUI { enum class SLAGizmoEventType : unsigned char; class ClippingPlane; struct Camera; +class GLGizmoMmuSegmentation; enum class PainterGizmoType { FDM_SUPPORTS, @@ -148,7 +148,7 @@ protected: void on_save(cereal::BinaryOutputArchive& ar) const override {} CommonGizmosDataID on_get_requirements() const override; - friend class GLGizmoMmuSegmentation; + friend class ::Slic3r::GUI::GLGizmoMmuSegmentation; }; From 168b4afbc21876e6b4797658e907f33e767a920f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 3 May 2021 20:37:14 +0200 Subject: [PATCH 18/26] Fixed MMU segmentation for multi-volume objects. MMU segmentation no longer works directly on lslices, instead of it works on custom merged regions. So lslices in PrintObject are no longer overwritten because of MMU segmentation. All regions are scaled by SCALED_EPSILON before merging and shrunk back by SCALED_EPSILON after merging. That fixed issues with multi-volume objects when very close regions weren't merged. Also, small expolygons and holes are filtered out that fixed missing segmentation at the boundary of two volumes in the case of multi-volume objects. --- src/libslic3r/ExPolygon.cpp | 21 +++++++++++++++++ src/libslic3r/ExPolygon.hpp | 3 +++ src/libslic3r/MultiMaterialSegmentation.cpp | 26 ++++++++++++++------- src/libslic3r/PrintObject.cpp | 9 ------- 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp index 489023041f..506ba8cb6f 100644 --- a/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -372,6 +372,27 @@ bool remove_sticks(ExPolygon &poly) return remove_sticks(poly.contour) || remove_sticks(poly.holes); } +bool remove_small_and_small_holes(ExPolygons &expolygons, double min_area) +{ + bool modified = false; + size_t free_idx = 0; + for (size_t expoly_idx = 0; expoly_idx < expolygons.size(); ++expoly_idx) { + if (std::abs(expolygons[expoly_idx].area()) >= min_area) { + // Expolygon is big enough, so also check all its holes + modified |= remove_small(expolygons[expoly_idx].holes, min_area); + if (free_idx < expoly_idx) { + std::swap(expolygons[expoly_idx].contour, expolygons[free_idx].contour); + std::swap(expolygons[expoly_idx].holes, expolygons[free_idx].holes); + } + ++free_idx; + } else + modified = true; + } + if (free_idx < expolygons.size()) + expolygons.erase(expolygons.begin() + free_idx, expolygons.end()); + return modified; +} + void keep_largest_contour_only(ExPolygons &polygons) { if (polygons.size() > 1) { diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index 73770bb185..89ba2e0068 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -360,6 +360,9 @@ extern std::vector get_extents_vector(const ExPolygons &polygons); extern bool remove_sticks(ExPolygon &poly); extern void keep_largest_contour_only(ExPolygons &polygons); +// Removes all expolygons smaller than min_area and also removes all holes smaller than min_area +extern bool remove_small_and_small_holes(ExPolygons &expolygons, double min_area); + inline double area(const ExPolygons &polys) { double s = 0.; diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index aa25361083..1ed08ef4b9 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1357,13 +1357,27 @@ std::vector>> multi_material_segmentati std::vector> painted_lines(print_object.layers().size()); std::vector edge_grids(print_object.layers().size()); const ConstLayerPtrsAdaptor layers = print_object.layers(); + std::vector input_polygons(layers.size()); + + // Merge all regions and remove small holes + for(size_t layer_idx = 0; layer_idx < layers.size(); layer_idx += 1) { + ExPolygons ex_polygons; + for (LayerRegion *region : layers[layer_idx]->regions()) + for (const Surface &surface : region->slices.surfaces) + Slic3r::append(ex_polygons, offset_ex(surface.expolygon, SCALED_EPSILON)); + // All expolygons are expanded by SCALED_EPSILON, merged, and then shrunk again by SCALED_EPSILON + // to ensure that very close polygons will be merged. + ex_polygons = union_ex(ex_polygons); + // Remove all expolygons and holes with an area less than 0.01mm^2 + remove_small_and_small_holes(ex_polygons, Slic3r::sqr(scale_(0.1f))); + input_polygons[layer_idx] = to_polygons(union_ex(offset_ex(ex_polygons, -SCALED_EPSILON))); + } for (size_t layer_idx = 0; layer_idx < layers.size(); ++layer_idx) { - const Layer *layer = layers[layer_idx]; - BoundingBox bbox(get_extents(layer->lslices)); + BoundingBox bbox(get_extents(input_polygons[layer_idx])); bbox.offset(SCALED_EPSILON); edge_grids[layer_idx].set_bbox(bbox); - edge_grids[layer_idx].create(layer->lslices, coord_t(scale_(10.))); + edge_grids[layer_idx].create(input_polygons[layer_idx], coord_t(scale_(10.))); } for (const ModelVolume *mv : print_object.model_object()->volumes) { @@ -1453,11 +1467,7 @@ std::vector>> multi_material_segmentati std::vector &painted_lines_single = painted_lines[layer_idx]; if (!painted_lines_single.empty()) { - Polygons original_polygons; - for (const Slic3r::EdgeGrid::Contour &contour : edge_grids[layer_idx].contours()) - original_polygons.emplace_back(Points(contour.begin(), contour.end())); - - std::vector> color_poly = colorize_polygons(original_polygons, painted_lines_single); + std::vector> color_poly = colorize_polygons(input_polygons[layer_idx], painted_lines_single); MMU_Graph graph = build_graph(layer_idx, color_poly); remove_multiple_edges_in_vertices(graph, color_poly); graph.remove_nodes_with_one_arc(); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 1c5c4f0216..688b7d3607 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1906,15 +1906,6 @@ void PrintObject::_slice(const std::vector &layer_height_profile) // --------------------MMU_SEGMENTATION_BEGIN---------------------- - // Temporary fix for not assigned lslices - for(size_t layer_idx = 0; layer_idx < m_layers.size(); layer_idx += 1) { - ExPolygons ex_polygons; - for (LayerRegion *region : this->m_layers[layer_idx]->regions()) - for (const Surface &surface : region->slices.surfaces) - ex_polygons.emplace_back(surface.expolygon); - this->m_layers[layer_idx]->lslices = union_ex(ex_polygons); - } - size_t region_count_before_change = this->num_regions(); std::vector>> segmented_regions = multi_material_segmentation_by_painting(*this); // Skip region with default extruder From 2e9f0d6eaffb05399029df1a6c437ce480506bec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 3 May 2021 20:56:39 +0200 Subject: [PATCH 19/26] Fixed a few cases of missing colored segments in MMU segmentation. Occasionally, some input polygons contained self-intersections that caused problems with Voronoi diagrams and consequently with the extraction of colored segments by function extract_colored_segments. Also, occasionally input polygons contained several points very close together (distance between points is 1 or so). Such close points sometimes caused that the Voronoi diagram has self-intersecting edges around these vertices. This consequently leads to issues with the extraction of colored segments by function extract_colored_segments. --- src/libslic3r/MultiMaterialSegmentation.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index 1ed08ef4b9..011851614c 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1370,7 +1370,14 @@ std::vector>> multi_material_segmentati ex_polygons = union_ex(ex_polygons); // Remove all expolygons and holes with an area less than 0.01mm^2 remove_small_and_small_holes(ex_polygons, Slic3r::sqr(scale_(0.1f))); - input_polygons[layer_idx] = to_polygons(union_ex(offset_ex(ex_polygons, -SCALED_EPSILON))); + // Occasionally, some input polygons contained self-intersections that caused problems with Voronoi diagrams + // and consequently with the extraction of colored segments by function extract_colored_segments. + // Calling simplify_polygons removes these self-intersections. + // Also, occasionally input polygons contained several points very close together (distance between points is 1 or so). + // Such close points sometimes caused that the Voronoi diagram has self-intersecting edges around these vertices. + // This consequently leads to issues with the extraction of colored segments by function extract_colored_segments. + // Calling expolygons_simplify fixed these issues. + input_polygons[layer_idx] = simplify_polygons(to_polygons(expolygons_simplify(offset_ex(ex_polygons, -SCALED_EPSILON), SCALED_EPSILON))); } for (size_t layer_idx = 0; layer_idx < layers.size(); ++layer_idx) { From 5bfdaa7ac8771efb7f4ee4349633971c7b1e13fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 3 May 2021 21:01:52 +0200 Subject: [PATCH 20/26] Parallelization of regions merging for MMU segmentation. --- src/libslic3r/MultiMaterialSegmentation.cpp | 40 +++++++++++---------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index 011851614c..cccb67f377 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1360,25 +1360,27 @@ std::vector>> multi_material_segmentati std::vector input_polygons(layers.size()); // Merge all regions and remove small holes - for(size_t layer_idx = 0; layer_idx < layers.size(); layer_idx += 1) { - ExPolygons ex_polygons; - for (LayerRegion *region : layers[layer_idx]->regions()) - for (const Surface &surface : region->slices.surfaces) - Slic3r::append(ex_polygons, offset_ex(surface.expolygon, SCALED_EPSILON)); - // All expolygons are expanded by SCALED_EPSILON, merged, and then shrunk again by SCALED_EPSILON - // to ensure that very close polygons will be merged. - ex_polygons = union_ex(ex_polygons); - // Remove all expolygons and holes with an area less than 0.01mm^2 - remove_small_and_small_holes(ex_polygons, Slic3r::sqr(scale_(0.1f))); - // Occasionally, some input polygons contained self-intersections that caused problems with Voronoi diagrams - // and consequently with the extraction of colored segments by function extract_colored_segments. - // Calling simplify_polygons removes these self-intersections. - // Also, occasionally input polygons contained several points very close together (distance between points is 1 or so). - // Such close points sometimes caused that the Voronoi diagram has self-intersecting edges around these vertices. - // This consequently leads to issues with the extraction of colored segments by function extract_colored_segments. - // Calling expolygons_simplify fixed these issues. - input_polygons[layer_idx] = simplify_polygons(to_polygons(expolygons_simplify(offset_ex(ex_polygons, -SCALED_EPSILON), SCALED_EPSILON))); - } + tbb::parallel_for(tbb::blocked_range(0, layers.size()), [&](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + ExPolygons ex_polygons; + for (LayerRegion *region : layers[layer_idx]->regions()) + for (const Surface &surface : region->slices.surfaces) + Slic3r::append(ex_polygons, offset_ex(surface.expolygon, SCALED_EPSILON)); + // All expolygons are expanded by SCALED_EPSILON, merged, and then shrunk again by SCALED_EPSILON + // to ensure that very close polygons will be merged. + ex_polygons = union_ex(ex_polygons); + // Remove all expolygons and holes with an area less than 0.01mm^2 + remove_small_and_small_holes(ex_polygons, Slic3r::sqr(scale_(0.1f))); + // Occasionally, some input polygons contained self-intersections that caused problems with Voronoi diagrams + // and consequently with the extraction of colored segments by function extract_colored_segments. + // Calling simplify_polygons removes these self-intersections. + // Also, occasionally input polygons contained several points very close together (distance between points is 1 or so). + // Such close points sometimes caused that the Voronoi diagram has self-intersecting edges around these vertices. + // This consequently leads to issues with the extraction of colored segments by function extract_colored_segments. + // Calling expolygons_simplify fixed these issues. + input_polygons[layer_idx] = simplify_polygons(to_polygons(expolygons_simplify(offset_ex(ex_polygons, -SCALED_EPSILON), SCALED_EPSILON))); + } + }); // end of parallel_for for (size_t layer_idx = 0; layer_idx < layers.size(); ++layer_idx) { BoundingBox bbox(get_extents(input_polygons[layer_idx])); From fa8c319721f5b796f9ef2284655107fff377b2af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 3 May 2021 21:06:46 +0200 Subject: [PATCH 21/26] Fixed MMU segmentation for cases when a contour was whole colored by one color and a hole was whole colored by a different color. --- src/libslic3r/MultiMaterialSegmentation.cpp | 30 ++++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index cccb67f377..64f25dad11 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -694,6 +694,24 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vector force_edge_adding(color_poly.size()); + + // For each polygon, check if it is all colored with the same color. If it is, we need to force adding one edge to it. + for (const std::vector &c_poly : color_poly) { + bool force_edge = true; + for (const ColoredLine &c_line : c_poly) + if (c_line.color != c_poly.front().color) { + force_edge = false; + break; + } + force_edge_adding[&c_poly - &color_poly.front()] = force_edge; + } + boost::polygon::construct_voronoi(lines_colored.begin(), lines_colored.end(), &vd); MMU_Graph graph; for (const Point &point : points) @@ -861,23 +879,27 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vectorvertex0())) { if (is_point_closer_to_beginning_of_line(contour_line, v0)) { - if (!has_same_color(contour_line_prev, colored_line) && points_inside(contour_line_prev.line, contour_line, v1)) { + if ((!has_same_color(contour_line_prev, colored_line) || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line_prev.line, contour_line, v1)) { graph.append_edge(from_idx, to_idx); + force_edge_adding[colored_line.poly_idx] = false; } } else { - if (!has_same_color(contour_line_next, colored_line) && points_inside(contour_line, contour_line_next.line, v1)) { + if ((!has_same_color(contour_line_next, colored_line) || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line, contour_line_next.line, v1)) { graph.append_edge(from_idx, to_idx); + force_edge_adding[colored_line.poly_idx] = false; } } } else { assert(graph.is_vertex_on_contour(edge_it->vertex1())); if (is_point_closer_to_beginning_of_line(contour_line, v1)) { - if (!has_same_color(contour_line_prev, colored_line) && points_inside(contour_line_prev.line, contour_line, v0)) { + if ((!has_same_color(contour_line_prev, colored_line) || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line_prev.line, contour_line, v0)) { graph.append_edge(from_idx, to_idx); + force_edge_adding[colored_line.poly_idx] = false; } } else { - if (!has_same_color(contour_line_next, colored_line) && points_inside(contour_line, contour_line_next.line, v0)) { + if ((!has_same_color(contour_line_next, colored_line) || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line, contour_line_next.line, v0)) { graph.append_edge(from_idx, to_idx); + force_edge_adding[colored_line.poly_idx] = false; } } } From 2d3eef4e845a04e2163aebccb535546f2c6e1fd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 3 May 2021 21:13:13 +0200 Subject: [PATCH 22/26] Fixed possible warnings --- src/libslic3r/MultiMaterialSegmentation.cpp | 10 ++++----- src/libslic3r/TriangleSelector.cpp | 2 +- .../GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 21 +++++++++---------- .../GUI/Gizmos/GLGizmoMmuSegmentation.hpp | 4 ++-- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 4 ++-- 5 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index 64f25dad11..9e26adcac3 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -6,7 +6,7 @@ #include "VoronoiVisualUtils.hpp" #include -#include +#include #include #include @@ -28,7 +28,7 @@ struct ColoredLine { } #include -namespace boost { namespace polygon { +namespace boost::polygon { template <> struct geometry_concept { typedef segment_concept type; }; @@ -37,11 +37,11 @@ struct segment_traits { typedef coord_t coordinate_type; typedef Slic3r::Point point_type; - static inline point_type get(const Slic3r::ColoredLine& line, direction_1d dir) { + static inline point_type get(const Slic3r::ColoredLine& line, const direction_1d& dir) { return dir.to_int() ? line.line.b : line.line.a; } }; -} } +} namespace Slic3r { @@ -1471,7 +1471,7 @@ std::vector>> multi_material_segmentati visitor.reset(); visitor.line_to_test.a = line_start; visitor.line_to_test.b = line_end; - visitor.color = extruder_idx; + visitor.color = int(extruder_idx); edge_grids[layer_idx].visit_cells_intersecting_line(line_start, line_end, visitor); append(painted_lines[layer_idx], std::move(painted_line_tmp)); diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index f5907511bd..0846cec89c 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -699,7 +699,7 @@ void TriangleSelector::deserialize(const std::map> data) int num_of_children = num_of_split_sides != 0 ? num_of_split_sides + 1 : 0; bool is_split = num_of_children != 0; // Value of the second nibble was subtracted by 3, so it is added back. - EnforcerBlockerType state = EnforcerBlockerType(next_code[0] >> 2 == 0b11 ? next_code[1] + 3 : next_code[0] >> 2); + auto state = EnforcerBlockerType(next_code[0] >> 2 == 0b11 ? next_code[1] + 3 : next_code[0] >> 2); int special_side = (next_code[0] >> 2); // Take care of the first iteration separately, so handling of the others is simpler. diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index f069cd2f72..11192ecd69 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -1,8 +1,5 @@ #include "GLGizmoMmuSegmentation.hpp" -#include "libslic3r/Model.hpp" - -//#include "slic3r/GUI/3DScene.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/ImGuiWrapper.hpp" @@ -10,6 +7,7 @@ #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/BitmapCache.hpp" #include "libslic3r/PresetBundle.hpp" +#include "libslic3r/Model.hpp" #include @@ -210,7 +208,7 @@ bool GLGizmoMmuSegmentation::gizmo_event(SLAGizmoEventType action, const Vec2d& if (m_seed_fill_enabled) m_triangle_selectors[m_rr.mesh_id]->seed_fill_apply_on_triangles(new_state); else - m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, m_rr.facet, camera_pos, m_cursor_radius, m_cursor_type, + m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, int(m_rr.facet), camera_pos, m_cursor_radius, m_cursor_type, new_state, trafo_matrix, m_triangle_splitting_enabled); m_last_mouse_click = mouse_position; } @@ -247,7 +245,7 @@ bool GLGizmoMmuSegmentation::gizmo_event(SLAGizmoEventType action, const Vec2d& } assert(m_rr.mesh_id < int(m_triangle_selectors.size())); - m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, m_rr.facet, m_seed_fill_angle); + m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), m_seed_fill_angle); return true; } @@ -277,7 +275,7 @@ bool GLGizmoMmuSegmentation::gizmo_event(SLAGizmoEventType action, const Vec2d& } activate_internal_undo_redo_stack(true); - Plater::TakeSnapshot(wxGetApp().plater(), action_name); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), action_name); update_model_object(); m_button_down = Button::None; @@ -303,7 +301,7 @@ static void render_extruders_combo(const std::string &la ImVec2 combo_pos = ImGui::GetCursorScreenPos(); if (ImGui::BeginCombo(label.c_str(), "")) { for (size_t extruder_idx = 0; extruder_idx < extruders.size(); ++extruder_idx) { - ImGui::PushID(extruder_idx); + ImGui::PushID(int(extruder_idx)); ImVec2 start_position = ImGui::GetCursorScreenPos(); if (ImGui::Selectable("", extruder_idx == selection_idx)) @@ -439,7 +437,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott ImGui::Separator(); if (m_imgui->button(m_desc.at("remove_all"))) { - Plater::TakeSnapshot(wxGetApp().plater(), wxString(_L("Reset selection"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), wxString(_L("Reset selection"))); ModelObject *mo = m_c->selection_info()->model_object(); int idx = -1; for (ModelVolume *mv : mo->volumes) { @@ -515,7 +513,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott ImGui::SameLine(clipping_slider_left); ImGui::PushItemWidth(window_width - clipping_slider_left); - float clp_dist = m_c->object_clipper()->get_position(); + auto clp_dist = float(m_c->object_clipper()->get_position()); if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f")) m_c->object_clipper()->set_position(clp_dist, true); if (ImGui::IsItemHovered()) { @@ -632,14 +630,15 @@ void TriangleSelectorMmuGui::render(ImGuiWrapper *imgui) for (size_t color_idx = 0; color_idx < m_iva_colors.size(); ++color_idx) { if (render_colors[color_idx]) { - std::array color = {m_colors[color_idx][0] / 255.0f, m_colors[color_idx][1] / 255.0f, m_colors[color_idx][2] / 255.0f, 1.f}; + std::array color = {float(m_colors[color_idx][0]) / 255.0f, float(m_colors[color_idx][1]) / 255.0f, + float(m_colors[color_idx][2]) / 255.0f, 1.f}; shader->set_uniform("uniform_color", color); m_iva_colors[color_idx].render(); } } if (render_seed_fill) { - std::array color = {0.f, 1.00f, 0.44f, 1.f}; + std::array color = {0.f, 1.f, 0.44f, 1.f}; shader->set_uniform("uniform_color", color); m_iva_seed_fill.render(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp index 0653fd007b..e6673e2309 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp @@ -14,7 +14,7 @@ public: // Render current selection. Transformation matrices are supposed // to be already set. - virtual void render(ImGuiWrapper* imgui = nullptr); + void render(ImGuiWrapper* imgui) override; private: const std::vector> &m_colors; @@ -29,7 +29,7 @@ public: void render_painter_gizmo() const override; - virtual bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) override; + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) override; protected: void on_render_input_window(float x, float y, float bottom_limit) override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 56f33afdd6..9db2d5e6dd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -355,7 +355,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous if (m_seed_fill_enabled) m_triangle_selectors[m_rr.mesh_id]->seed_fill_apply_on_triangles(new_state); else - m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, m_rr.facet, camera_pos, m_cursor_radius, m_cursor_type, + m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, int(m_rr.facet), camera_pos, m_cursor_radius, m_cursor_type, new_state, trafo_matrix, m_triangle_splitting_enabled); m_last_mouse_click = mouse_position; } @@ -392,7 +392,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous } assert(m_rr.mesh_id < int(m_triangle_selectors.size())); - m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, m_rr.facet, m_seed_fill_angle); + m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), m_seed_fill_angle); return true; } From cd5fea8b42d850840276fc3cc266c1d2cba28aaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 3 May 2021 21:50:40 +0200 Subject: [PATCH 23/26] Fixed compiler warnings --- src/libslic3r/MultiMaterialSegmentation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index 9e26adcac3..5404057bb2 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1387,7 +1387,7 @@ std::vector>> multi_material_segmentati ExPolygons ex_polygons; for (LayerRegion *region : layers[layer_idx]->regions()) for (const Surface &surface : region->slices.surfaces) - Slic3r::append(ex_polygons, offset_ex(surface.expolygon, SCALED_EPSILON)); + Slic3r::append(ex_polygons, offset_ex(surface.expolygon, float(SCALED_EPSILON))); // All expolygons are expanded by SCALED_EPSILON, merged, and then shrunk again by SCALED_EPSILON // to ensure that very close polygons will be merged. ex_polygons = union_ex(ex_polygons); @@ -1400,7 +1400,7 @@ std::vector>> multi_material_segmentati // Such close points sometimes caused that the Voronoi diagram has self-intersecting edges around these vertices. // This consequently leads to issues with the extraction of colored segments by function extract_colored_segments. // Calling expolygons_simplify fixed these issues. - input_polygons[layer_idx] = simplify_polygons(to_polygons(expolygons_simplify(offset_ex(ex_polygons, -SCALED_EPSILON), SCALED_EPSILON))); + input_polygons[layer_idx] = simplify_polygons(to_polygons(expolygons_simplify(offset_ex(ex_polygons, float(-SCALED_EPSILON)), SCALED_EPSILON))); } }); // end of parallel_for From 71c60a5187e8a86d0d73832605f9c2bdd2f1267c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 3 May 2021 22:11:04 +0200 Subject: [PATCH 24/26] Follow-up of 168b4afbc21876e6b4797658e907f33e767a920f: Replaced forgotten lslices in the top and bottom layer propagation in MMU segmentation. --- src/libslic3r/MultiMaterialSegmentation.cpp | 58 +++++++++++---------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index 5404057bb2..ad3975353d 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1125,12 +1125,12 @@ static void remove_multiple_edges_in_vertices(MMU_Graph &graph, const std::vecto } } -static void cut_segmented_layers(const ConstLayerPtrsAdaptor layers, std::vector>> &segmented_regions, const float cut_width) { +static void cut_segmented_layers(const std::vector &input_expolygons, std::vector>> &segmented_regions, const float cut_width) { tbb::parallel_for(tbb::blocked_range(0, segmented_regions.size()),[&](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { std::vector> segmented_regions_cuts; for (const std::pair &colored_expoly : segmented_regions[layer_idx]) { - ExPolygons cut_colored_expoly = diff_ex({colored_expoly.first}, offset_ex(layers[layer_idx]->lslices, cut_width)); + ExPolygons cut_colored_expoly = diff_ex({colored_expoly.first}, offset_ex(input_expolygons[layer_idx], cut_width)); for (const ExPolygon &expoly : cut_colored_expoly) { segmented_regions_cuts.emplace_back(expoly, colored_expoly.second); } @@ -1141,7 +1141,7 @@ static void cut_segmented_layers(const ConstLayerPtrsAdaptor layers, std::vector } // Returns MMU segmentation of top and bottom layers based on painting in MMU segmentation gizmo -static inline std::vector> mmu_segmentation_top_and_bottom_layers(const PrintObject &print_object) +static inline std::vector> mmu_segmentation_top_and_bottom_layers(const PrintObject &print_object, const std::vector &input_expolygons) { const size_t num_extruders = print_object.print()->config().nozzle_diameter.size(); const ConstLayerPtrsAdaptor layers = print_object.layers(); @@ -1224,25 +1224,25 @@ static inline std::vector> mmu_segmentation_top_and_bott return (*top_bottom_layer_it)->region()->config().bottom_solid_layers; }; - std::vector top_layers(layers.size()); - top_layers.back() = layers.back()->lslices; - tbb::parallel_for(tbb::blocked_range(1, layers.size()), [&](const tbb::blocked_range &range) { + std::vector top_layers(input_expolygons.size()); + top_layers.back() = input_expolygons.back(); + tbb::parallel_for(tbb::blocked_range(1, input_expolygons.size()), [&](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { float extrusion_width = 0.1f * float(scale_(get_extrusion_width(layer_idx))); - top_layers[layer_idx - 1] = diff_ex(layers[layer_idx - 1]->lslices, offset_ex(layers[layer_idx]->lslices, extrusion_width)); + top_layers[layer_idx - 1] = diff_ex(input_expolygons[layer_idx - 1], offset_ex(input_expolygons[layer_idx], extrusion_width)); } }); // end of parallel_for - std::vector bottom_layers(layers.size()); - bottom_layers.front() = layers.front()->lslices; - tbb::parallel_for(tbb::blocked_range(0, layers.size() - 1), [&](const tbb::blocked_range &range) { + std::vector bottom_layers(input_expolygons.size()); + bottom_layers.front() = input_expolygons.front(); + tbb::parallel_for(tbb::blocked_range(0, input_expolygons.size() - 1), [&](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { float extrusion_width = 0.1f * float(scale_(get_extrusion_width(layer_idx))); - bottom_layers[layer_idx + 1] = diff_ex(layers[layer_idx + 1]->lslices, offset_ex(layers[layer_idx]->lslices, extrusion_width)); + bottom_layers[layer_idx + 1] = diff_ex(input_expolygons[layer_idx + 1], offset_ex(input_expolygons[layer_idx], extrusion_width)); } }); // end of parallel_for - tbb::parallel_for(tbb::blocked_range(0, print_object.layers().size()), [&](const tbb::blocked_range &range) { + tbb::parallel_for(tbb::blocked_range(0, input_expolygons.size()), [&](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { float extrusion_width = 0.1f * float(scale_(get_extrusion_width(layer_idx))); for (std::vector &triangles : triangles_by_color) { @@ -1258,10 +1258,10 @@ static inline std::vector> mmu_segmentation_top_and_bott std::vector> triangles_by_color_bottom(num_extruders); std::vector> triangles_by_color_top(num_extruders); - triangles_by_color_bottom.assign(num_extruders, std::vector(layers.size())); - triangles_by_color_top.assign(num_extruders, std::vector(layers.size())); + triangles_by_color_bottom.assign(num_extruders, std::vector(input_expolygons.size())); + triangles_by_color_top.assign(num_extruders, std::vector(input_expolygons.size())); - for (size_t layer_idx = 0; layer_idx < print_object.layers().size(); ++layer_idx) { + for (size_t layer_idx = 0; layer_idx < input_expolygons.size(); ++layer_idx) { BOOST_LOG_TRIVIAL(debug) << "MMU segmentation of top layer: " << layer_idx; float extrusion_width = scale_(get_extrusion_width(layer_idx)); int top_solid_layers = get_top_solid_layers(layer_idx); @@ -1280,10 +1280,10 @@ static inline std::vector> mmu_segmentation_top_and_bott float offset_value = float(layer_idx - last_idx) * (-1.0f) * extrusion_width; if (offset_ex(top_expolygon, offset_value).empty()) continue; - ExPolygons layer_slices_trimmed = layers[last_idx]->lslices; + ExPolygons layer_slices_trimmed = input_expolygons[last_idx]; for (int last_idx_1 = last_idx; last_idx_1 < int(layer_idx); ++last_idx_1) { - layer_slices_trimmed = intersection_ex(layer_slices_trimmed, layers[last_idx_1 + 1]->lslices); + layer_slices_trimmed = intersection_ex(layer_slices_trimmed, input_expolygons[last_idx_1 + 1]); } ExPolygons offset_e = offset_ex(layer_slices_trimmed, offset_value); @@ -1295,7 +1295,7 @@ static inline std::vector> mmu_segmentation_top_and_bott } } - for (size_t layer_idx = 0; layer_idx < print_object.layers().size(); ++layer_idx) { + for (size_t layer_idx = 0; layer_idx < input_expolygons.size(); ++layer_idx) { BOOST_LOG_TRIVIAL(debug) << "MMU segmentation of bottom layer: " << layer_idx; float extrusion_width = scale_(get_extrusion_width(layer_idx)); int bottom_solid_layers = get_bottom_solid_layers(layer_idx); @@ -1311,13 +1311,13 @@ static inline std::vector> mmu_segmentation_top_and_bott if (!intersection_poly.empty()) { triangles_by_color_bottom[color_idx][layer_idx].insert(triangles_by_color_bottom[color_idx][layer_idx].end(), intersection_poly.begin(), intersection_poly.end()); - for (size_t last_idx = layer_idx + 1; last_idx < std::min(layer_idx + bottom_solid_layers, layers.size()); ++last_idx) { + for (size_t last_idx = layer_idx + 1; last_idx < std::min(layer_idx + bottom_solid_layers, input_expolygons.size()); ++last_idx) { float offset_value = float(last_idx - layer_idx) * (-1.0f) * extrusion_width; if (offset_ex(bottom_expolygon, offset_value).empty()) continue; - ExPolygons layer_slices_trimmed = layers[last_idx]->lslices; + ExPolygons layer_slices_trimmed = input_expolygons[last_idx]; for (int last_idx_1 = int(last_idx); last_idx_1 > int(layer_idx); --last_idx_1) { - layer_slices_trimmed = intersection_ex(layer_slices_trimmed, offset_ex(layers[last_idx_1 - 1]->lslices, offset_value)); + layer_slices_trimmed = intersection_ex(layer_slices_trimmed, offset_ex(input_expolygons[last_idx_1 - 1], offset_value)); } ExPolygons offset_e = offset_ex(layer_slices_trimmed, offset_value); @@ -1329,8 +1329,8 @@ static inline std::vector> mmu_segmentation_top_and_bott } std::vector> triangles_by_color_merged(num_extruders); - triangles_by_color_merged.assign(num_extruders, std::vector(layers.size())); - for (size_t layer_idx = 0; layer_idx < layers.size(); ++layer_idx) { + triangles_by_color_merged.assign(num_extruders, std::vector(input_expolygons.size())); + for (size_t layer_idx = 0; layer_idx < input_expolygons.size(); ++layer_idx) { for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) { auto &self = triangles_by_color_merged[color_idx][layer_idx]; append(self, std::move(triangles_by_color_bottom[color_idx][layer_idx])); @@ -1379,6 +1379,7 @@ std::vector>> multi_material_segmentati std::vector> painted_lines(print_object.layers().size()); std::vector edge_grids(print_object.layers().size()); const ConstLayerPtrsAdaptor layers = print_object.layers(); + std::vector input_expolygons(layers.size()); std::vector input_polygons(layers.size()); // Merge all regions and remove small holes @@ -1400,15 +1401,16 @@ std::vector>> multi_material_segmentati // Such close points sometimes caused that the Voronoi diagram has self-intersecting edges around these vertices. // This consequently leads to issues with the extraction of colored segments by function extract_colored_segments. // Calling expolygons_simplify fixed these issues. - input_polygons[layer_idx] = simplify_polygons(to_polygons(expolygons_simplify(offset_ex(ex_polygons, float(-SCALED_EPSILON)), SCALED_EPSILON))); + input_expolygons[layer_idx] = simplify_polygons_ex(to_polygons(expolygons_simplify(offset_ex(ex_polygons, float(-SCALED_EPSILON)), SCALED_EPSILON))); + input_polygons[layer_idx] = to_polygons(input_expolygons[layer_idx]); } }); // end of parallel_for for (size_t layer_idx = 0; layer_idx < layers.size(); ++layer_idx) { - BoundingBox bbox(get_extents(input_polygons[layer_idx])); + BoundingBox bbox(get_extents(input_expolygons[layer_idx])); bbox.offset(SCALED_EPSILON); edge_grids[layer_idx].set_bbox(bbox); - edge_grids[layer_idx].create(input_polygons[layer_idx], coord_t(scale_(10.))); + edge_grids[layer_idx].create(input_expolygons[layer_idx], coord_t(scale_(10.))); } for (const ModelVolume *mv : print_object.model_object()->volumes) { @@ -1510,10 +1512,10 @@ std::vector>> multi_material_segmentati }); // end of parallel_for if (auto w = print_object.print()->config().mmu_segmented_region_max_width; w > 0.f) - cut_segmented_layers(layers, segmented_regions, float(-scale_(w))); + cut_segmented_layers(input_expolygons, segmented_regions, float(-scale_(w))); // return segmented_regions; - std::vector> top_and_bottom_layers = mmu_segmentation_top_and_bottom_layers(print_object); + std::vector> top_and_bottom_layers = mmu_segmentation_top_and_bottom_layers(print_object, input_expolygons); std::vector>> segmented_regions_merged = merge_segmented_layers(segmented_regions, std::move(top_and_bottom_layers)); return segmented_regions_merged; } From 781e6607c89ea5d5196cd4c2b07a209ca9211db5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 4 May 2021 07:59:16 +0200 Subject: [PATCH 25/26] Fixed updating of extruders list in MMU segmentation gizmo after adding extruders and reloading 3MF with different extruder count and colors. --- .../GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 83 ++++++++++++------- .../GUI/Gizmos/GLGizmoMmuSegmentation.hpp | 14 +++- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 2 +- 3 files changed, 64 insertions(+), 35 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index 11192ecd69..962015ddeb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -16,8 +16,6 @@ namespace Slic3r::GUI { void GLGizmoMmuSegmentation::on_shutdown() { -// m_seed_fill_angle = 0.f; -// m_seed_fill_enabled = false; m_parent.use_slope(false); } @@ -30,7 +28,7 @@ std::string GLGizmoMmuSegmentation::on_get_name() const bool GLGizmoMmuSegmentation::on_is_selectable() const { return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF - && wxGetApp().get_mode() != comSimple && wxGetApp().extruders_cnt() > 1); + && wxGetApp().get_mode() != comSimple && wxGetApp().extruders_edited_cnt() > 1); } static std::vector> get_extruders_colors() @@ -49,7 +47,7 @@ static std::vector> get_extruders_colors() static std::vector get_extruders_names() { - size_t extruders_count = wxGetApp().extruders_cnt(); + size_t extruders_count = wxGetApp().extruders_edited_cnt(); std::vector extruders_out; extruders_out.reserve(extruders_count); for (size_t extruder_idx = 1; extruder_idx <= extruders_count; ++extruder_idx) @@ -58,6 +56,15 @@ static std::vector get_extruders_names() return extruders_out; } +void GLGizmoMmuSegmentation::init_extruders_data() +{ + m_original_extruders_names = get_extruders_names(); + m_original_extruders_colors = get_extruders_colors(); + m_modified_extruders_colors = m_original_extruders_colors; + m_first_selected_extruder_idx = 0; + m_second_selected_extruder_idx = 1; +} + bool GLGizmoMmuSegmentation::on_init() { // FIXME Lukas H.: Discuss and change shortcut @@ -78,8 +85,7 @@ bool GLGizmoMmuSegmentation::on_init() m_desc["sphere"] = _L("Sphere"); m_desc["seed_fill_angle"] = _L("Seed fill angle"); - m_extruders_names = get_extruders_names(); - m_extruders_colors = get_extruders_colors(); + init_extruders_data(); return true; } @@ -286,6 +292,22 @@ bool GLGizmoMmuSegmentation::gizmo_event(SLAGizmoEventType action, const Vec2d& return false; } +void GLGizmoMmuSegmentation::set_painter_gizmo_data(const Selection &selection) +{ + GLGizmoPainterBase::set_painter_gizmo_data(selection); + + if (m_state != On) + return; + + size_t prev_extruders_count = m_original_extruders_colors.size(); + if (prev_extruders_count != wxGetApp().extruders_edited_cnt() || get_extruders_colors() != m_original_extruders_colors) { + this->init_extruders_data(); + // Reinitialize triangle selectors because of change of extruder count need also change the size of GLIndexedVertexArray + if (prev_extruders_count != wxGetApp().extruders_edited_cnt()) + this->init_model_triangle_selectors(); + } +} + static void render_extruders_combo(const std::string &label, const std::vector &extruders, const std::vector> &extruders_colors, @@ -395,27 +417,27 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott m_imgui->text(""); ImGui::Separator(); - const std::array &select_first_color = m_extruders_colors[m_first_selected_extruder_idx]; - const std::array &select_second_color = m_extruders_colors[m_second_selected_extruder_idx]; + const std::array &select_first_color = m_modified_extruders_colors[m_first_selected_extruder_idx]; + const std::array &select_second_color = m_modified_extruders_colors[m_second_selected_extruder_idx]; m_imgui->text(m_desc.at("first_color")); ImGui::SameLine(combo_label_width); ImGui::PushItemWidth(window_width - combo_label_width - color_button_width); - render_extruders_combo("##first_color_combo", m_extruders_names, get_extruders_colors(), m_first_selected_extruder_idx); + render_extruders_combo("##first_color_combo", m_original_extruders_names, m_original_extruders_colors, m_first_selected_extruder_idx); ImGui::SameLine(); ImVec4 first_color = ImVec4(float(select_first_color[0]) / 255.0f, float(select_first_color[1]) / 255.0f, float(select_first_color[2]) / 255.0f, 1.0f); ImVec4 second_color = ImVec4(float(select_second_color[0]) / 255.0f, float(select_second_color[1]) / 255.0f, float(select_second_color[2]) / 255.0f, 1.0f); if(ImGui::ColorEdit4("First color##color_picker", (float*)&first_color, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel)) - m_extruders_colors[m_first_selected_extruder_idx] = {uint8_t(first_color.x * 255.0f), uint8_t(first_color.y * 255.0f), uint8_t(first_color.z * 255.0f)}; + m_modified_extruders_colors[m_first_selected_extruder_idx] = {uint8_t(first_color.x * 255.0f), uint8_t(first_color.y * 255.0f), uint8_t(first_color.z * 255.0f)}; m_imgui->text(m_desc.at("second_color")); ImGui::SameLine(combo_label_width); ImGui::PushItemWidth(window_width - combo_label_width - color_button_width); - render_extruders_combo("##second_color_combo", m_extruders_names, get_extruders_colors(), m_second_selected_extruder_idx); + render_extruders_combo("##second_color_combo", m_original_extruders_names, m_original_extruders_colors, m_second_selected_extruder_idx); ImGui::SameLine(); if(ImGui::ColorEdit4("Second color##color_picker", (float*)&second_color, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel)) - m_extruders_colors[m_second_selected_extruder_idx] = {uint8_t(second_color.x * 255.0f), uint8_t(second_color.y * 255.0f), uint8_t(second_color.z * 255.0f)}; + m_modified_extruders_colors[m_second_selected_extruder_idx] = {uint8_t(second_color.x * 255.0f), uint8_t(second_color.y * 255.0f), uint8_t(second_color.z * 255.0f)}; ImGui::Separator(); @@ -443,7 +465,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott for (ModelVolume *mv : mo->volumes) { if (mv->is_model_part()) { ++idx; - m_triangle_selectors[idx]->reset(); + m_triangle_selectors[idx]->reset(EnforcerBlockerType(mv->extruder_id())); } } @@ -542,26 +564,27 @@ void GLGizmoMmuSegmentation::update_model_object() const m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } +void GLGizmoMmuSegmentation::init_model_triangle_selectors() +{ + const ModelObject *mo = m_c->selection_info()->model_object(); + m_triangle_selectors.clear(); + + for (const ModelVolume *mv : mo->volumes) { + if (!mv->is_model_part()) + continue; + + // This mesh does not account for the possible Z up SLA offset. + const TriangleMesh *mesh = &mv->mesh(); + + m_triangle_selectors.emplace_back(std::make_unique(*mesh, m_modified_extruders_colors)); + m_triangle_selectors.back()->deserialize(mv->mmu_segmentation_facets.get_data()); + } +} + void GLGizmoMmuSegmentation::update_from_model_object() { wxBusyCursor wait; - - const ModelObject* mo = m_c->selection_info()->model_object(); - m_triangle_selectors.clear(); - - int volume_id = -1; - for (const ModelVolume* mv : mo->volumes) { - if (! mv->is_model_part()) - continue; - - ++volume_id; - - // This mesh does not account for the possible Z up SLA offset. - const TriangleMesh* mesh = &mv->mesh(); - - m_triangle_selectors.emplace_back(std::make_unique(*mesh, wxGetApp().extruders_cnt(), m_extruders_colors)); - m_triangle_selectors.back()->deserialize(mv->mmu_segmentation_facets.get_data()); - } + this->init_model_triangle_selectors(); } PainterGizmoType GLGizmoMmuSegmentation::get_painter_type() const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp index e6673e2309..35ade52fb7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp @@ -7,9 +7,9 @@ namespace Slic3r::GUI { class TriangleSelectorMmuGui : public TriangleSelectorGUI { public: - explicit TriangleSelectorMmuGui(const TriangleMesh& mesh, size_t extruder_count, const std::vector> &colors) + explicit TriangleSelectorMmuGui(const TriangleMesh& mesh, const std::vector> &colors) : TriangleSelectorGUI(mesh), m_colors(colors) { - m_iva_colors = std::vector(extruder_count); + m_iva_colors = std::vector(colors.size()); } // Render current selection. Transformation matrices are supposed @@ -31,6 +31,8 @@ public: bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) override; + virtual void set_painter_gizmo_data(const Selection& selection) override; + protected: void on_render_input_window(float x, float y, float bottom_limit) override; std::string on_get_name() const override; @@ -39,8 +41,9 @@ protected: size_t m_first_selected_extruder_idx = 0; size_t m_second_selected_extruder_idx = 1; - std::vector m_extruders_names; - std::vector> m_extruders_colors; + std::vector m_original_extruders_names; + std::vector> m_original_extruders_colors; + std::vector> m_modified_extruders_colors; private: bool on_init() override; @@ -52,6 +55,9 @@ private: void on_shutdown() override; PainterGizmoType get_painter_type() const override; + void init_model_triangle_selectors(); + void init_extruders_data(); + // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. std::map m_desc; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index 0f99e9b20e..55e4df865f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -68,7 +68,7 @@ private: public: GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); ~GLGizmoPainterBase() override {} - void set_painter_gizmo_data(const Selection& selection); + virtual void set_painter_gizmo_data(const Selection& selection); virtual bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); // Following function renders the triangles and cursor. Having this separated From 46a14abbaae75b581bb71d625d76e8c5ff9e4278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 4 May 2021 08:13:04 +0200 Subject: [PATCH 26/26] Added default object color in MMU segmentation according to the default extruder color for printing the object. --- src/libslic3r/TriangleSelector.cpp | 12 ++++++------ src/libslic3r/TriangleSelector.hpp | 10 +++++----- src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 11 ++++++----- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 0846cec89c..95f2984e32 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -463,7 +463,7 @@ TriangleSelector::TriangleSelector(const TriangleMesh& mesh) } -void TriangleSelector::reset() +void TriangleSelector::reset(const EnforcerBlockerType reset_state) { if (m_orig_size_indices != 0) // unless this is run from constructor garbage_collect(); @@ -474,7 +474,7 @@ void TriangleSelector::reset() for (size_t i=0; iits.indices.size(); ++i) { const stl_triangle_vertex_indices& ind = m_mesh->its.indices[i]; const Vec3f& normal = m_mesh->stl.facet_start[i].normal; - push_triangle(ind[0], ind[1], ind[2], normal); + push_triangle(ind[0], ind[1], ind[2], normal, reset_state); } m_orig_size_vertices = m_vertices.size(); m_orig_size_indices = m_triangles.size(); @@ -500,13 +500,13 @@ void TriangleSelector::set_edge_limit(float edge_limit) -void TriangleSelector::push_triangle(int a, int b, int c, const Vec3f& normal) +void TriangleSelector::push_triangle(int a, int b, int c, const Vec3f& normal, const EnforcerBlockerType state) { for (int i : {a, b, c}) { assert(i >= 0 && i < int(m_vertices.size())); ++m_vertices[i].ref_cnt; } - m_triangles.emplace_back(a, b, c, normal); + m_triangles.emplace_back(a, b, c, normal, state); } @@ -663,9 +663,9 @@ std::map> TriangleSelector::serialize() const return out; } -void TriangleSelector::deserialize(const std::map> data) +void TriangleSelector::deserialize(const std::map> data, const EnforcerBlockerType init_state) { - reset(); // dump any current state + reset(init_state); // dump any current state for (const auto& [triangle_id, code] : data) { assert(triangle_id < int(m_triangles.size())); assert(! code.empty()); diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index 1a420c547b..383aab4809 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -49,7 +49,7 @@ public: void set_facet(int facet_idx, EnforcerBlockerType state); // Clear everything and make the tree empty. - void reset(); + void reset(const EnforcerBlockerType reset_state = EnforcerBlockerType{0}); // Remove all unnecessary data. void garbage_collect(); @@ -59,7 +59,7 @@ public: std::map> serialize() const; // Load serialized data. Assumes that correct mesh is loaded. - void deserialize(const std::map> data); + void deserialize(const std::map> data, const EnforcerBlockerType init_state = EnforcerBlockerType{0}); // For all triangles, remove the flag indicating that the triangle was selected by seed fill. void seed_fill_unselect_all_triangles(); @@ -73,10 +73,10 @@ protected: public: // Use TriangleSelector::push_triangle to create a new triangle. // It increments/decrements reference counter on vertices. - Triangle(int a, int b, int c, const Vec3f& normal_) + Triangle(int a, int b, int c, const Vec3f& normal_, const EnforcerBlockerType init_state) : verts_idxs{a, b, c}, normal{normal_}, - state{EnforcerBlockerType(0)}, + state{init_state}, number_of_splits{0}, special_side_idx{0}, old_number_of_splits{0} @@ -178,7 +178,7 @@ protected: void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant. bool is_pointer_in_triangle(int facet_idx) const; bool is_edge_inside_cursor(int facet_idx) const; - void push_triangle(int a, int b, int c, const Vec3f& normal); + void push_triangle(int a, int b, int c, const Vec3f &normal, const EnforcerBlockerType state = EnforcerBlockerType{0}); void perform_split(int facet_idx, EnforcerBlockerType old_state); }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index 962015ddeb..e5acda8c6b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -299,7 +299,7 @@ void GLGizmoMmuSegmentation::set_painter_gizmo_data(const Selection &selection) if (m_state != On) return; - size_t prev_extruders_count = m_original_extruders_colors.size(); + int prev_extruders_count = m_original_extruders_colors.size(); if (prev_extruders_count != wxGetApp().extruders_edited_cnt() || get_extruders_colors() != m_original_extruders_colors) { this->init_extruders_data(); // Reinitialize triangle selectors because of change of extruder count need also change the size of GLIndexedVertexArray @@ -465,7 +465,8 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott for (ModelVolume *mv : mo->volumes) { if (mv->is_model_part()) { ++idx; - m_triangle_selectors[idx]->reset(EnforcerBlockerType(mv->extruder_id())); + size_t extruder_id = (mv->extruder_id() > 0) ? mv->extruder_id() - 1 : 0; + m_triangle_selectors[idx]->reset(EnforcerBlockerType(extruder_id)); } } @@ -576,8 +577,9 @@ void GLGizmoMmuSegmentation::init_model_triangle_selectors() // This mesh does not account for the possible Z up SLA offset. const TriangleMesh *mesh = &mv->mesh(); + size_t extruder_id = (mv->extruder_id() > 0) ? mv->extruder_id() - 1 : 0; m_triangle_selectors.emplace_back(std::make_unique(*mesh, m_modified_extruders_colors)); - m_triangle_selectors.back()->deserialize(mv->mmu_segmentation_facets.get_data()); + m_triangle_selectors.back()->deserialize(mv->mmu_segmentation_facets.get_data(), EnforcerBlockerType(extruder_id)); } } @@ -602,8 +604,7 @@ void TriangleSelectorMmuGui::render(ImGuiWrapper *imgui) for (size_t color_idx = 0; color_idx < m_iva_colors.size(); ++color_idx) { for (const Triangle &tr : m_triangles) { - if (!tr.valid || tr.is_split() || /*tr.get_state() == EnforcerBlockerType::NONE ||*/ tr.is_selected_by_seed_fill() || - tr.get_state() != EnforcerBlockerType(color_idx)) + if (!tr.valid || tr.is_split() || tr.is_selected_by_seed_fill() || tr.get_state() != EnforcerBlockerType(color_idx)) continue; for (int i = 0; i < 3; ++i)