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.