diff --git a/resources/icons/fuzzy_skin_painting.svg b/resources/icons/fuzzy_skin_painting.svg new file mode 100644 index 0000000000..ebbfc6d870 --- /dev/null +++ b/resources/icons/fuzzy_skin_painting.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/resources/icons/fuzzy_skin_painting_.svg b/resources/icons/fuzzy_skin_painting_.svg new file mode 100644 index 0000000000..bb956b6c8b --- /dev/null +++ b/resources/icons/fuzzy_skin_painting_.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 6cf688b3fb..3dc98acf8a 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -133,6 +133,7 @@ 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* MM_SEGMENTATION_ATTR = "slic3rpe:mmu_segmentation"; +static constexpr const char* FUZZY_SKIN_ATTR = "slic3rpe:fuzzy_skin"; static constexpr const char* KEY_ATTR = "key"; static constexpr const char* VALUE_ATTR = "value"; @@ -374,6 +375,7 @@ namespace Slic3r { std::vector custom_supports; std::vector custom_seam; std::vector mm_segmentation; + std::vector fuzzy_skin; bool empty() { return vertices.empty() || triangles.empty(); } @@ -383,6 +385,7 @@ namespace Slic3r { custom_supports.clear(); custom_seam.clear(); mm_segmentation.clear(); + fuzzy_skin.clear(); } }; @@ -2075,6 +2078,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.fuzzy_skin.push_back(get_attribute_value_string(attributes, num_attributes, FUZZY_SKIN_ATTR)); // Now load MM segmentation data. Unfortunately, BambuStudio has changed the attribute name after they forked us, // leading to https://github.com/prusa3d/PrusaSlicer/issues/12502. Let's try to load both keys if the usual @@ -2579,10 +2583,11 @@ namespace Slic3r { if (has_transform) volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object); - // recreate custom supports, seam and mm segmentation from previously loaded attribute + // recreate custom supports, seam, mm segmentation and fuzzy skin from previously loaded attribute volume->supported_facets.reserve(triangles_count); volume->seam_facets.reserve(triangles_count); volume->mm_segmentation_facets.reserve(triangles_count); + volume->fuzzy_skin_facets.reserve(triangles_count); for (size_t i=0; isupported_facets.set_triangle_from_string(i, geometry.custom_supports[index]); volume->seam_facets.set_triangle_from_string(i, geometry.custom_seam[index]); volume->mm_segmentation_facets.set_triangle_from_string(i, geometry.mm_segmentation[index]); + volume->fuzzy_skin_facets.set_triangle_from_string(i, geometry.fuzzy_skin[index]); } volume->supported_facets.shrink_to_fit(); volume->seam_facets.shrink_to_fit(); volume->mm_segmentation_facets.shrink_to_fit(); + volume->fuzzy_skin_facets.shrink_to_fit(); if (auto &es = volume_data.shape_configuration; es.has_value()) volume->emboss_shape = std::move(es); @@ -3278,6 +3285,15 @@ namespace Slic3r { output_buffer += "\""; } + std::string fuzzy_skin_data_string = volume->fuzzy_skin_facets.get_triangle_as_string(i); + if (!fuzzy_skin_data_string.empty()) { + output_buffer += " "; + output_buffer += FUZZY_SKIN_ATTR; + output_buffer += "=\""; + output_buffer += fuzzy_skin_data_string; + output_buffer += "\""; + } + output_buffer += "/>\n"; if (! flush()) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 6d0a416275..3fdfee91be 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -648,6 +648,11 @@ bool Model::is_mm_painted() const return std::any_of(this->objects.cbegin(), this->objects.cend(), [](const ModelObject *mo) { return mo->is_mm_painted(); }); } +bool Model::is_fuzzy_skin_painted() const +{ + return std::any_of(this->objects.cbegin(), this->objects.cend(), [](const ModelObject *mo) { return mo->is_fuzzy_skin_painted(); }); +} + ModelObject::~ModelObject() { this->clear_volumes(); @@ -831,6 +836,11 @@ bool ModelObject::is_mm_painted() const return std::any_of(this->volumes.cbegin(), this->volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); }); } +bool ModelObject::is_fuzzy_skin_painted() const +{ + return std::any_of(this->volumes.cbegin(), this->volumes.cend(), [](const ModelVolume *mv) { return mv->is_fuzzy_skin_painted(); }); +} + bool ModelObject::is_text() const { return this->volumes.size() == 1 && this->volumes[0]->is_text(); @@ -1248,6 +1258,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->mm_segmentation_facets.assign(volume->mm_segmentation_facets); + vol->fuzzy_skin_facets.assign(volume->fuzzy_skin_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. @@ -1360,6 +1371,7 @@ void ModelVolume::reset_extra_facets() this->supported_facets.reset(); this->seam_facets.reset(); this->mm_segmentation_facets.reset(); + this->fuzzy_skin_facets.reset(); } @@ -1926,6 +1938,7 @@ void ModelVolume::assign_new_unique_ids_recursive() supported_facets.set_new_unique_id(); seam_facets.set_new_unique_id(); mm_segmentation_facets.set_new_unique_id(); + fuzzy_skin_facets.set_new_unique_id(); } void ModelVolume::rotate(double angle, Axis axis) @@ -2273,6 +2286,13 @@ bool model_mmu_segmentation_data_changed(const ModelObject& mo, const ModelObjec [](const ModelVolume &mv_old, const ModelVolume &mv_new){ return mv_old.mm_segmentation_facets.timestamp_matches(mv_new.mm_segmentation_facets); }); } +bool model_fuzzy_skin_data_changed(const ModelObject &mo, const ModelObject &mo_new) +{ + return model_property_changed(mo, mo_new, + [](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; }, + [](const ModelVolume &mv_old, const ModelVolume &mv_new){ return mv_old.fuzzy_skin_facets.timestamp_matches(mv_new.fuzzy_skin_facets); }); +} + bool model_has_parameter_modifiers_in_objects(const Model &model) { for (const auto& model_object : model.objects) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 820617f6a9..95dc96a36f 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -393,6 +393,8 @@ public: bool is_seam_painted() const; // Checks if any of object volume is painted using the multi-material painting gizmo. bool is_mm_painted() const; + // Checks if any of object volume is painted using the fuzzy skin painting gizmo. + bool is_fuzzy_skin_painted() const; // Checks if object contains just one volume and it's a text bool is_text() const; // This object may have a varying layer height by painting or by a table. @@ -802,6 +804,9 @@ public: // List of mesh facets painted for MM segmentation. FacetsAnnotation mm_segmentation_facets; + // List of mesh facets painted for fuzzy skin. + FacetsAnnotation fuzzy_skin_facets; + // Is set only when volume is Embossed Text type // Contain information how to re-create volume std::optional text_configuration; @@ -906,11 +911,13 @@ public: this->supported_facets.set_new_unique_id(); this->seam_facets.set_new_unique_id(); this->mm_segmentation_facets.set_new_unique_id(); + this->fuzzy_skin_facets.set_new_unique_id(); } bool is_fdm_support_painted() const { return !this->supported_facets.empty(); } bool is_seam_painted() const { return !this->seam_facets.empty(); } bool is_mm_painted() const { return !this->mm_segmentation_facets.empty(); } + bool is_fuzzy_skin_painted() const { return !this->fuzzy_skin_facets.empty(); } // Returns 0-based indices of extruders painted by multi-material painting gizmo. std::vector get_extruders_from_multi_material_painting() const; @@ -958,10 +965,12 @@ private: assert(this->supported_facets.id().valid()); assert(this->seam_facets.id().valid()); assert(this->mm_segmentation_facets.id().valid()); + assert(this->fuzzy_skin_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->mm_segmentation_facets.id()); + assert(this->id() != this->fuzzy_skin_facets.id()); return true; } @@ -988,13 +997,14 @@ private: 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), mm_segmentation_facets(other.mm_segmentation_facets), - cut_info(other.cut_info), text_configuration(other.text_configuration), emboss_shape(other.emboss_shape) + fuzzy_skin_facets(other.fuzzy_skin_facets), cut_info(other.cut_info), text_configuration(other.text_configuration), emboss_shape(other.emboss_shape) { assert(this->id().valid()); assert(this->config.id().valid()); assert(this->supported_facets.id().valid()); assert(this->seam_facets.id().valid()); assert(this->mm_segmentation_facets.id().valid()); + assert(this->fuzzy_skin_facets.id().valid()); assert(this->id() != this->config.id()); assert(this->id() != this->supported_facets.id()); assert(this->id() != this->seam_facets.id()); @@ -1004,6 +1014,7 @@ private: assert(this->supported_facets.id() == other.supported_facets.id()); assert(this->seam_facets.id() == other.seam_facets.id()); assert(this->mm_segmentation_facets.id() == other.mm_segmentation_facets.id()); + assert(this->fuzzy_skin_facets.id() == other.fuzzy_skin_facets.id()); this->set_material_id(other.material_id()); } // Providing a new mesh, therefore this volume will get a new unique ID assigned. @@ -1016,10 +1027,12 @@ private: assert(this->supported_facets.id().valid()); assert(this->seam_facets.id().valid()); assert(this->mm_segmentation_facets.id().valid()); + assert(this->fuzzy_skin_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->mm_segmentation_facets.id()); + assert(this->id() != this->fuzzy_skin_facets.id()); assert(this->id() != other.id()); assert(this->config.id() == other.config.id()); this->set_material_id(other.material_id()); @@ -1031,10 +1044,12 @@ private: assert(this->supported_facets.id() != other.supported_facets.id()); assert(this->seam_facets.id() != other.seam_facets.id()); assert(this->mm_segmentation_facets.id() != other.mm_segmentation_facets.id()); + assert(this->fuzzy_skin_facets.id() != other.fuzzy_skin_facets.id()); assert(this->id() != this->config.id()); assert(this->supported_facets.empty()); assert(this->seam_facets.empty()); assert(this->mm_segmentation_facets.empty()); + assert(this->fuzzy_skin_facets.empty()); } ModelVolume& operator=(ModelVolume &rhs) = delete; @@ -1042,12 +1057,13 @@ 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), mm_segmentation_facets(-1), object(nullptr) { + ModelVolume() : ObjectBase(-1), config(-1), supported_facets(-1), seam_facets(-1), mm_segmentation_facets(-1), fuzzy_skin_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->mm_segmentation_facets.id().invalid()); + assert(this->fuzzy_skin_facets.id().invalid()); } template void load(Archive &ar) { bool has_convex_hull; @@ -1055,6 +1071,7 @@ private: cereal::load_by_value(ar, supported_facets); cereal::load_by_value(ar, seam_facets); cereal::load_by_value(ar, mm_segmentation_facets); + cereal::load_by_value(ar, fuzzy_skin_facets); cereal::load_by_value(ar, config); cereal::load(ar, text_configuration); cereal::load(ar, emboss_shape); @@ -1073,6 +1090,7 @@ private: cereal::save_by_value(ar, supported_facets); cereal::save_by_value(ar, seam_facets); cereal::save_by_value(ar, mm_segmentation_facets); + cereal::save_by_value(ar, fuzzy_skin_facets); cereal::save_by_value(ar, config); cereal::save(ar, text_configuration); cereal::save(ar, emboss_shape); @@ -1337,6 +1355,8 @@ public: bool is_seam_painted() const; // Checks if any of objects is painted using the multi-material painting gizmo. bool is_mm_painted() const; + // Checks if any of objects is painted using the fuzzy skin painting gizmo. + bool is_fuzzy_skin_painted() const; private: explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); } @@ -1381,6 +1401,10 @@ bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo // The function assumes that volumes list is synchronized. extern bool model_mmu_segmentation_data_changed(const ModelObject& mo, const ModelObject& mo_new); +// Test whether the now ModelObject has newer fuzzy skin data than the old one. +// The function assumes that volumes list is synchronized. +extern bool model_fuzzy_skin_data_changed(const ModelObject &mo, const ModelObject &mo_new); + // If the model has object(s) which contains a modofoer, then it is currently not supported by the SLA mode. // Either the model cannot be loaded, or a SLA printer has to be activated. bool model_has_parameter_modifiers_in_objects(const Model& model); diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 9a5ba3169f..d6297710db 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -105,6 +105,8 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, mv_dst.seam_facets.assign(mv_src.seam_facets); assert(mv_dst.mm_segmentation_facets.id() == mv_src.mm_segmentation_facets.id()); mv_dst.mm_segmentation_facets.assign(mv_src.mm_segmentation_facets); + assert(mv_dst.fuzzy_skin_facets.id() == mv_src.fuzzy_skin_facets.id()); + mv_dst.fuzzy_skin_facets.assign(mv_src.fuzzy_skin_facets); //FIXME what to do with the materials? // mv_dst.m_material_id = mv_src.m_material_id; ++ i_src; @@ -1196,7 +1198,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ // Only volume IDs, volume types, transformation matrices and their order are checked, configuration and other parameters are NOT checked. bool solid_or_modifier_differ = model_volume_list_changed(model_object, model_object_new, solid_or_modifier_types) || model_mmu_segmentation_data_changed(model_object, model_object_new) || - (model_object_new.is_mm_painted() && num_extruders_changed); + (model_object_new.is_mm_painted() && num_extruders_changed) || + model_fuzzy_skin_data_changed(model_object, model_object_new); bool supports_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER) || model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER); bool layer_height_ranges_differ = ! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty()); diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index 0deb6aea52..2cd5a0a980 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -37,6 +37,8 @@ enum class TriangleStateType : int8_t { NONE = 0, ENFORCER = 1, BLOCKER = 2, + // For the fuzzy skin, we use just two values (NONE and FUZZY_SKIN). + FUZZY_SKIN = ENFORCER, // Maximum is 15. The value is serialized in TriangleSelector into 6 bits using a 2 bit prefix code. Extruder1 = ENFORCER, Extruder2 = BLOCKER, diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 9604c77d66..a8c3d915f4 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -81,6 +81,8 @@ set(SLIC3R_GUI_SOURCES GUI/Gizmos/GLGizmoSlaSupports.hpp GUI/Gizmos/GLGizmoFdmSupports.cpp GUI/Gizmos/GLGizmoFdmSupports.hpp + GUI/Gizmos/GLGizmoFuzzySkin.cpp + GUI/Gizmos/GLGizmoFuzzySkin.hpp GUI/Gizmos/GLGizmoFlatten.cpp GUI/Gizmos/GLGizmoFlatten.hpp GUI/Gizmos/GLGizmoCut.cpp diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index b7777f9105..8a77950221 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1555,11 +1555,12 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject auto gizmo_type = gm.get_current_type(); if ( (gizmo_type == GLGizmosManager::FdmSupports || gizmo_type == GLGizmosManager::Seam - || gizmo_type == GLGizmosManager::Cut) + || gizmo_type == GLGizmosManager::Cut + || gizmo_type == GLGizmosManager::FuzzySkin) && !vol->is_modifier) { vol->force_neutral_color = true; } - else if (gizmo_type == GLGizmosManager::MmuSegmentation) + else if (gizmo_type == GLGizmosManager::MmSegmentation) vol->is_active = false; else vol->force_native_color = true; @@ -3149,8 +3150,9 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) const GLGizmosManager::EType gizmo_type = m_gizmos.get_current_type(); if (keyCode == WXK_ALT && (gizmo_type == GLGizmosManager::FdmSupports || gizmo_type == GLGizmosManager::Seam || - gizmo_type == GLGizmosManager::MmuSegmentation)) { - // Prevents focusing on the menu bar when ALT is pressed in painting gizmos (FdmSupports, Seam, and MmuSegmentation). + gizmo_type == GLGizmosManager::MmSegmentation || + gizmo_type == GLGizmosManager::FuzzySkin)) { + // Prevents focusing on the menu bar when ALT is pressed in painting gizmos (FdmSupports, Seam, MmSegmentation, and FuzzySkin). evt.Skip(false); } else if (keyCode != WXK_TAB && keyCode != WXK_LEFT @@ -3359,7 +3361,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; // do not return if dragging or tooltip not empty to allow for tooltip update // also, do not return if the mouse is moving and also is inside MM gizmo to allow update seed fill selection - if (!m_mouse.dragging && m_tooltip.is_empty() && (m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation || !evt.Moving())) + if (!m_mouse.dragging && m_tooltip.is_empty() && (m_gizmos.get_current_type() != GLGizmosManager::MmSegmentation || !evt.Moving())) return; } @@ -3540,7 +3542,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_gizmos.get_current_type() != GLGizmosManager::Seam && m_gizmos.get_current_type() != GLGizmosManager::Cut && m_gizmos.get_current_type() != GLGizmosManager::Measure && - m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation) { + m_gizmos.get_current_type() != GLGizmosManager::MmSegmentation && + m_gizmos.get_current_type() != GLGizmosManager::FuzzySkin) { m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::EState::Select : GLSelectionRectangle::EState::Deselect); m_dirty = true; } @@ -5797,7 +5800,8 @@ void GLCanvas3D::_render_bed(const Transform3d& view_matrix, const Transform3d& && 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::MmuSegmentation); + && m_gizmos.get_current_type() != GLGizmosManager::MmSegmentation + && m_gizmos.get_current_type() != GLGizmosManager::FuzzySkin); m_bed.render(*this, view_matrix, projection_matrix, bottom, scale_factor, show_texture); } @@ -5950,12 +5954,13 @@ void GLCanvas3D::_render_sequential_clearance() { case GLGizmosManager::EType::Flatten: case GLGizmosManager::EType::Cut: - case GLGizmosManager::EType::MmuSegmentation: + case GLGizmosManager::EType::MmSegmentation: case GLGizmosManager::EType::Measure: case GLGizmosManager::EType::Emboss: case GLGizmosManager::EType::Simplify: case GLGizmosManager::EType::FdmSupports: - case GLGizmosManager::EType::Seam: { return; } + case GLGizmosManager::EType::Seam: + case GLGizmosManager::EType::FuzzySkin: { return; } default: { break; } } diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index dc47d1bc87..c8a2106b1f 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1921,13 +1921,20 @@ void ObjectList::del_info_item(const int obj_idx, InfoItemType type) } break; - case InfoItemType::MmuSegmentation: + case InfoItemType::MmSegmentation: cnv->get_gizmos_manager().reset_all_states(); Plater::TakeSnapshot(plater, _L("Remove Multi Material painting")); for (ModelVolume* mv : (*m_objects)[obj_idx]->volumes) mv->mm_segmentation_facets.reset(); break; + case InfoItemType::FuzzySkin: + cnv->get_gizmos_manager().reset_all_states(); + Plater::TakeSnapshot(plater, _L("Remove paint-on fuzzy skin")); + for (ModelVolume* mv : (*m_objects)[obj_idx]->volumes) + mv->fuzzy_skin_facets.reset(); + break; + case InfoItemType::Sinking: Plater::TakeSnapshot(plater, _L("Shift objects to bed")); (*m_objects)[obj_idx]->ensure_on_bed(); @@ -2132,8 +2139,8 @@ void ObjectList::split() take_snapshot(_(L("Split to Parts"))); - // Before splitting volume we have to remove all custom supports, seams, and multimaterial painting. - wxGetApp().plater()->clear_before_change_mesh(obj_idx, _u8L("Custom supports, seams and multimaterial painting were " + // Before splitting volume we have to remove all custom supports, seams, fuzzy skin and multi-material painting. + wxGetApp().plater()->clear_before_change_mesh(obj_idx, _u8L("Custom supports, seams, fuzzy skin and multi-material painting were " "removed after splitting the object.")); volume->split(nozzle_dmrs_cnt); @@ -2148,8 +2155,8 @@ void ObjectList::split() // update printable state for new volumes on canvas3D wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object(obj_idx); - // After removing custom supports, seams, and multimaterial painting, we have to update info about the object to remove information about - // custom supports, seams, and multimaterial painting in the right panel. + // After removing custom supports, seams, fuzzy skin, and multi-material painting, we have to update info about the object to remove information about + // custom supports, seams, fuzzy skin, and multi-material painting in the right panel. wxGetApp().obj_list()->update_info_items(obj_idx); } @@ -2727,11 +2734,13 @@ void ObjectList::part_selection_changed() } case InfoItemType::CustomSupports: case InfoItemType::CustomSeam: - case InfoItemType::MmuSegmentation: + case InfoItemType::MmSegmentation: + case InfoItemType::FuzzySkin: { GLGizmosManager::EType gizmo_type = info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports : info_type == InfoItemType::CustomSeam ? GLGizmosManager::EType::Seam : - GLGizmosManager::EType::MmuSegmentation; + info_type == InfoItemType::FuzzySkin ? GLGizmosManager::EType::FuzzySkin : + GLGizmosManager::EType::MmSegmentation; if (gizmos_mgr.get_current_type() != gizmo_type) gizmos_mgr.open_gizmo(gizmo_type); break; @@ -2901,7 +2910,8 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio for (InfoItemType type : {InfoItemType::CustomSupports, InfoItemType::CustomSeam, InfoItemType::CutConnectors, - InfoItemType::MmuSegmentation, + InfoItemType::MmSegmentation, + InfoItemType::FuzzySkin, InfoItemType::Sinking, InfoItemType::VariableLayerHeight}) { wxDataViewItem item = m_objects_model->GetInfoItemByType(item_obj, type); @@ -2911,12 +2921,14 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio switch (type) { case InfoItemType::CustomSupports : case InfoItemType::CustomSeam : - case InfoItemType::MmuSegmentation : + case InfoItemType::MmSegmentation : + case InfoItemType::FuzzySkin : 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() : type == InfoItemType::CustomSeam ? mv->seam_facets.empty() : + type == InfoItemType::FuzzySkin ? mv->fuzzy_skin_facets.empty() : mv->mm_segmentation_facets.empty()); }); break; @@ -4642,7 +4654,7 @@ void ObjectList::fix_through_winsdk() msg += "\n"; } - plater->clear_before_change_mesh(obj_idx, _u8L("Custom supports, seams and multimaterial painting were " + plater->clear_before_change_mesh(obj_idx, _u8L("Custom supports, seams, fuzzy skin and multimaterial painting were " "removed after repairing the mesh.")); std::string res; if (!fix_model_by_win10_sdk_gui(*(object(obj_idx)), vol_idx, progress_dlg, msg, res)) @@ -4984,10 +4996,5 @@ ModelObject* ObjectList::object(const int obj_idx) const return (*m_objects)[obj_idx]; } -bool ObjectList::has_paint_on_segmentation() -{ - return m_objects_model->HasInfoItem(InfoItemType::MmuSegmentation); -} - } //namespace GUI } //namespace Slic3r diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index ece9b59430..0efd8092f6 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -410,7 +410,6 @@ public: void set_extruder_for_selected_items(const int extruder) const ; wxDataViewItemArray reorder_volumes_and_get_selection(size_t obj_idx, std::function add_to_selection = nullptr); void apply_volumes_order(); - bool has_paint_on_segmentation(); bool is_editing() const { return m_is_editing_started; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFuzzySkin.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFuzzySkin.cpp new file mode 100644 index 0000000000..e250b396c9 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoFuzzySkin.cpp @@ -0,0 +1,311 @@ +#include "GLGizmoFuzzySkin.hpp" + +#include "libslic3r/Model.hpp" +#include "libslic3r/Print.hpp" + +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/GUI_ObjectList.hpp" +#include "slic3r/GUI/ImGuiWrapper.hpp" +#include "slic3r/GUI/MsgDialog.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/Utils/UndoRedo.hpp" + +#include +#include + +namespace Slic3r::GUI { + +void GLGizmoFuzzySkin::on_shutdown() +{ + m_parent.use_slope(false); + m_parent.toggle_model_objects_visibility(true); +} + +std::string GLGizmoFuzzySkin::on_get_name() const +{ + return _u8L("Paint-on fuzzy skin"); +} + +bool GLGizmoFuzzySkin::on_init() +{ + m_shortcut_key = WXK_CONTROL_H; + + m_desc["clipping_of_view"] = _u8L("Clipping of view") + ": "; + m_desc["reset_direction"] = _u8L("Reset direction"); + m_desc["cursor_size"] = _u8L("Brush size") + ": "; + m_desc["cursor_type"] = _u8L("Brush shape") + ": "; + m_desc["add_fuzzy_skin_caption"] = _u8L("Left mouse button") + ": "; + m_desc["add_fuzzy_skin"] = _u8L("Add fuzzy skin"); + m_desc["remove_fuzzy_skin_caption"] = _u8L("Shift + Left mouse button") + ": "; + m_desc["remove_fuzzy_skin"] = _u8L("Remove fuzzy skin"); + m_desc["remove_all"] = _u8L("Remove all selection"); + m_desc["circle"] = _u8L("Circle"); + m_desc["sphere"] = _u8L("Sphere"); + m_desc["pointer"] = _u8L("Triangles"); + m_desc["tool_type"] = _u8L("Tool type") + ": "; + m_desc["tool_brush"] = _u8L("Brush"); + m_desc["tool_smart_fill"] = _u8L("Smart fill"); + m_desc["smart_fill_angle"] = _u8L("Smart fill angle"); + m_desc["split_triangles"] = _u8L("Split triangles"); + + return true; +} + +void GLGizmoFuzzySkin::render_painter_gizmo() +{ + 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(); + m_c->instances_hider()->render_cut(); + render_cursor(); + + glsafe(::glDisable(GL_BLEND)); +} + +void GLGizmoFuzzySkin::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(22.f); + + y = std::min(y, bottom_limit - approx_height); + ImGuiPureWrap::set_next_window_pos(x, y, ImGuiCond_Always); + + ImGuiPureWrap::begin(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(ImGuiPureWrap::calc_text_size(m_desc.at("clipping_of_view")).x, + ImGuiPureWrap::calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); + const float cursor_slider_left = ImGuiPureWrap::calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f); + const float smart_fill_slider_left = ImGuiPureWrap::calc_text_size(m_desc.at("smart_fill_angle")).x + m_imgui->scaled(1.f); + + const float cursor_type_radio_circle = ImGuiPureWrap::calc_text_size(m_desc["circle"]).x + m_imgui->scaled(2.5f); + const float cursor_type_radio_sphere = ImGuiPureWrap::calc_text_size(m_desc["sphere"]).x + m_imgui->scaled(2.5f); + const float cursor_type_radio_pointer = ImGuiPureWrap::calc_text_size(m_desc["pointer"]).x + m_imgui->scaled(2.5f); + + const float button_width = ImGuiPureWrap::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 tool_type_radio_left = ImGuiPureWrap::calc_text_size(m_desc["tool_type"]).x + m_imgui->scaled(1.f); + const float tool_type_radio_brush = ImGuiPureWrap::calc_text_size(m_desc["tool_brush"]).x + m_imgui->scaled(2.5f); + const float tool_type_radio_smart_fill = ImGuiPureWrap::calc_text_size(m_desc["tool_smart_fill"]).x + m_imgui->scaled(2.5f); + + const float split_triangles_checkbox_width = ImGuiPureWrap::calc_text_size(m_desc["split_triangles"]).x + m_imgui->scaled(2.5f); + + float caption_max = 0.f; + float total_text_max = 0.f; + for (const std::string t : {"add_fuzzy_skin", "remove_fuzzy_skin"}) { + caption_max = std::max(caption_max, ImGuiPureWrap::calc_text_size(m_desc[t + "_caption"]).x); + total_text_max = std::max(total_text_max, ImGuiPureWrap::calc_text_size(m_desc[t]).x); + } + + total_text_max += caption_max + m_imgui->scaled(1.f); + caption_max += m_imgui->scaled(1.f); + + const float sliders_left_width = std::max(smart_fill_slider_left, std::max(cursor_slider_left, clipping_slider_left)); + const float slider_icon_width = ImGuiPureWrap::get_slider_icon_size().x; + float window_width = minimal_slider_width + sliders_left_width + slider_icon_width; + window_width = std::max(window_width, total_text_max); + window_width = std::max(window_width, button_width); + window_width = std::max(window_width, split_triangles_checkbox_width); + window_width = std::max(window_width, cursor_type_radio_circle + cursor_type_radio_sphere + cursor_type_radio_pointer); + window_width = std::max(window_width, tool_type_radio_left + tool_type_radio_brush + tool_type_radio_smart_fill); + window_width = std::max(window_width, 2.f * buttons_width + m_imgui->scaled(1.f)); + + auto draw_text_with_caption = [&caption_max](const std::string &caption, const std::string &text) { + ImGuiPureWrap::text_colored(ImGuiPureWrap::COL_ORANGE_LIGHT, caption); + ImGui::SameLine(caption_max); + ImGuiPureWrap::text(text); + }; + + for (const std::string t : {"add_fuzzy_skin", "remove_fuzzy_skin"}) { + draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t)); + } + + ImGui::Separator(); + + std::string format_str = std::string("%.f") + I18N::translate_utf8("°", + "Degree sign to use in the respective slider in fuzzy skin gizmo," + "placed after the number with no whitespace in between."); + + const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; + + ImGui::AlignTextToFramePadding(); + ImGuiPureWrap::text(m_desc["tool_type"]); + + float tool_type_offset = tool_type_radio_left + (window_width - tool_type_radio_left - tool_type_radio_brush - tool_type_radio_smart_fill + m_imgui->scaled(0.5f)) / 2.f; + ImGui::SameLine(tool_type_offset); + ImGui::PushItemWidth(tool_type_radio_brush); + if (ImGuiPureWrap::radio_button(m_desc["tool_brush"], m_tool_type == ToolType::BRUSH)) + m_tool_type = ToolType::BRUSH; + + if (ImGui::IsItemHovered()) + ImGuiPureWrap::tooltip(_u8L("Paints facets according to the chosen painting brush."), max_tooltip_width); + + ImGui::SameLine(tool_type_offset + tool_type_radio_brush); + ImGui::PushItemWidth(tool_type_radio_smart_fill); + if (ImGuiPureWrap::radio_button(m_desc["tool_smart_fill"], m_tool_type == ToolType::SMART_FILL)) + m_tool_type = ToolType::SMART_FILL; + + if (ImGui::IsItemHovered()) + ImGuiPureWrap::tooltip(_u8L("Paints neighboring facets whose relative angle is less or equal to set angle."), max_tooltip_width); + + ImGui::Separator(); + + if (m_tool_type == ToolType::BRUSH) { + ImGuiPureWrap::text(m_desc.at("cursor_type")); + ImGui::NewLine(); + + float cursor_type_offset = (window_width - cursor_type_radio_sphere - cursor_type_radio_circle - cursor_type_radio_pointer + m_imgui->scaled(1.5f)) / 2.f; + ImGui::SameLine(cursor_type_offset); + ImGui::PushItemWidth(cursor_type_radio_sphere); + if (ImGuiPureWrap::radio_button(m_desc["sphere"], m_cursor_type == TriangleSelector::CursorType::SPHERE)) + m_cursor_type = TriangleSelector::CursorType::SPHERE; + + if (ImGui::IsItemHovered()) + ImGuiPureWrap::tooltip(_u8L("Paints all facets inside, regardless of their orientation."), max_tooltip_width); + + ImGui::SameLine(cursor_type_offset + cursor_type_radio_sphere); + ImGui::PushItemWidth(cursor_type_radio_circle); + + if (ImGuiPureWrap::radio_button(m_desc["circle"], m_cursor_type == TriangleSelector::CursorType::CIRCLE)) + m_cursor_type = TriangleSelector::CursorType::CIRCLE; + + if (ImGui::IsItemHovered()) + ImGuiPureWrap::tooltip(_u8L("Ignores facets facing away from the camera."), max_tooltip_width); + + ImGui::SameLine(cursor_type_offset + cursor_type_radio_sphere + cursor_type_radio_circle); + ImGui::PushItemWidth(cursor_type_radio_pointer); + + if (ImGuiPureWrap::radio_button(m_desc["pointer"], m_cursor_type == TriangleSelector::CursorType::POINTER)) + m_cursor_type = TriangleSelector::CursorType::POINTER; + + if (ImGui::IsItemHovered()) + ImGuiPureWrap::tooltip(_u8L("Paints only one facet."), max_tooltip_width); + + m_imgui->disabled_begin(m_cursor_type != TriangleSelector::CursorType::SPHERE && m_cursor_type != TriangleSelector::CursorType::CIRCLE); + + ImGui::AlignTextToFramePadding(); + ImGuiPureWrap::text(m_desc.at("cursor_size")); + ImGui::SameLine(sliders_left_width); + ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width); + m_imgui->slider_float("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f", 1.0f, true, _L("Alt + Mouse wheel")); + + ImGuiPureWrap::checkbox(m_desc["split_triangles"], m_triangle_splitting_enabled); + + if (ImGui::IsItemHovered()) + ImGuiPureWrap::tooltip(_u8L("Splits bigger facets into smaller ones while the object is painted."), max_tooltip_width); + + m_imgui->disabled_end(); + } else { + assert(m_tool_type == ToolType::SMART_FILL); + ImGui::AlignTextToFramePadding(); + ImGuiPureWrap::text(m_desc["smart_fill_angle"] + ":"); + + ImGui::SameLine(sliders_left_width); + ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width); + if (m_imgui->slider_float("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, format_str.data(), 1.0f, true, _L("Alt + Mouse wheel"))) + for (auto &triangle_selector : m_triangle_selectors) { + triangle_selector->seed_fill_unselect_all_triangles(); + triangle_selector->request_update_render_data(); + } + } + + ImGui::Separator(); + if (m_c->object_clipper()->get_position() == 0.f) { + ImGui::AlignTextToFramePadding(); + ImGuiPureWrap::text(m_desc.at("clipping_of_view")); + } else { + if (ImGuiPureWrap::button(m_desc.at("reset_direction"))) { + wxGetApp().CallAfter([this]() { m_c->object_clipper()->set_position_by_ratio(-1., false); }); + } + } + + auto clp_dist = float(m_c->object_clipper()->get_position()); + ImGui::SameLine(sliders_left_width); + ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width); + if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true, from_u8(GUI::shortkey_ctrl_prefix()) + _L("Mouse wheel"))) + m_c->object_clipper()->set_position_by_ratio(clp_dist, true); + + ImGui::Separator(); + if (ImGuiPureWrap::button(m_desc.at("remove_all"))) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Reset selection"), UndoRedo::SnapshotType::GizmoAction); + 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(); + m_triangle_selectors[idx]->request_update_render_data(); + } + + update_model_object(); + m_parent.set_as_dirty(); + } + + ImGuiPureWrap::end(); +} + +void GLGizmoFuzzySkin::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->fuzzy_skin_facets.set(*m_triangle_selectors[idx]); + } + + 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)); + } +} + +void GLGizmoFuzzySkin::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)); + // Reset of TriangleSelector is done inside TriangleSelectorGUI's constructor, so we don't need it to perform it again in deserialize(). + m_triangle_selectors.back()->deserialize(mv->fuzzy_skin_facets.get_data(), false); + m_triangle_selectors.back()->request_update_render_data(); + } +} + +PainterGizmoType GLGizmoFuzzySkin::get_painter_type() const +{ + return PainterGizmoType::FUZZY_SKIN; +} + +wxString GLGizmoFuzzySkin::handle_snapshot_action_name(bool shift_down, GLGizmoPainterBase::Button button_down) const +{ + return shift_down ? _L("Remove fuzzy skin") : _L("Add fuzzy skin"); +} + +} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFuzzySkin.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFuzzySkin.hpp new file mode 100644 index 0000000000..899e75c295 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoFuzzySkin.hpp @@ -0,0 +1,47 @@ +#ifndef slic3r_GLGizmoFuzzySkin_hpp_ +#define slic3r_GLGizmoFuzzySkin_hpp_ + +#include "GLGizmoPainterBase.hpp" + +#include "slic3r/GUI/I18N.hpp" + +namespace Slic3r::GUI { + +class GLGizmoFuzzySkin : public GLGizmoPainterBase +{ +public: + GLGizmoFuzzySkin(GLCanvas3D &parent, const std::string &icon_filename, unsigned int sprite_id) : GLGizmoPainterBase(parent, icon_filename, sprite_id) {} + + void render_painter_gizmo() override; + +protected: + void on_render_input_window(float x, float y, float bottom_limit) override; + std::string on_get_name() const override; + + wxString handle_snapshot_action_name(bool shift_down, Button button_down) const override; + + std::string get_gizmo_entering_text() const override { return _u8L("Entering Paint-on fuzzy skin"); } + std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Paint-on fuzzy skin"); } + std::string get_action_snapshot_name() const override { return _u8L("Paint-on fuzzy skin editing"); } + + TriangleStateType get_left_button_state_type() const override { return TriangleStateType::FUZZY_SKIN; } + TriangleStateType get_right_button_state_type() const override { return TriangleStateType::NONE; } + +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; + + // 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 Slic3r::GUI + +#endif // slic3r_GLGizmoFuzzySkin_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index 62f58c757b..c39eb4edc1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -560,7 +560,7 @@ void GLGizmoMmuSegmentation::update_from_model_object() PainterGizmoType GLGizmoMmuSegmentation::get_painter_type() const { - return PainterGizmoType::MMU_SEGMENTATION; + return PainterGizmoType::MM_SEGMENTATION; } ColorRGBA GLGizmoMmuSegmentation::get_cursor_sphere_left_button_color() const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index bd185fce0e..31f38cc743 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -31,7 +31,8 @@ class Selection; enum class PainterGizmoType { FDM_SUPPORTS, SEAM, - MMU_SEGMENTATION + MM_SEGMENTATION, + FUZZY_SKIN }; class TriangleSelectorGUI : public TriangleSelector { diff --git a/src/slic3r/GUI/Gizmos/GLGizmos.hpp b/src/slic3r/GUI/Gizmos/GLGizmos.hpp index 7b57590a1e..c51d73a2e8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmos.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmos.hpp @@ -36,6 +36,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/GLGizmoFuzzySkin.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 8fea76a79c..bce5114591 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -21,6 +21,7 @@ #include "slic3r/GUI/Gizmos/GLGizmoFlatten.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp" #include "slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoFuzzySkin.hpp" #include "slic3r/GUI/Gizmos/GLGizmoCut.hpp" #include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp" @@ -113,8 +114,9 @@ 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, "mmu_segmentation.svg", 9)); - m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, "measure.svg", 10)); + m_gizmos.emplace_back(new GLGizmoFuzzySkin(m_parent, "fuzzy_skin_painting.svg", 9)); + m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "mmu_segmentation.svg", 10)); + m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, "measure.svg", 11)); m_gizmos.emplace_back(new GLGizmoEmboss(m_parent)); m_gizmos.emplace_back(new GLGizmoSVG(m_parent)); m_gizmos.emplace_back(new GLGizmoSimplify(m_parent)); @@ -294,12 +296,14 @@ 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 if (m_current == MmSegmentation) + return dynamic_cast(m_gizmos[MmSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == Measure) return dynamic_cast(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == Cut) return dynamic_cast(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + else if (m_current == FuzzySkin) + return dynamic_cast(m_gizmos[FuzzySkin].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else return false; } @@ -367,7 +371,7 @@ bool GLGizmosManager::on_mouse_wheel(const wxMouseEvent &evt) { bool processed = false; - if (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) { + if (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmSegmentation || m_current == FuzzySkin) { 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; @@ -540,7 +544,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) case 'r' : case 'R' : { - if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && gizmo_event(SLAGizmoEventType::ResetClippingPlane)) + if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmSegmentation || m_current == FuzzySkin) && gizmo_event(SLAGizmoEventType::ResetClippingPlane)) processed = true; break; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 6e55ce72c2..97d86270dc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -80,7 +80,8 @@ public: SlaSupports, FdmSupports, Seam, - MmuSegmentation, + FuzzySkin, + MmSegmentation, Measure, Emboss, Svg, diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index e425016cd6..69d2d29549 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -164,6 +164,7 @@ void KBShortcutsDialog::fill_shortcuts() { "C", L("Gizmo cut") }, { "F", L("Gizmo Place face on bed") }, { "H", L("Gizmo SLA hollow") }, + { "H", L("Gizmo FDM paint-on fuzzy skin") }, { "L", L("Gizmo SLA support points") }, { "L", L("Gizmo FDM paint-on supports") }, { "P", L("Gizmo FDM paint-on seam") }, diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index c9ed154e09..99021fa8ab 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1722,10 +1722,11 @@ void NotificationManager::UpdatedItemsInfoNotification::add_type(InfoItemType ty switch ((*it).first) { case InfoItemType::CustomSupports: text += format(_L_PLURAL("%1$d object was loaded with custom supports.", "%1$d objects were loaded with custom supports.", (*it).second), (*it).second) + "\n"; break; case InfoItemType::CustomSeam: text += format(_L_PLURAL("%1$d object was loaded with custom seam.", "%1$d objects were loaded with custom seam.", (*it).second), (*it).second) + "\n"; break; - case InfoItemType::MmuSegmentation: text += format(_L_PLURAL("%1$d object was loaded with multimaterial painting.", "%1$d objects were loaded with multimaterial painting.",(*it).second), (*it).second) + "\n"; break; + case InfoItemType::MmSegmentation: text += format(_L_PLURAL("%1$d object was loaded with multimaterial painting.", "%1$d objects were loaded with multimaterial painting.",(*it).second), (*it).second) + "\n"; break; case InfoItemType::VariableLayerHeight: text += format(_L_PLURAL("%1$d object was loaded with variable layer height.", "%1$d objects were loaded with variable layer height.", (*it).second), (*it).second) + "\n"; break; case InfoItemType::Sinking: text += format(_L_PLURAL("%1$d object was loaded with partial sinking.", "%1$d objects were loaded with partial sinking.", (*it).second), (*it).second) + "\n"; break; case InfoItemType::CutConnectors: text += format(_L_PLURAL("%1$d object was loaded as a part of cut object.", "%1$d objects were loaded as parts of cut object", (*it).second), (*it).second) + "\n"; break; + case InfoItemType::FuzzySkin: text += format(_L_PLURAL("%1$d object was loaded with fuzzy skin painting.", "%1$d objects were loaded with fuzzy skin painting.", (*it).second), (*it).second) + "\n"; break; default: BOOST_LOG_TRIVIAL(error) << "Unknown InfoItemType: " << (*it).second; break; } } diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index babbbc0d7c..4def2c650c 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -68,12 +68,13 @@ struct InfoItemAtributes { const std::map INFO_ITEMS{ // info_item Type info_item Name info_item BitmapName - { InfoItemType::CustomSupports, {L("Paint-on supports"), "fdm_supports_" }, }, - { InfoItemType::CustomSeam, {L("Paint-on seam"), "seam_" }, }, - { InfoItemType::CutConnectors, {L("Connectors"), "cut_connectors" }, }, - { InfoItemType::MmuSegmentation, {L("Multimaterial painting"), "mmu_segmentation_"}, }, - { InfoItemType::Sinking, {L("Sinking"), "sinking"}, }, - { InfoItemType::VariableLayerHeight, {L("Variable layer height"), "layers"}, }, + { InfoItemType::CustomSupports, {L("Paint-on supports"), "fdm_supports_" }, }, + { InfoItemType::CustomSeam, {L("Paint-on seam"), "seam_" }, }, + { InfoItemType::CutConnectors, {L("Connectors"), "cut_connectors" }, }, + { InfoItemType::MmSegmentation, {L("Multimaterial painting"), "mmu_segmentation_" }, }, + { InfoItemType::Sinking, {L("Sinking"), "sinking" }, }, + { InfoItemType::VariableLayerHeight, {L("Variable layer height"), "layers" }, }, + { InfoItemType::FuzzySkin, {L("Paint-on fuzzy skin"), "fuzzy_skin_painting_" }, }, }; ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index b366abe51e..26cd045c9c 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -56,7 +56,8 @@ enum class InfoItemType CustomSupports, CustomSeam, CutConnectors, - MmuSegmentation, + MmSegmentation, + FuzzySkin, Sinking, VariableLayerHeight }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e22d7a260a..f66d8dfc9b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2438,6 +2438,7 @@ bool Plater::priv::replace_volume_with_stl(int object_idx, int volume_idx, const new_volume->supported_facets.assign(old_volume->supported_facets); new_volume->seam_facets.assign(old_volume->seam_facets); new_volume->mm_segmentation_facets.assign(old_volume->mm_segmentation_facets); + new_volume->fuzzy_skin_facets.assign(old_volume->fuzzy_skin_facets); } std::swap(old_model_object->volumes[volume_idx], old_model_object->volumes.back()); old_model_object->delete_volume(old_model_object->volumes.size() - 1); @@ -6686,11 +6687,12 @@ bool Plater::set_printer_technology(PrinterTechnology printer_technology) } void Plater::clear_before_change_volume(ModelVolume &mv, const std::string ¬ification_msg) { - // When we change the geometry of the volume, we remove any custom supports/seams/multi-material painting. - if (const bool paint_removed = !mv.supported_facets.empty() || !mv.seam_facets.empty() || !mv.mm_segmentation_facets.empty(); paint_removed) { + // When we change the geometry of the volume, we remove any custom supports/seams/multi-material/fuzzy skin painting. + if (const bool paint_removed = !mv.supported_facets.empty() || !mv.seam_facets.empty() || !mv.mm_segmentation_facets.empty() || !mv.fuzzy_skin_facets.empty(); paint_removed) { mv.supported_facets.reset(); mv.seam_facets.reset(); mv.mm_segmentation_facets.reset(); + mv.fuzzy_skin_facets.reset(); get_notification_manager()->push_notification( NotificationType::CustomSupportsAndSeamRemovedAfterRepair, @@ -6703,14 +6705,15 @@ void Plater::clear_before_change_mesh(int obj_idx, const std::string ¬ificati { ModelObject* mo = model().objects[obj_idx]; - // If there are custom supports/seams/mmu segmentation, remove them. Fixed mesh + // If there are custom supports/seams/mm segmentation/fuzzy skin, 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() || ! mv->mm_segmentation_facets.empty(); + for (ModelVolume *mv : mo->volumes) { + paint_removed |= !mv->supported_facets.empty() || !mv->seam_facets.empty() || !mv->mm_segmentation_facets.empty() || !mv->fuzzy_skin_facets.empty(); mv->supported_facets.reset(); mv->seam_facets.reset(); mv->mm_segmentation_facets.reset(); + mv->fuzzy_skin_facets.reset(); } if (paint_removed) { // snapshot_time is captured by copy so the lambda knows where to undo/redo to.