+ Added CutObjectBase class which contains cut attributes for object
+ ObjectList and ManipulationPanel :
    * Disable all ManipulationEditors for solid/negative volumes of cut object
    * Disable Scale/Size ManipulationEditors for objects/instances of objects which are CutParts of initial object
+ Scale/Rotation/Move gizmos are disabled for solid/negative volumes of cut object
+ Select whole CutParts of initial object when ScaleGizmo is active
This commit is contained in:
YuSanka 2022-04-11 11:20:00 +02:00
parent f9e22513c1
commit 463e9ab530
12 changed files with 261 additions and 10 deletions

View File

@ -1,4 +1,5 @@
#include "Model.hpp"
#include "Model.hpp"
#include "libslic3r.h"
#include "BuildVolume.hpp"
#include "Exception.hpp"
@ -610,6 +611,8 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
this->layer_height_profile = rhs.layer_height_profile;
this->printable = rhs.printable;
this->origin_translation = rhs.origin_translation;
this->cut_connectors_count = rhs.cut_connectors_count;
this->cut_id.copy(rhs.cut_id);
m_bounding_box = rhs.m_bounding_box;
m_bounding_box_valid = rhs.m_bounding_box_valid;
m_raw_bounding_box = rhs.m_raw_bounding_box;
@ -1369,12 +1372,17 @@ indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes conn
void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes)
{
// discard old connector markers vor volumes
for (ModelVolume* volume : volumes) {
volume->source.is_connector = false;
}
if (cut_connectors.empty())
return;
indexed_triangle_set connector_mesh = get_connector_mesh(connector_attributes);
size_t connector_id = 0;
size_t connector_id = cut_connectors_count;
for (const CutConnector& connector : cut_connectors) {
TriangleMesh mesh = TriangleMesh(connector_mesh);
@ -1393,10 +1401,20 @@ void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttr
new_volume->source.is_connector = true;
}
cut_connectors_count += cut_connectors.size();
// delete all connectors
cut_connectors.clear();
}
void ModelObject::synchronize_model_after_cut()
{
for (ModelObject* obj : m_model->objects) {
if (obj == this || obj->cut_id.is_equal(this->cut_id))
continue;
obj->cut_id.set_check_sum(this->cut_id.check_sum());
}
}
ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes)
{
if (!attributes.has(ModelObjectCutAttribute::KeepUpper) && !attributes.has(ModelObjectCutAttribute::KeepLower))
@ -1404,6 +1422,18 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start";
// initiate/update cut attributes for object
if (cut_id.id().invalid())
cut_id.init();
{
int cut_obj_cnt = -1;
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) cut_obj_cnt++;
if (attributes.has(ModelObjectCutAttribute::KeepLower)) cut_obj_cnt++;
if (attributes.has(ModelObjectCutAttribute::CreateDowels)) cut_obj_cnt++;
if (cut_obj_cnt > 0)
cut_id.increase_check_sum(size_t(cut_obj_cnt));
}
// Clone the object to duplicate instances, materials etc.
ModelObject* upper = attributes.has(ModelObjectCutAttribute::KeepUpper) ? ModelObject::new_clone(*this) : nullptr;
ModelObject* lower = attributes.has(ModelObjectCutAttribute::KeepLower) ? ModelObject::new_clone(*this) : nullptr;
@ -1499,10 +1529,16 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 0.0));
}
}
else
else {
// Modifiers are not cut, but we still need to add the instance transformation
// to the modifier volume transformation to preserve their shape properly.
volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix));
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
upper->add_volume(*volume);
if (attributes.has(ModelObjectCutAttribute::KeepLower))
lower->add_volume(*volume);
}
}
else if (!volume->mesh().empty()
// && !volume->source.is_connector // we don't allow to cut a connectors
@ -1646,6 +1682,8 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end";
synchronize_model_after_cut();
return res;
}

View File

