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,48 +3036,6 @@ bool ObjectList::can_split_instances()
return selection.is_multiple_full_instance() || selection.is_single_full_instance();
}
bool ObjectList::can_merge_to_multipart_object() const
{
if (has_selected_cut_object())
return false;
if (printer_technology() == ptSLA)
return false;
wxDataViewItemArray sels;
GetSelections(sels);
if (sels.IsEmpty())
return false;
// should be selected just objects
for (wxDataViewItem item : sels) {
if (!(m_objects_model->GetItemType(item) & (itObject | itInstance)))
return false;
}
return true;
}
bool ObjectList::can_merge_to_single_object() const
{
int obj_idx = get_selected_obj_idx();
if (obj_idx < 0)
return false;
// selected object should be multipart
return (*m_objects)[obj_idx]->volumes.size() > 1;
}
bool ObjectList::can_mesh_boolean() const
{
int obj_idx = get_selected_obj_idx();
if (obj_idx < 0)
return false;
// selected object should be multi mesh
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;
@ -3081,7 +3045,9 @@ bool ObjectList::has_selected_cut_object() const
for (wxDataViewItem item : sels) {
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
if (obj_idx >= 0 && object(obj_idx)->is_cut())
// 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;
}
@ -3101,9 +3067,10 @@ void ObjectList::invalidate_cut_info_for_selection()
void ObjectList::invalidate_cut_info_for_object(int obj_idx)
{
ModelObject* init_obj = object(obj_idx);
if (!init_obj->is_cut()) return;
if (!init_obj->is_cut())
return;
take_snapshot("Invalidate cut info");
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)
@ -3133,13 +3100,7 @@ void ObjectList::delete_all_connectors_for_object(int 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;
};
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)
@ -3148,7 +3109,7 @@ void ObjectList::delete_all_connectors_for_object(int obj_idx)
if (ModelObject* obj = object(idx); obj->cut_id.is_equal(cut_id)) {
obj->delete_connectors();
if (obj->volumes.empty() || !has_solid_mesh(obj)) {
if (obj->volumes.empty() || !obj->has_solid_mesh()) {
model.delete_object(idx);
m_objects_model->Delete(m_objects_model->GetItemById(idx));
continue;
@ -3162,6 +3123,46 @@ void ObjectList::delete_all_connectors_for_object(int obj_idx)
update_lock_icons_for_model();
}
bool ObjectList::can_merge_to_multipart_object() const
{
if (has_selected_cut_object())
return false;
if (printer_technology() == ptSLA)
return false;
wxDataViewItemArray sels;
GetSelections(sels);
if (sels.IsEmpty())
return false;
// should be selected just objects
for (wxDataViewItem item : sels)
if (!(m_objects_model->GetItemType(item) & (itObject | itInstance)))
return false;
return true;
}
bool ObjectList::can_merge_to_single_object() const
{
int obj_idx = get_selected_obj_idx();
if (obj_idx < 0)
return false;
// selected object should be multipart
return (*m_objects)[obj_idx]->volumes.size() > 1;
}
bool ObjectList::can_mesh_boolean() const
{
int obj_idx = get_selected_obj_idx();
if (obj_idx < 0)
return false;
// selected object should be multi mesh
return (*m_objects)[obj_idx]->volumes.size() > 1 || ((*m_objects)[obj_idx]->volumes.size() == 1 && (*m_objects)[obj_idx]->volumes[0]->is_splittable());
}
// NO_PARAMETERS function call means that changed object index will be determine from Selection()
void ObjectList::changed_object(const int obj_idx/* = -1*/) const