mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-15 01:45:55 +08:00
Cut WIP:
+ 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:
parent
f9e22513c1
commit
463e9ab530
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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")) {
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user