Fixed a crash when deleting the last object with autocenter enabled (prusa3d/PrusaSlicer#11186).

(cherry picked from commit prusa3d/PrusaSlicer@926af1ab8d)
This commit is contained in:
Lukas Matena 2023-11-17 09:32:45 +08:00 committed by Noisyfox
parent f6e4c48fe7
commit 67c1f40eae
3 changed files with 104 additions and 93 deletions

View file

@ -2289,6 +2289,14 @@ int ModelObject::get_repaired_errors_count(const int vol_idx /*= -1*/) const
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)
{
m_material_id = material_id;

View file

@ -523,6 +523,8 @@ public:
// 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;
// Detect if object has at least one solid mash
bool has_solid_mesh() const;
bool is_cut() const { return cut_id.id().valid(); }
bool has_connectors() const;
private:

View file

@ -1,3 +1,9 @@
///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas, Tomáš Mészáros @tamasmeszaros, Vojtěch Bubník @bubnikv, Pavel Mikuš @Godrak, David Kocík @kocikdav, Filip Sykala @Jony01, Vojtěch Král @vojtechkral
///|/ Copyright (c) 2021 Mathias Rasmussen
///|/ Copyright (c) 2020 rongith
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "libslic3r/libslic3r.h"
#include "libslic3r/PresetBundle.hpp"
#include "GUI_ObjectList.hpp"
@ -3030,6 +3036,93 @@ bool ObjectList::can_split_instances()
return selection.is_multiple_full_instance() || selection.is_single_full_instance();
}
bool ObjectList::has_selected_cut_object() const
{
wxDataViewItemArray sels;
GetSelections(sels);
if (sels.IsEmpty())
return false;
for (wxDataViewItem item : sels) {
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
// ys_FIXME: The obj_idx<size condition is a workaround for https://github.com/prusa3d/PrusaSlicer/issues/11186,
// but not the correct fix. The deleted item probably should not be in sels in the first place.
if (obj_idx >= 0 && obj_idx < int(m_objects->size()) && object(obj_idx)->is_cut())
return true;
}
return false;
}
void ObjectList::invalidate_cut_info_for_selection()
{
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(int obj_idx)
{
ModelObject* init_obj = object(obj_idx);
if (!init_obj->is_cut())
return;
take_snapshot(_u8L("Invalidate cut info"));
const CutObjectBase cut_id = init_obj->cut_id;
// 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(int(idx)); obj->cut_id.is_equal(cut_id)) {
obj->invalidate_cut();
update_info_items(idx);
add_volumes_to_object_in_list(idx);
}
update_lock_icons_for_model();
}
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(_u8L("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();
}
bool ObjectList::can_merge_to_multipart_object() const
{
if (has_selected_cut_object())
@ -3044,10 +3137,9 @@ bool ObjectList::can_merge_to_multipart_object() const
return false;
// should be selected just objects
for (wxDataViewItem item : sels) {
for (wxDataViewItem item : sels)
if (!(m_objects_model->GetItemType(item) & (itObject | itInstance)))
return false;
}
return true;
}
@ -3072,97 +3164,6 @@ bool ObjectList::can_mesh_boolean() const
return (*m_objects)[obj_idx]->volumes.size() > 1 || ((*m_objects)[obj_idx]->volumes.size() == 1 && (*m_objects)[obj_idx]->volumes[0]->is_splittable());
}
bool ObjectList::has_selected_cut_object() const
{
wxDataViewItemArray sels;
GetSelections(sels);
if (sels.IsEmpty())
return false;
for (wxDataViewItem item : sels) {
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
if (obj_idx >= 0 && object(obj_idx)->is_cut())
return true;
}
return false;
}
void ObjectList::invalidate_cut_info_for_selection()
{
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(int obj_idx)
{
ModelObject *init_obj = object(obj_idx);
if (!init_obj->is_cut()) return;
take_snapshot("Invalidate cut info");
const CutObjectBase cut_id = init_obj->cut_id;
// 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(int(idx)); obj->cut_id.is_equal(cut_id)) {
obj->invalidate_cut();
update_info_items(idx);
add_volumes_to_object_in_list(idx);
}
update_lock_icons_for_model();
}
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("Delete all connectors");
auto has_solid_mesh = [](ModelObject* obj) {
for (const ModelVolume *volume : obj->volumes)
if (volume->is_model_part()) return true;
return false;
};
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() || !has_solid_mesh(obj)) {
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();
}
// NO_PARAMETERS function call means that changed object index will be determine from Selection()
void ObjectList::changed_object(const int obj_idx/* = -1*/) const
{