diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 59df8eb61..56c1f8b7e 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1149,6 +1149,8 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b for (ModelVolume *volume : volumes) { const auto volume_matrix = volume->get_matrix(); + volume->m_supported_facets.clear(); + if (! volume->is_model_part()) { // Modifiers are not cut, but we still need to add the instance transformation // to the modifier volume transformation to preserve their shape properly. @@ -1848,6 +1850,41 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const return ret; } + +std::vector FacetsAnnotation::get_facets(FacetSupportType type) const +{ + std::vector out; + for (auto& [facet_idx, this_type] : m_data) + if (this_type == type) + out.push_back(facet_idx); + return out; +} + + + +void FacetsAnnotation::set_facet(int idx, FacetSupportType type) +{ + bool changed = true; + + if (type == FacetSupportType::NONE) + changed = m_data.erase(idx) != 0; + else + m_data[idx] = type; + + if (changed) + update_timestamp(); +} + + + +void FacetsAnnotation::clear() +{ + m_data.clear(); + update_timestamp(); +} + + + // Test whether the two models contain the same number of ModelObjects with the same set of IDs // ordered in the same order. In that case it is not necessary to kill the background processing. bool model_object_list_equal(const Model &model_old, const Model &model_new) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 2ddad9e59..33fc8a622 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -19,6 +19,7 @@ #include #include #include +#include namespace cereal { class BinaryInputArchive; @@ -391,6 +392,35 @@ enum class ModelVolumeType : int { SUPPORT_BLOCKER, }; +enum class FacetSupportType : int8_t { + NONE = 0, + ENFORCER = 1, + BLOCKER = 2 +}; + +class FacetsAnnotation { +public: + using ClockType = std::chrono::steady_clock; + + + std::vector get_facets(FacetSupportType type) const; + void set_facet(int idx, FacetSupportType type); + void clear(); + + ClockType::time_point get_timestamp() const { return timestamp; } + bool is_newer_than(const FacetsAnnotation& other) const { + return timestamp > other.get_timestamp(); + } + +private: + std::map m_data; + + ClockType::time_point timestamp; + void update_timestamp() { + timestamp = ClockType::now(); + } +}; + // An object STL, or a modifier volume, over which a different set of parameters shall be applied. // ModelVolume instances are owned by a ModelObject. class ModelVolume final : public ObjectBase @@ -421,6 +451,9 @@ public: // overriding the global Slic3r settings and the ModelObject settings. ModelConfig config; + // List of mesh facets to be supported/unsupported. + FacetsAnnotation m_supported_facets; + // A parent object owning this modifier volume. ModelObject* get_object() const { return this->object; }; ModelVolumeType type() const { return m_type; } @@ -548,7 +581,9 @@ private: // Copying an existing volume, therefore this volume will get a copy of the ID assigned. ModelVolume(ModelObject *object, const ModelVolume &other) : ObjectBase(other), - name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) + 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), + m_supported_facets(other.m_supported_facets) { assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); assert(this->id() == other.id() && this->config.id() == other.config.id()); @@ -565,6 +600,8 @@ private: if (mesh.stl.stats.number_of_facets > 1) calculate_convex_hull(); assert(this->config.id().valid()); assert(this->config.id() != other.config.id()); assert(this->id() != this->config.id()); + + m_supported_facets.clear(); } ModelVolume& operator=(ModelVolume &rhs) = delete; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 8631db624..facc5f521 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -404,6 +404,7 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, // Copy the ModelVolume data. mv_dst.name = mv_src.name; static_cast(mv_dst.config) = static_cast(mv_src.config); + mv_dst.m_supported_facets = mv_src.m_supported_facets; //FIXME what to do with the materials? // mv_dst.m_material_id = mv_src.m_material_id; ++ i_src; @@ -881,7 +882,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ } } } - // Synchronize (just copy) the remaining data of ModelVolumes (name, config). + // Synchronize (just copy) the remaining data of ModelVolumes (name, config, custom supports data). //FIXME What to do with m_material_id? model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART); model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 7086f3c8d..085986c46 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -8,6 +8,7 @@ #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/PresetBundle.hpp" #include "slic3r/GUI/Camera.hpp" +#include "libslic3r/Model.hpp" @@ -187,7 +188,16 @@ void GLGizmoFdmSupports::update_mesh() // This mesh does not account for the possible Z up SLA offset. const TriangleMesh* mesh = &mv->mesh(); - m_selected_facets[volume_id].assign(mesh->its.indices.size(), SelType::NONE); + m_selected_facets[volume_id].assign(mesh->its.indices.size(), FacetSupportType::NONE); + + // Load current state from ModelVolume. + for (FacetSupportType type : {FacetSupportType::ENFORCER, FacetSupportType::BLOCKER}) { + const std::vector& list = mv->m_supported_facets.get_facets(type); + for (int i : list) + m_selected_facets[volume_id][i] = type; + } + update_vertex_buffers(mv, volume_id, true, true); + m_neighbors[volume_id].resize(3 * mesh->its.indices.size()); // Prepare vector of vertex_index - facet_index pairs to quickly find adjacent facets @@ -243,16 +253,16 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous || action == SLAGizmoEventType::RightDown || (action == SLAGizmoEventType::Dragging && m_button_down != Button::None)) { - SelType new_state = SelType::NONE; + FacetSupportType new_state = FacetSupportType::NONE; if (! shift_down) { if (action == SLAGizmoEventType::Dragging) new_state = m_button_down == Button::Left - ? SelType::ENFORCER - : SelType::BLOCKER; + ? FacetSupportType::ENFORCER + : FacetSupportType::BLOCKER; else new_state = action == SLAGizmoEventType::LeftDown - ? SelType::ENFORCER - : SelType::BLOCKER; + ? FacetSupportType::ENFORCER + : FacetSupportType::BLOCKER; } const Camera& camera = wxGetApp().plater()->get_camera(); @@ -379,9 +389,9 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous // Now just select all facets that passed. for (size_t next_facet : facets_to_select) { - SelType& facet = m_selected_facets[mesh_id][next_facet]; + FacetSupportType& facet = m_selected_facets[mesh_id][next_facet]; - if (facet != new_state && facet != SelType::NONE) { + if (facet != new_state && facet != FacetSupportType::NONE) { // this triangle is currently in the other VBA. // Both VBAs need to be refreshed. update_both = true; @@ -391,8 +401,8 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous } update_vertex_buffers(mv, mesh_id, - new_state == SelType::ENFORCER || update_both, - new_state == SelType::BLOCKER || update_both + new_state == FacetSupportType::ENFORCER || update_both, + new_state == FacetSupportType::BLOCKER || update_both ); } @@ -416,6 +426,18 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::RightUp) && m_button_down != Button::None) { m_button_down = Button::None; + + // Synchronize gizmo with ModelVolume data. + ModelObject* mo = m_c->selection_info()->model_object(); + int idx = -1; + for (ModelVolume* mv : mo->volumes) { + ++idx; + if (! mv->is_model_part()) + continue; + for (int i=0; im_supported_facets.set_facet(i, m_selected_facets[idx][i]); + } + return true; } @@ -430,16 +452,16 @@ void GLGizmoFdmSupports::update_vertex_buffers(const ModelVolume* mv, { const TriangleMesh* mesh = &mv->mesh(); - for (SelType type : {SelType::ENFORCER, SelType::BLOCKER}) { - if ((type == SelType::ENFORCER && ! update_enforcers) - || (type == SelType::BLOCKER && ! update_blockers)) + for (FacetSupportType type : {FacetSupportType::ENFORCER, FacetSupportType::BLOCKER}) { + if ((type == FacetSupportType::ENFORCER && ! update_enforcers) + || (type == FacetSupportType::BLOCKER && ! update_blockers)) continue; - GLIndexedVertexArray& iva = m_ivas[mesh_id][type==SelType::ENFORCER ? 0 : 1]; + GLIndexedVertexArray& iva = m_ivas[mesh_id][type==FacetSupportType::ENFORCER ? 0 : 1]; iva.release_geometry(); size_t triangle_cnt=0; for (size_t facet_idx=0; facet_idx> m_selected_facets; + std::vector> m_selected_facets; // Store two vertex buffer arrays (for enforcers/blockers) // for each model-part volume.