Cut Improvements: Fixed Undo/Redo for cut performance
+ ObjectList: Fixed items update after Invalidate cut information + CutGizmo: Fixed wrong mode selection after delete object and that add new
This commit is contained in:
parent
85af9b93f1
commit
64c57faf8f
@ -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()
|
||||
|
@ -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<class Archive> 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<class Archive> 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<class Archive> 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);
|
||||
|
@ -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<class Archive> void serialize(Archive& ar) {
|
||||
ar(cereal::base_class<ObjectBase>(this));
|
||||
ar(m_check_sum, m_connectors_cnt);
|
||||
}
|
||||
};
|
||||
|
||||
// Unique object / instance ID for the wipe tower.
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) |
|
||||
|
@ -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(),
|
||||
|
Loading…
Reference in New Issue
Block a user