Cut: Fixes and improvements for object's context menu

* Disable or delete some menu items, which are inappropriate for cut objects
* For cut objects added menu item "Invalidate cut info" to disconnect related cut parts of initial object
* If just one part is kept after cut performance, than don't apply a cut info for this object.

+ CutGizmo: Fixed selection of the mode
   An object has connectors         -> Connectors mode
   An object doesn't has connectors -> CutPlane mode
This commit is contained in:
YuSanka 2022-10-07 16:26:19 +02:00
parent 74a32e3261
commit 85af9b93f1
10 changed files with 84 additions and 19 deletions

View File

@ -1261,7 +1261,9 @@ void ModelObject::invalidate_cut()
{
for (ModelObject* obj : m_model->objects)
if (obj != this && obj->cut_id.is_equal(this->cut_id))
obj->cut_id.ivalidate();
obj->cut_id.invalidate();
// invalidate own cut_id
this->cut_id.invalidate();
}
void ModelObject::synchronize_model_after_cut()
@ -1276,6 +1278,10 @@ void ModelObject::synchronize_model_after_cut()
void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes)
{
// we don't save cut information, if result will not contains all parts of initial object
if (!attributes.has(ModelObjectCutAttribute::KeepUpper) || !attributes.has(ModelObjectCutAttribute::KeepLower))
return;
if (cut_id.id().invalid())
cut_id.init();
{

View File

@ -161,7 +161,7 @@ public:
return *this;
}
void ivalidate() {
void invalidate() {
set_invalid_id();
m_check_sum = 1;
m_connectors_cnt = 0;

View File

@ -685,6 +685,24 @@ wxMenuItem* MenuFactory::append_menu_item_printable(wxMenu* menu)
return menu_item_printable;
}
void MenuFactory::append_menu_item_invalidate_cut_info(wxMenu* menu)
{
const wxString menu_name = _L("Invalidate cut info");
bool is_cut = obj_list()->has_selected_cut_object();
auto menu_item_id = menu->FindItem(menu_name);
if (menu_item_id != wxNOT_FOUND) {
// Delete old menu item if selected object isn't cut
if (!is_cut)
menu->Destroy(menu_item_id);
}
else if (is_cut)
append_menu_item(menu, wxID_ANY, menu_name, "",
[](wxCommandEvent&) { obj_list()->invalidate_cut_info_for_selection(); }, "", menu,
[]() { return true; }, m_parent);
}
void MenuFactory::append_menu_items_osx(wxMenu* menu)
{
append_menu_item(menu, wxID_ANY, _L("Rename"), "",
@ -821,6 +839,8 @@ void MenuFactory::append_menu_items_convert_unit(wxMenu* menu, int insert_pos/*
ModelObjectPtrs objects;
for (int obj_idx : obj_idxs) {
ModelObject* object = obj_list()->object(obj_idx);
if (object->is_cut())
return false;
if (vol_idxs.empty()) {
for (ModelVolume* volume : object->volumes)
if (volume_respects_conversion(volume, conver_type))
@ -1021,6 +1041,7 @@ wxMenu* MenuFactory::object_menu()
append_menu_item_settings(&m_object_menu);
append_menu_item_change_extruder(&m_object_menu);
update_menu_items_instance_manipulation(mtObjectFFF);
append_menu_item_invalidate_cut_info(&m_object_menu);
return &m_object_menu;
}
@ -1030,6 +1051,7 @@ wxMenu* MenuFactory::sla_object_menu()
append_menu_items_convert_unit(&m_sla_object_menu, 11);
append_menu_item_settings(&m_sla_object_menu);
update_menu_items_instance_manipulation(mtObjectSLA);
append_menu_item_invalidate_cut_info(&m_sla_object_menu);
return &m_sla_object_menu;
}

View File

@ -89,6 +89,7 @@ private:
wxMenuItem* append_menu_item_change_type(wxMenu* menu);
wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu);
wxMenuItem* append_menu_item_printable(wxMenu* menu);
void append_menu_item_invalidate_cut_info(wxMenu *menu);
void append_menu_items_osx(wxMenu* menu);
wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu);
wxMenuItem* append_menu_item_simplify(wxMenu* menu);

View File

@ -2443,9 +2443,41 @@ 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);
if (obj_idx >= 0 && object(obj_idx)->is_cut())
return true;
}
return false;
}
void ObjectList::invalidate_cut_info_for_selection()
{
wxDataViewItemArray sels;
GetSelections(sels);
if (sels.IsEmpty())
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();
}
update_lock_icons_for_model();
}
bool ObjectList::can_merge_to_multipart_object() const
{
if (printer_technology() == ptSLA)
if (printer_technology() == ptSLA || has_selected_cut_object())
return false;
wxDataViewItemArray sels;

View File

@ -277,6 +277,8 @@ public:
bool is_splittable(bool to_objects);
bool selected_instances_of_same_object();
bool can_split_instances();
bool has_selected_cut_object() const;
void invalidate_cut_info_for_selection();
bool can_merge_to_multipart_object() const;
bool can_merge_to_single_object() const;

View File

@ -1226,6 +1226,7 @@ bool GLGizmoCut3D::update_bb()
if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info()) {
clear_selection();
m_selected.resize(selection->model_object()->cut_connectors.size(), false);
m_connectors_editing = !m_selected.empty();
}
return true;
@ -1333,12 +1334,12 @@ void GLGizmoCut3D::adjust_window_position(float x, float y, float bottom_limit)
ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always);
if (last_h != win_h || last_y != y) {
if (!is_approx(last_h, win_h) || !is_approx(last_y, y)) {
// ask canvas for another frame to render the window in the correct position
m_imgui->set_requires_extra_frame();
if (last_h != win_h)
if (!is_approx(last_h, win_h))
last_h = win_h;
if (last_y != y)
if (!is_approx(last_y, y))
last_y = y;
}
}
@ -1773,9 +1774,9 @@ bool GLGizmoCut3D::can_perform_cut() const
return tbb.contains(m_plane_center);
}
void GLGizmoCut3D::apply_connectors_in_model(ModelObject* mo, const bool has_connectors, bool &create_dowels_as_separate_object)
void GLGizmoCut3D::apply_connectors_in_model(ModelObject* mo, bool &create_dowels_as_separate_object)
{
if (has_connectors && m_connector_mode == CutConnectorMode::Manual) {
if (m_connector_mode == CutConnectorMode::Manual) {
clear_selection();
for (CutConnector&connector : mo->cut_connectors) {
@ -1824,7 +1825,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
{
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, has_connectors, create_dowels_as_separate_object);
apply_connectors_in_model(mo, create_dowels_as_separate_object);
}
plater->cut(object_idx, instance_idx, assemble_transform(cut_center_offset) * m_rotation_m,

View File

@ -225,7 +225,7 @@ private:
void render_connectors();
bool can_perform_cut() const;
void apply_connectors_in_model(ModelObject* mo, const bool has_connectors, bool &create_dowels_as_separate_object);
void apply_connectors_in_model(ModelObject* mo, bool &create_dowels_as_separate_object);
bool cut_line_processing() const;
void discard_cut_line_processing();

View File

@ -1807,6 +1807,7 @@ void ObjectDataViewModel::UpdateLockIcon(const wxDataViewItem& item, bool has_lo
for (const wxDataViewItem& child : children)
UpdateLockIcon(child, has_lock);
}
ItemChanged(item);
}
} // namespace GUI

View File

@ -4786,7 +4786,8 @@ bool Plater::priv::can_split(bool to_objects) const
bool Plater::priv::can_scale_to_print_volume() const
{
const BuildVolume::Type type = this->bed.build_volume().type();
return !view3D->get_canvas3d()->get_selection().is_empty() && (type == BuildVolume::Type::Rectangle || type == BuildVolume::Type::Circle);
return !sidebar->obj_list()->has_selected_cut_object() &&
!view3D->get_canvas3d()->get_selection().is_empty() && (type == BuildVolume::Type::Rectangle || type == BuildVolume::Type::Circle);
}
bool Plater::priv::layers_height_allowed() const
@ -4801,16 +4802,19 @@ bool Plater::priv::layers_height_allowed() const
bool Plater::priv::can_mirror() const
{
return get_selection().is_from_single_instance();
return !sidebar->obj_list()->has_selected_cut_object() && get_selection().is_from_single_instance();
}
bool Plater::priv::can_replace_with_stl() const
{
return get_selection().get_volume_idxs().size() == 1;
return !sidebar->obj_list()->has_selected_cut_object() && get_selection().get_volume_idxs().size() == 1;
}
bool Plater::priv::can_reload_from_disk() const
{
if (sidebar->obj_list()->has_selected_cut_object())
return false;
#if ENABLE_RELOAD_FROM_DISK_REWORK
// collect selected reloadable ModelVolumes
std::vector<std::pair<int, int>> selected_volumes = reloadable_volumes(model, get_selection());
@ -4939,9 +4943,7 @@ bool Plater::priv::can_increase_instances() const
|| q->canvas3D()->get_gizmos_manager().is_in_editing_mode())
return false;
int obj_idx = get_selected_object_idx();
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size())
&& !model.objects[obj_idx]->is_cut();
return !sidebar->obj_list()->has_selected_cut_object();
}
bool Plater::priv::can_decrease_instances() const
@ -4950,9 +4952,7 @@ bool Plater::priv::can_decrease_instances() const
|| q->canvas3D()->get_gizmos_manager().is_in_editing_mode())
return false;
int obj_idx = get_selected_object_idx();
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1)
&& !model.objects[obj_idx]->is_cut();
return !sidebar->obj_list()->has_selected_cut_object();
}
bool Plater::priv::can_split_to_objects() const