diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 597152c06..54d4fcab7 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1259,11 +1259,9 @@ void ModelObject::apply_cut_connectors(const std::string& new_name) void ModelObject::invalidate_cut() { - for (ModelObject* obj : m_model->objects) - if (obj != this && obj->cut_id.is_equal(this->cut_id)) - obj->cut_id.invalidate(); - // invalidate own cut_id this->cut_id.invalidate(); + for (ModelVolume* volume : this->volumes) + volume->invalidate_cut_info(); } void ModelObject::synchronize_model_after_cut() diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 0d54ebc1f..ee611b006 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -444,7 +444,7 @@ public: size_t parts_count() const; static indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes); void apply_cut_connectors(const std::string& name); - // invalidate cut state for this and related objects from the whole model + // invalidate cut state for this object and its connectors/volumes void invalidate_cut(); void synchronize_model_after_cut(); void apply_cut_attributes(ModelObjectCutAttributes attributes); @@ -742,10 +742,16 @@ public: {} void set_processed() { is_processed = true; } + void invalidate() { is_connector = false; } + + template inline void serialize(Archive& ar) { + ar(is_connector, is_processed, connector_type, radius_tolerance, height_tolerance); + } }; CutInfo cut_info; bool is_cut_connector() const { return cut_info.is_processed && cut_info.is_connector; } + void invalidate_cut_info() { cut_info.invalidate(); } // The triangular model. const TriangleMesh& mesh() const { return *m_mesh.get(); } @@ -954,7 +960,8 @@ private: ObjectBase(other), name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation), - supported_facets(other.supported_facets), seam_facets(other.seam_facets), mmu_segmentation_facets(other.mmu_segmentation_facets) + supported_facets(other.supported_facets), seam_facets(other.seam_facets), mmu_segmentation_facets(other.mmu_segmentation_facets), + cut_info(other.cut_info) { assert(this->id().valid()); assert(this->config.id().valid()); @@ -974,7 +981,8 @@ private: } // Providing a new mesh, therefore this volume will get a new unique ID assigned. ModelVolume(ModelObject *object, const ModelVolume &other, TriangleMesh &&mesh) : - name(other.name), source(other.source), config(other.config), object(object), m_mesh(new TriangleMesh(std::move(mesh))), m_type(other.m_type), m_transformation(other.m_transformation) + name(other.name), source(other.source), config(other.config), object(object), m_mesh(new TriangleMesh(std::move(mesh))), m_type(other.m_type), m_transformation(other.m_transformation), + cut_info(other.cut_info) { assert(this->id().valid()); assert(this->config.id().valid()); @@ -1016,7 +1024,7 @@ private: } template void load(Archive &ar) { bool has_convex_hull; - ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull, cut_info); cereal::load_by_value(ar, supported_facets); cereal::load_by_value(ar, seam_facets); cereal::load_by_value(ar, mmu_segmentation_facets); @@ -1032,7 +1040,7 @@ private: } template void save(Archive &ar) const { bool has_convex_hull = m_convex_hull.get() != nullptr; - ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull, cut_info); cereal::save_by_value(ar, supported_facets); cereal::save_by_value(ar, seam_facets); cereal::save_by_value(ar, mmu_segmentation_facets); diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp index f907b03e1..6be19c88f 100644 --- a/src/libslic3r/ObjectID.hpp +++ b/src/libslic3r/ObjectID.hpp @@ -156,7 +156,7 @@ public: this->m_check_sum = rhs.check_sum(); this->m_connectors_cnt = rhs.connectors_cnt() ; } - CutObjectBase operator=(const CutObjectBase& other) { + CutObjectBase& operator=(const CutObjectBase& other) { this->copy(other); return *this; } @@ -179,6 +179,13 @@ public: size_t connectors_cnt() const { return m_connectors_cnt; } void increase_connectors_cnt(size_t connectors_cnt) { m_connectors_cnt += connectors_cnt; } + +private: + friend class cereal::access; + template void serialize(Archive& ar) { + ar(cereal::base_class(this)); + ar(m_check_sum, m_connectors_cnt); + } }; // Unique object / instance ID for the wipe tower. diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index cced147f8..663290a8b 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2458,19 +2458,37 @@ bool ObjectList::has_selected_cut_object() const return false; } - void ObjectList::invalidate_cut_info_for_selection() { - wxDataViewItemArray sels; - GetSelections(sels); - if (sels.IsEmpty()) + const wxDataViewItem item = GetSelection(); + if (item) { + const int obj_idx = m_objects_model->GetObjectIdByItem(item); + if (obj_idx >= 0) + invalidate_cut_info_for_object(size_t(obj_idx)); + } +} + +void ObjectList::invalidate_cut_info_for_object(size_t obj_idx) +{ + ModelObject* init_obj = object(int(obj_idx)); + if (!init_obj->is_cut()) return; - for (wxDataViewItem item : sels) { - const int obj_idx = m_objects_model->GetObjectIdByItem(item); - if (obj_idx >= 0 && object(obj_idx)->is_cut()) - object(obj_idx)->invalidate_cut(); - } + take_snapshot(_L("Invalidate cut info")); + + auto invalidate_cut = [this](size_t obj_idx) { + object(int(obj_idx))->invalidate_cut(); + update_info_items(obj_idx); + add_volumes_to_object_in_list(obj_idx); + }; + + // invalidate cut for related objects (which have the same cut_id) + for (size_t idx = 0; idx < m_objects->size(); idx++) + if (ModelObject* obj = object(idx); obj != init_obj && obj->cut_id.is_equal(init_obj->cut_id)) + invalidate_cut(idx); + + // invalidate own cut information + invalidate_cut(size_t(obj_idx)); update_lock_icons_for_model(); } @@ -2970,8 +2988,6 @@ void ObjectList::update_lock_icons_for_model() bool ObjectList::delete_from_model_and_list(const ItemType type, const int obj_idx, const int sub_obj_idx) { -// take_snapshot(_(L("Delete Selected Item"))); // #ysFIXME - delete this redundant snapshot after test - if (type & (itObject | itVolume | itInstance)) { if (type & itObject) { bool was_cut = object(obj_idx)->is_cut(); @@ -2983,7 +2999,7 @@ bool ObjectList::delete_from_model_and_list(const ItemType type, const int obj_i } return false; } - else if (del_subobject_from_object(obj_idx, sub_obj_idx, type)) { + if (del_subobject_from_object(obj_idx, sub_obj_idx, type)) { type == itVolume ? delete_volume_from_list(obj_idx, sub_obj_idx) : delete_instance_from_list(obj_idx, sub_obj_idx); return true; @@ -3189,6 +3205,8 @@ void ObjectList::remove() if (m_objects_model->InvalidItem(item)) // item can be deleted for this moment (like last 2 Instances or Volumes) continue; parent = delete_item(item); + if (parent == item && m_objects_model->GetItemType(item) & itObject) // Object wasn't deleted + break; } } diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 3dd02ab8d..eb718b48d 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -279,6 +279,7 @@ public: bool can_split_instances(); bool has_selected_cut_object() const; void invalidate_cut_info_for_selection(); + void invalidate_cut_info_for_object(size_t obj_idx); bool can_merge_to_multipart_object() const; bool can_merge_to_single_object() const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index d75132542..459c6390c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1223,11 +1223,10 @@ bool GLGizmoCut3D::update_bb() on_unregister_raycasters_for_picking(); - if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info()) { - clear_selection(); + clear_selection(); + if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info()) m_selected.resize(selection->model_object()->cut_connectors.size(), false); - m_connectors_editing = !m_selected.empty(); - } + m_connectors_editing = !m_selected.empty(); return true; } @@ -1811,6 +1810,9 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) if (!mo) return; + // deactivate CutGizmo and than perform a cut + m_parent.reset_all_gizmos(); + // m_cut_z is the distance from the bed. Subtract possible SLA elevation. const double sla_shift_z = selection.get_first_volume()->get_sla_shift_z(); const double object_cut_z = m_plane_center.z() - sla_shift_z; @@ -1820,13 +1822,12 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) cut_center_offset[Z] -= sla_shift_z; if (0.0 < object_cut_z && can_perform_cut()) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by Plane")); + bool create_dowels_as_separate_object = false; const bool has_connectors = !mo->cut_connectors.empty(); - { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by Plane")); - // update connectors pos as offset of its center before cut performing - apply_connectors_in_model(mo, create_dowels_as_separate_object); - } + // update connectors pos as offset of its center before cut performing + apply_connectors_in_model(mo, create_dowels_as_separate_object); plater->cut(object_idx, instance_idx, assemble_transform(cut_center_offset) * m_rotation_m, only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7c7fbc989..2051192ee 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3020,8 +3020,6 @@ bool Plater::priv::delete_object_from_model(size_t obj_idx) dialog.SetButtonLabel(wxID_YES, _L("Delete object")); if (dialog.ShowModal() == wxID_CANCEL) return false; - // unmark all related CutParts of initial object - obj->invalidate_cut(); } wxString snapshot_label = _L("Delete Object"); @@ -3029,6 +3027,10 @@ bool Plater::priv::delete_object_from_model(size_t obj_idx) snapshot_label += ": " + wxString::FromUTF8(obj->name.c_str()); Plater::TakeSnapshot snapshot(q, snapshot_label); m_worker.cancel_all(); + + if (obj->is_cut()) + sidebar->obj_list()->invalidate_cut_info_for_object(obj_idx); + model.delete_object(obj_idx); update(); object_list_changed(); @@ -5940,9 +5942,8 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_mat wxCHECK_RET(instance_idx < object->instances.size(), "instance_idx out of bounds"); - this->suppress_snapshots(); wxBusyCursor wait; - + const auto new_objects = object->cut(instance_idx, cut_matrix, attributes); model().delete_object(obj_idx); @@ -5951,8 +5952,6 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_mat // suppress to call selection update for Object List to avoid call of early Gizmos on/off update p->load_model_objects(new_objects, false, false); - this->allow_snapshots(); - // now process all updates of the 3d scene update(); // Update InfoItems in ObjectList after update() to use of a correct value of the GLCanvas3D::is_sinking(),