@ -346,6 +346,9 @@ public:
// Connectors to be added into the object after cut
CutConnectors cut_connectors;
// count of connectors in object
size_t cut_connectors_count{ 0 };
CutObjectBase cut_id;
/* This vector accumulates the total translation applied to the object by the
center_around_origin() method. Callers might want to apply the same translation
@ -434,6 +437,7 @@ public:
ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes);
static indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes);
void apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes);
void synchronize_model_after_cut();
ModelObjectPtrs cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes);
void split(ModelObjectPtrs* new_objects);
void merge();
@ -458,6 +462,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;
bool is_cut() const { return cut_id.id().valid(); }
private:
friend class Model;
// This constructor assigns new ID to this ModelObject and its config.

View File

@ -128,6 +128,41 @@ private:
template<class Archive> void serialize(Archive &ar) { ar(m_timestamp); }
};
class CutObjectBase : public ObjectBase
{
// check sum of CutPartsObject
size_t m_check_sum{ 1 };
public:
// Default Constructor to assign an invalid ID
CutObjectBase() : ObjectBase(-1) {}
// Constructor with ignored int parameter to assign an invalid ID, to be replaced
// by an existing ID copied from elsewhere.
CutObjectBase(int) : ObjectBase(-1) {}
// The class tree will have virtual tables and type information.
virtual ~CutObjectBase() = default;
bool operator<(const CutObjectBase& other) const { return other.id() > this->id(); }
bool operator==(const CutObjectBase& other) const { return other.id() == this->id(); }
void copy(const CutObjectBase& rhs) {
this->copy_id(rhs);
this->m_check_sum = rhs.check_sum();
}
CutObjectBase operator=(const CutObjectBase& other) {
this->copy(other);
return *this;
}
void init() { this->set_new_unique_id(); }
bool has_same_id(const CutObjectBase& rhs) { return this->id() == rhs.id(); }
bool is_equal(const CutObjectBase& rhs) { return this->id() == rhs.id() && this->check_sum() == rhs.check_sum(); }
size_t check_sum() const { return m_check_sum; }
void set_check_sum(size_t cs) { m_check_sum = cs; }
void increase_check_sum(size_t cnt) { m_check_sum += cnt; }
};
// Unique object / instance ID for the wipe tower.
extern ObjectID wipe_tower_object_id();
extern ObjectID wipe_tower_instance_id();

View File

@ -3360,6 +3360,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
show_sinking_contours();
}
}
else if (evt.LeftUp() &&
m_gizmos.get_current_type() == GLGizmosManager::EType::Scale &&
m_gizmos.get_current()->get_state() == GLGizmoBase::EState::On) {
wxGetApp().obj_list()->selection_changed();
}
#if ENABLE_OBJECT_MANIPULATOR_FOCUS
handle_sidebar_focus_event("", false);

View File

@ -665,6 +665,8 @@ void ObjectList::selection_changed()
fix_multiselection_conflicts();
fix_cut_selection();
// update object selection on Plater
if (!m_prevent_canvas_selection_update)
update_selections_on_canvas();
@ -2437,6 +2439,9 @@ void ObjectList::part_selection_changed()
bool update_and_show_settings = false;
bool update_and_show_layers = false;
bool enable_manipulation {true};
bool disable_ss_manipulation {false};
const auto item = GetSelection();
if ( multiple_selection() || (item && m_objects_model->GetItemType(item) == itInstanceRoot )) {
@ -2445,6 +2450,43 @@ void ObjectList::part_selection_changed()
const Selection& selection = scene_selection();
// don't show manipulation panel for case of all Object's parts selection
update_and_show_manipulations = !selection.is_single_full_instance();
if (int obj_idx = selection.get_object_idx(); obj_idx >= 0) {
if (selection.is_any_volume() || selection.is_any_modifier())
enable_manipulation = !(*m_objects)[obj_idx]->is_cut();
else// if (item && m_objects_model->GetItemType(item) == itInstanceRoot)
disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut();
}
else {
wxDataViewItemArray sels;
GetSelections(sels);
if (selection.is_single_full_object() || selection.is_multiple_full_instance() ) {
int obj_idx = m_objects_model->GetObjectIdByItem(sels.front());
disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut();
}
else if (selection.is_mixed() || selection.is_multiple_full_object()) {
std::map<CutObjectBase, std::set<int>> cut_objects;
// find cut objects
for (auto item : sels) {
int obj_idx = m_objects_model->GetObjectIdByItem(item);
const ModelObject* obj = object(obj_idx);
if (obj->is_cut()) {
if (cut_objects.find(obj->cut_id) == cut_objects.end())
cut_objects[obj->cut_id] = std::set<int>{ obj_idx };
else
cut_objects.at(obj->cut_id).insert(obj_idx);
}
}
// check if selected cut objects are "full selected"
for (auto cut_object : cut_objects)
if (cut_object.first.check_sum() != cut_object.second.size()) {
disable_ss_manipulation = true;
break;
}
}
}
}
else {
if (item) {
@ -2486,6 +2528,8 @@ void ObjectList::part_selection_changed()
default: { break; }
}
}
else
disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut();
}
else {
if (type & itSettings) {
@ -2509,6 +2553,7 @@ void ObjectList::part_selection_changed()
volume_id = m_objects_model->GetVolumeIdByItem(item);
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
update_and_show_manipulations = true;
enable_manipulation = !(*m_objects)[obj_idx]->is_cut();
}
else if (type & itInstance) {
og_name = _L("Instance manipulation");
@ -2516,6 +2561,7 @@ void ObjectList::part_selection_changed()
// fill m_config by object's values
m_config = &(*m_objects)[obj_idx]->config;
disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut();
}
else if (type & (itLayerRoot|itLayer)) {
og_name = type & itLayerRoot ? _L("Height ranges") : _L("Settings for height range");
@ -2538,6 +2584,11 @@ void ObjectList::part_selection_changed()
wxGetApp().obj_manipul()->update_item_name(m_objects_model->GetName(item));
wxGetApp().obj_manipul()->update_warning_icon_state(get_mesh_errors_info(obj_idx, volume_id));
}
if (disable_ss_manipulation)
wxGetApp().obj_manipul()->DisableScale();
else
wxGetApp().obj_manipul()->Enable(enable_manipulation);
}
if (update_and_show_settings)
@ -2554,6 +2605,7 @@ void ObjectList::part_selection_changed()
panel.Freeze();
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false);
wxGetApp().plater()->canvas3D()->enable_moving(enable_manipulation);
wxGetApp().obj_manipul() ->UpdateAndShow(update_and_show_manipulations);
wxGetApp().obj_settings()->UpdateAndShow(update_and_show_settings);
wxGetApp().obj_layers() ->UpdateAndShow(update_and_show_layers);
@ -3420,11 +3472,34 @@ void ObjectList::update_selections()
if (sels.size() == 0 || m_selection_mode & smSettings)
m_selection_mode = smUndef;
select_items(sels);
// Scroll selected Item in the middle of an object list
ensure_current_item_visible();
if (fix_cut_selection(sels)) {
m_prevent_list_events = true;
// If some part is selected, unselect all items except of selected parts of the current object
UnselectAll();
SetSelections(sels);
m_prevent_list_events = false;
// update object selection on Plater
if (!m_prevent_canvas_selection_update)
update_selections_on_canvas();
// to update the toolbar and info sizer
if (!GetSelection() || m_objects_model->GetItemType(GetSelection()) == itObject) {
auto event = SimpleEvent(EVT_OBJ_LIST_OBJECT_SELECT);
event.SetEventObject(this);
wxPostEvent(this, event);
}
part_selection_changed();
}
else {
select_items(sels);
// Scroll selected Item in the middle of an object list
ensure_current_item_visible();
}
}
void ObjectList::update_selections_on_canvas()
@ -3753,6 +3828,52 @@ void ObjectList::fix_multiselection_conflicts()
m_prevent_list_events = false;
}
bool ObjectList::fix_cut_selection(wxDataViewItemArray& sels)
{
if (wxGetApp().plater()->canvas3D()->get_gizmos_manager().get_current_type() == GLGizmosManager::Scale) {
for (const auto& item : sels) {
if (m_objects_model->GetItemType(item) & (itInstance | itObject) ||
(m_objects_model->GetItemType(item) & itSettings &&
m_objects_model->GetItemType(m_objects_model->GetParent(item)) & itObject)) {
bool is_instance_selection = m_objects_model->GetItemType(item) & itInstance;
int obj_idx = m_objects_model->GetObjectIdByItem(item);
int inst_idx = is_instance_selection ? m_objects_model->GetInstanceIdByItem(item) : 0;
if (auto obj = object(obj_idx); obj->is_cut()) {
sels.Clear();
auto cut_id = obj->cut_id;
for (int obj_idx = 0; obj_idx < (*m_objects).size(); ++obj_idx) {
auto object = (*m_objects)[obj_idx];
if (object->is_cut() && object->cut_id.has_same_id(cut_id))
sels.Add(is_instance_selection ? m_objects_model->GetItemByInstanceId(obj_idx, inst_idx) : m_objects_model->GetItemById(obj_idx));
}
return true;
}
}
}
}
return false;
}
void ObjectList::fix_cut_selection()
{
wxDataViewItemArray sels;
GetSelections(sels);
if (fix_cut_selection(sels)) {
m_prevent_list_events = true;
// If some part is selected, unselect all items except of selected parts of the current object
UnselectAll();
SetSelections(sels);
m_prevent_list_events = false;
}
}
ModelVolume* ObjectList::get_selected_model_volume()
{
wxDataViewItem item = GetSelection();

View File

@ -353,6 +353,9 @@ public:
bool check_last_selection(wxString& msg_str);
// correct current selections to avoid of the possible conflicts
void fix_multiselection_conflicts();
// correct selection in respect to the cut_id if any exists
void fix_cut_selection();
bool fix_cut_selection(wxDataViewItemArray& sels);
ModelVolume* get_selected_model_volume();
void change_part_type();

View File

@ -465,6 +465,22 @@ void ObjectManipulation::UpdateAndShow(const bool show)
OG_Settings::UpdateAndShow(show);
}
void ObjectManipulation::Enable(const bool enadle)
{
for (auto editor : m_editors)
editor->Enable(enadle);
for (wxWindow* win : std::initializer_list<wxWindow*>{ m_reset_scale_button, m_reset_rotation_button, m_drop_to_bed_button, m_check_inch, m_lock_bnt })
win->Enable(enadle);
}
void ObjectManipulation::DisableScale()
{
for (auto editor : m_editors)
editor->Enable(editor->has_opt_key("scale") || editor->has_opt_key("size") ? false : true);
for (wxWindow* win : std::initializer_list<wxWindow*>{ m_reset_scale_button, m_lock_bnt })
win->Enable(false);
}
void ObjectManipulation::update_ui_from_settings()
{
if (m_imperial_units != (wxGetApp().app_config->get("use_inches") == "1")) {

View File

@ -57,6 +57,8 @@ public:
void set_value(const wxString& new_value);
void kill_focus(ObjectManipulation *parent);
bool has_opt_key(const std::string& key) { return m_opt_key == key; }
private:
double get_value();
};
@ -173,6 +175,9 @@ public:
void Show(const bool show) override;
bool IsShown() override;
void UpdateAndShow(const bool show) override;
void Enable(const bool enadle = true);
void Disable() { Enable(false); }
void DisableScale();
void update_ui_from_settings();
bool use_colors() { return m_use_colors; }

View File

@ -5,6 +5,7 @@
#if ENABLE_GL_SHADERS_ATTRIBUTES
#include "slic3r/GUI/Plater.hpp"
#endif // ENABLE_GL_SHADERS_ATTRIBUTES
#include "libslic3r/Model.hpp"
#include <GL/glew.h>
@ -63,7 +64,13 @@ std::string GLGizmoMove3D::on_get_name() const
bool GLGizmoMove3D::on_is_activable() const
{
return !m_parent.get_selection().is_empty();
const Selection& selection = m_parent.get_selection();
if (selection.is_any_volume() || selection.is_any_modifier()) {
if (int obj_idx = selection.get_object_idx(); obj_idx >= 0)
return !m_parent.get_model()->objects[obj_idx]->is_cut();
}
return !selection.is_empty();
}
void GLGizmoMove3D::on_start_dragging()

View File

@ -9,6 +9,7 @@
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/Model.hpp"
#include "slic3r/GUI/Jobs/RotoptimizeJob.hpp"
@ -814,7 +815,13 @@ std::string GLGizmoRotate3D::on_get_name() const
bool GLGizmoRotate3D::on_is_activable() const
{
return !m_parent.get_selection().is_empty();
const Selection& selection = m_parent.get_selection();
if (selection.is_any_volume() || selection.is_any_modifier()) {
if (int obj_idx = selection.get_object_idx(); obj_idx >= 0)
return !m_parent.get_model()->objects[obj_idx]->is_cut();
}
return !selection.is_empty();
}
void GLGizmoRotate3D::on_start_dragging()

View File

@ -5,6 +5,7 @@
#if ENABLE_GL_SHADERS_ATTRIBUTES
#include "slic3r/GUI/Plater.hpp"
#endif // ENABLE_GL_SHADERS_ATTRIBUTES
#include "libslic3r/Model.hpp"
#include <GL/glew.h>
@ -136,6 +137,11 @@ std::string GLGizmoScale3D::on_get_name() const
bool GLGizmoScale3D::on_is_activable() const
{
const Selection& selection = m_parent.get_selection();
if (selection.is_any_volume() || selection.is_any_modifier()) {
if (int obj_idx = selection.get_object_idx(); obj_idx >= 0)
return !m_parent.get_model()->objects[obj_idx]->is_cut();
}
return !selection.is_empty() && !selection.is_wipe_tower();
}

View File

@ -4860,7 +4860,8 @@ bool Plater::priv::can_increase_instances() const
return false;
int obj_idx = get_selected_object_idx();
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size());
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size())
&& !model.objects[obj_idx]->is_cut();
}
bool Plater::priv::can_decrease_instances() const
@ -4870,7 +4871,8 @@ bool Plater::priv::can_decrease_instances() const
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);
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1)
&& !model.objects[obj_idx]->is_cut();
}
bool Plater::priv::can_split_to_objects() const