Cut: Extension for delete parts from cut objects.

When try to delete something from the cut object, than not just inform the users about an impossibility of this action,
but allow them to invalidate a cut information or delete all connectors from related objects, but leave the cut info.
This commit is contained in:
YuSanka 2022-12-01 11:08:33 +01:00
parent b51d478031
commit ba8b81b27e
5 changed files with 121 additions and 28 deletions

View File

@ -1279,6 +1279,14 @@ void ModelObject::invalidate_cut()
volume->invalidate_cut_info(); volume->invalidate_cut_info();
} }
void ModelObject::delete_connectors()
{
for (int id = int(this->volumes.size()) - 1; id >= 0; id--) {
if (volumes[id]->is_cut_connector())
this->delete_volume(size_t(id));
}
}
void ModelObject::synchronize_model_after_cut() void ModelObject::synchronize_model_after_cut()
{ {
for (ModelObject* obj : m_model->objects) { for (ModelObject* obj : m_model->objects) {
@ -1994,6 +2002,14 @@ int ModelObject::get_repaired_errors_count(const int vol_idx /*= -1*/) const
stats.facets_reversed + stats.backwards_edges; stats.facets_reversed + stats.backwards_edges;
} }
bool ModelObject::has_solid_mesh() const
{
for (const ModelVolume* volume : volumes)
if (volume->is_model_part())
return true;
return false;
}
void ModelVolume::set_material_id(t_model_material_id material_id) void ModelVolume::set_material_id(t_model_material_id material_id)
{ {
m_material_id = material_id; m_material_id = material_id;

View File

@ -449,6 +449,8 @@ public:
void apply_cut_connectors(const std::string& name); void apply_cut_connectors(const std::string& name);
// invalidate cut state for this object and its connectors/volumes // invalidate cut state for this object and its connectors/volumes
void invalidate_cut(); void invalidate_cut();
// delete volumes which are marked as connector for this object
void delete_connectors();
void synchronize_model_after_cut(); void synchronize_model_after_cut();
void apply_cut_attributes(ModelObjectCutAttributes attributes); void apply_cut_attributes(ModelObjectCutAttributes attributes);
void clone_for_cut(ModelObject **obj); void clone_for_cut(ModelObject **obj);
@ -482,6 +484,8 @@ public:
// Get count of errors in the mesh( or all object's meshes, if volume index isn't defined) // Get count of errors in the mesh( or all object's meshes, if volume index isn't defined)
int get_repaired_errors_count(const int vol_idx = -1) const; int get_repaired_errors_count(const int vol_idx = -1) const;
// Detect if object has at least one solid mash
bool has_solid_mesh() const;
bool is_cut() const { return cut_id.id().valid(); } bool is_cut() const { return cut_id.id().valid(); }
bool has_connectors() const; bool has_connectors() const;

View File

@ -1880,6 +1880,7 @@ bool ObjectList::del_subobject_item(wxDataViewItem& item)
wxDataViewItem parent = m_objects_model->GetParent(item); wxDataViewItem parent = m_objects_model->GetParent(item);
InfoItemType item_info_type = m_objects_model->GetInfoItemType(item);
if (type & itSettings) if (type & itSettings)
del_settings_from_config(parent); del_settings_from_config(parent);
else if (type & itInstanceRoot && obj_idx != -1) else if (type & itInstanceRoot && obj_idx != -1)
@ -1889,7 +1890,7 @@ bool ObjectList::del_subobject_item(wxDataViewItem& item)
else if (type & itLayer && obj_idx != -1) else if (type & itLayer && obj_idx != -1)
del_layer_from_object(obj_idx, m_objects_model->GetLayerRangeByItem(item)); del_layer_from_object(obj_idx, m_objects_model->GetLayerRangeByItem(item));
else if (type & itInfo && obj_idx != -1) else if (type & itInfo && obj_idx != -1)
del_info_item(obj_idx, m_objects_model->GetInfoItemType(item)); del_info_item(obj_idx, item_info_type);
else if (idx == -1 || !del_subobject_from_object(obj_idx, idx, type)) else if (idx == -1 || !del_subobject_from_object(obj_idx, idx, type))
return false; return false;
@ -1898,9 +1899,12 @@ bool ObjectList::del_subobject_item(wxDataViewItem& item)
const std::string& icon_name = get_warning_icon_name(object(obj_idx)->get_object_stl_stats()); const std::string& icon_name = get_warning_icon_name(object(obj_idx)->get_object_stl_stats());
m_objects_model->UpdateWarningIcon(parent, icon_name); m_objects_model->UpdateWarningIcon(parent, icon_name);
} }
m_objects_model->Delete(item);
update_info_items(obj_idx); if (!(type & itInfo) || item_info_type != InfoItemType::CutConnectors) {
// Connectors Item is already updated/deleted inside the del_info_item()
m_objects_model->Delete(item);
update_info_items(obj_idx);
}
return true; return true;
} }
@ -1926,7 +1930,10 @@ void ObjectList::del_info_item(const int obj_idx, InfoItemType type)
break; break;
case InfoItemType::CutConnectors: case InfoItemType::CutConnectors:
show_error(nullptr, _L("Connectors cannot be deleted from cut object.")); if (!del_from_cut_object(true)) {
// there is no need to post EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS if nothing was changed
return;
}
break; break;
case InfoItemType::MmuSegmentation: case InfoItemType::MmuSegmentation:
@ -2018,6 +2025,38 @@ void ObjectList::del_layers_from_object(const int obj_idx)
changed_object(obj_idx); changed_object(obj_idx);
} }
bool ObjectList::del_from_cut_object(bool is_cut_connector, bool is_model_part/* = false*/, bool is_negative_volume/* = false*/)
{
const long buttons_style = is_cut_connector ? (wxYES | wxNO | wxCANCEL) : (wxYES | wxCANCEL);
const wxString title = is_cut_connector ? _L("Delete connector from object which is a part of cut") :
is_model_part ? _L("Delete solid part from object which is a part of cut") :
is_negative_volume ? _L("Delete negative volume from object which is a part of cut") : "";
const wxString msg_end = is_cut_connector ? ("\n" + _L("To save cut correspondence you can delete all connectors from all related objects.")) : "";
InfoDialog dialog(wxGetApp().plater(), title,
_L("This action will break a cut correspondence.\n"
"After that PrusaSlicer can't guarantee model consistency.\n"
"\n"
"To manipulate with solid parts or negative volumes you have to invalidate cut infornation first." + msg_end ),
false, buttons_style | wxCANCEL_DEFAULT | wxICON_WARNING);
dialog.SetButtonLabel(wxID_YES, _L("Invalidate cut info"));
if (is_cut_connector)
dialog.SetButtonLabel(wxID_NO, _L("Delete all connectors"));
const int answer = dialog.ShowModal();
if (answer == wxID_CANCEL)
return false;
if (answer == wxID_YES)
invalidate_cut_info_for_selection();
else if (answer == wxID_NO)
delete_all_connectors_for_selection();
return true;
}
bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, const int type) bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, const int type)
{ {
assert(idx >= 0); assert(idx >= 0);
@ -2039,15 +2078,10 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
Slic3r::GUI::show_error(nullptr, _L("From Object List You can't delete the last solid part from object.")); Slic3r::GUI::show_error(nullptr, _L("From Object List You can't delete the last solid part from object."));
return false; return false;
} }
if (object->is_cut()) { if (object->is_cut() && (volume->is_model_part() || volume->is_negative_volume())) {
if (volume->is_model_part()) { del_from_cut_object(volume->is_cut_connector(), volume->is_model_part(), volume->is_negative_volume());
Slic3r::GUI::show_error(nullptr, _L("Solid part cannot be deleted from cut object.")); // in any case return false to break the deletion
return false; return false;
}
if (volume->is_negative_volume()) {
Slic3r::GUI::show_error(nullptr, _L("Negative volume cannot be deleted from cut object."));
return false;
}
} }
take_snapshot(_L("Delete Subobject")); take_snapshot(_L("Delete Subobject"));
@ -2489,6 +2523,7 @@ bool ObjectList::has_selected_cut_object() const
return false; return false;
} }
void ObjectList::invalidate_cut_info_for_selection() void ObjectList::invalidate_cut_info_for_selection()
{ {
const wxDataViewItem item = GetSelection(); const wxDataViewItem item = GetSelection();
@ -2499,27 +2534,61 @@ void ObjectList::invalidate_cut_info_for_selection()
} }
} }
void ObjectList::invalidate_cut_info_for_object(size_t obj_idx) void ObjectList::invalidate_cut_info_for_object(int obj_idx)
{ {
ModelObject* init_obj = object(int(obj_idx)); ModelObject* init_obj = object(obj_idx);
if (!init_obj->is_cut()) if (!init_obj->is_cut())
return; return;
take_snapshot(_L("Invalidate cut info")); take_snapshot(_L("Invalidate cut info"));
auto invalidate_cut = [this](size_t obj_idx) { const CutObjectBase cut_id = init_obj->cut_id;
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) // invalidate cut for related objects (which have the same cut_id)
for (size_t idx = 0; idx < m_objects->size(); idx++) 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)) if (ModelObject* obj = object(int(idx)); obj->cut_id.is_equal(cut_id)) {
invalidate_cut(idx); obj->invalidate_cut();
update_info_items(idx);
add_volumes_to_object_in_list(idx);
}
// invalidate own cut information update_lock_icons_for_model();
invalidate_cut(size_t(obj_idx)); }
void ObjectList::delete_all_connectors_for_selection()
{
const wxDataViewItem item = GetSelection();
if (item) {
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
if (obj_idx >= 0)
delete_all_connectors_for_object(size_t(obj_idx));
}
}
void ObjectList::delete_all_connectors_for_object(int obj_idx)
{
ModelObject* init_obj = object(obj_idx);
if (!init_obj->is_cut())
return;
take_snapshot(_L("Delete all connectors"));
const CutObjectBase cut_id = init_obj->cut_id;
// Delete all connectors for related objects (which have the same cut_id)
Model& model = wxGetApp().plater()->model();
for (int idx = int(m_objects->size())-1; idx >= 0; idx--)
if (ModelObject* obj = object(idx); obj->cut_id.is_equal(cut_id)) {
obj->delete_connectors();
if (obj->volumes.empty() || !obj->has_solid_mesh()) {
model.delete_object(idx);
m_objects_model->Delete(m_objects_model->GetItemById(idx));
continue;
}
update_info_items(idx);
add_volumes_to_object_in_list(idx);
changed_object(int(idx));
}
update_lock_icons_for_model(); update_lock_icons_for_model();
} }
@ -3044,6 +3113,7 @@ bool ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& it
return false; return false;
m_prevent_list_events = true; m_prevent_list_events = true;
ScopeGuard sg_prevent_list_events = ScopeGuard([this]() { m_prevent_list_events = false; });
std::set<size_t> modified_objects_ids; std::set<size_t> modified_objects_ids;
for (std::vector<ItemForDelete>::const_reverse_iterator item = items_for_delete.rbegin(); item != items_for_delete.rend(); ++item) { for (std::vector<ItemForDelete>::const_reverse_iterator item = items_for_delete.rbegin(); item != items_for_delete.rend(); ++item) {
@ -3059,7 +3129,7 @@ bool ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& it
} }
else { else {
if (!del_subobject_from_object(item->obj_idx, item->sub_obj_idx, item->type)) if (!del_subobject_from_object(item->obj_idx, item->sub_obj_idx, item->type))
continue; return false;// continue;
if (item->type&itVolume) { if (item->type&itVolume) {
m_objects_model->Delete(m_objects_model->GetItemByVolumeId(item->obj_idx, item->sub_obj_idx)); m_objects_model->Delete(m_objects_model->GetItemByVolumeId(item->obj_idx, item->sub_obj_idx));
ModelObject* obj = object(item->obj_idx); ModelObject* obj = object(item->obj_idx);

View File

@ -266,6 +266,7 @@ public:
void del_instances_from_object(const int obj_idx); void del_instances_from_object(const int obj_idx);
void del_layer_from_object(const int obj_idx, const t_layer_height_range& layer_range); void del_layer_from_object(const int obj_idx, const t_layer_height_range& layer_range);
void del_layers_from_object(const int obj_idx); void del_layers_from_object(const int obj_idx);
bool del_from_cut_object(bool is_connector, bool is_model_part = false, bool is_negative_volume = false);
bool del_subobject_from_object(const int obj_idx, const int idx, const int type); bool del_subobject_from_object(const int obj_idx, const int idx, const int type);
void del_info_item(const int obj_idx, InfoItemType type); void del_info_item(const int obj_idx, InfoItemType type);
void split(); void split();
@ -282,7 +283,9 @@ public:
bool can_split_instances(); bool can_split_instances();
bool has_selected_cut_object() const; bool has_selected_cut_object() const;
void invalidate_cut_info_for_selection(); void invalidate_cut_info_for_selection();
void invalidate_cut_info_for_object(size_t obj_idx); void invalidate_cut_info_for_object(int obj_idx);
void delete_all_connectors_for_selection();
void delete_all_connectors_for_object(int obj_idx);
bool can_merge_to_multipart_object() const; bool can_merge_to_multipart_object() const;
bool can_merge_to_single_object() const; bool can_merge_to_single_object() const;

View File

@ -3001,7 +3001,7 @@ bool Plater::priv::delete_object_from_model(size_t obj_idx)
InfoDialog dialog(q, _L("Delete object which is a part of cut object"), InfoDialog dialog(q, _L("Delete object which is a part of cut object"),
_L("You try to delete an object which is a part of a cut object.\n" _L("You try to delete an object which is a part of a cut object.\n"
"This action will break a cut correspondence.\n" "This action will break a cut correspondence.\n"
"After that PrusaSlicer can't garantie model consistency"), "After that PrusaSlicer can't guarantee model consistency"),
false, wxYES | wxCANCEL | wxCANCEL_DEFAULT | wxICON_WARNING); false, wxYES | wxCANCEL | wxCANCEL_DEFAULT | wxICON_WARNING);
dialog.SetButtonLabel(wxID_YES, _L("Delete object")); dialog.SetButtonLabel(wxID_YES, _L("Delete object"));
if (dialog.ShowModal() == wxID_CANCEL) if (dialog.ShowModal() == wxID_CANCEL)