diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 307527fcbb..ea3d878888 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -33,8 +33,6 @@ // Changed algorithm to extract euler angles from rotation matrix #define ENABLE_NEW_EULER_ANGLES (1 && ENABLE_1_42_0_ALPHA4) -// Added minimum threshold for click and drag movements -#define ENABLE_MOVE_MIN_THRESHOLD (1 && ENABLE_1_42_0_ALPHA4) // Modified initial default placement of generic subparts #define ENABLE_GENERIC_SUBPARTS_PLACEMENT (1 && ENABLE_1_42_0_ALPHA4) // Bunch of fixes related to volumes centering @@ -58,4 +56,5 @@ // Toolbars and Gizmos use icons imported from svg files #define ENABLE_SVG_ICONS (1 && ENABLE_1_42_0_ALPHA8 && ENABLE_TEXTURES_FROM_SVG) + #endif // _technologies_h_ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 219f170823..ef60ab3fa9 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -28,6 +28,8 @@ set(SLIC3R_GUI_SOURCES GUI/GLCanvas3D.cpp GUI/GLCanvas3DManager.hpp GUI/GLCanvas3DManager.cpp + GUI/Selection.hpp + GUI/Selection.cpp GUI/Gizmos/GLGizmoBase.cpp GUI/Gizmos/GLGizmoBase.hpp GUI/Gizmos/GLGizmoMove.cpp diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 481dcb712d..7fb1ad25ed 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1,9 +1,9 @@ +#include "libslic3r/libslic3r.h" #include "slic3r/GUI/Gizmos/GLGizmos.hpp" #include "GLCanvas3D.hpp" #include "admesh/stl.h" #include "polypartition.h" -#include "libslic3r/libslic3r.h" #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/PrintConfig.hpp" #include "libslic3r/GCode/PreviewData.hpp" @@ -76,7 +76,6 @@ static const float DEFAULT_BG_DARK_COLOR[3] = { 0.478f, 0.478f, 0.478f }; static const float DEFAULT_BG_LIGHT_COLOR[3] = { 0.753f, 0.753f, 0.753f }; static const float ERROR_BG_DARK_COLOR[3] = { 0.478f, 0.192f, 0.039f }; static const float ERROR_BG_LIGHT_COLOR[3] = { 0.753f, 0.192f, 0.039f }; -static const float UNIFORM_SCALE_COLOR[3] = { 1.0f, 0.38f, 0.0f }; //static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } }; namespace Slic3r { @@ -716,1678 +715,25 @@ float GLCanvas3D::LayersEditing::reset_button_height(const GLCanvas3D &canvas) const Point GLCanvas3D::Mouse::Drag::Invalid_2D_Point(INT_MAX, INT_MAX); const Vec3d GLCanvas3D::Mouse::Drag::Invalid_3D_Point(DBL_MAX, DBL_MAX, DBL_MAX); -#if ENABLE_MOVE_MIN_THRESHOLD const int GLCanvas3D::Mouse::Drag::MoveThresholdPx = 5; -#endif // ENABLE_MOVE_MIN_THRESHOLD GLCanvas3D::Mouse::Drag::Drag() : start_position_2D(Invalid_2D_Point) , start_position_3D(Invalid_3D_Point) , move_volume_idx(-1) -#if ENABLE_MOVE_MIN_THRESHOLD , move_requires_threshold(false) , move_start_threshold_position_2D(Invalid_2D_Point) -#endif // ENABLE_MOVE_MIN_THRESHOLD { } GLCanvas3D::Mouse::Mouse() : dragging(false) - , left_down(false) , position(DBL_MAX, DBL_MAX) , scene_position(DBL_MAX, DBL_MAX, DBL_MAX) , ignore_up_event(false) { } -GLCanvas3D::Selection::VolumeCache::TransformCache::TransformCache() - : position(Vec3d::Zero()) - , rotation(Vec3d::Zero()) - , scaling_factor(Vec3d::Ones()) - , mirror(Vec3d::Ones()) - , rotation_matrix(Transform3d::Identity()) - , scale_matrix(Transform3d::Identity()) - , mirror_matrix(Transform3d::Identity()) - , full_matrix(Transform3d::Identity()) -{ -} - -GLCanvas3D::Selection::VolumeCache::TransformCache::TransformCache(const Geometry::Transformation& transform) - : position(transform.get_offset()) - , rotation(transform.get_rotation()) - , scaling_factor(transform.get_scaling_factor()) - , mirror(transform.get_mirror()) - , full_matrix(transform.get_matrix()) -{ - rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation); - scale_matrix = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scaling_factor); - mirror_matrix = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d::Ones(), mirror); -} - -GLCanvas3D::Selection::VolumeCache::VolumeCache(const Geometry::Transformation& volume_transform, const Geometry::Transformation& instance_transform) - : m_volume(volume_transform) - , m_instance(instance_transform) -{ -} - -GLCanvas3D::Selection::Selection() - : m_volumes(nullptr) - , m_model(nullptr) - , m_mode(Instance) - , m_type(Empty) - , m_valid(false) - , m_bounding_box_dirty(true) - , m_curved_arrow(16) - , m_scale_factor(1.0f) -{ -#if ENABLE_RENDER_SELECTION_CENTER - m_quadric = ::gluNewQuadric(); - if (m_quadric != nullptr) - ::gluQuadricDrawStyle(m_quadric, GLU_FILL); -#endif // ENABLE_RENDER_SELECTION_CENTER -} - -#if ENABLE_RENDER_SELECTION_CENTER -GLCanvas3D::Selection::~Selection() -{ - if (m_quadric != nullptr) - ::gluDeleteQuadric(m_quadric); -} -#endif // ENABLE_RENDER_SELECTION_CENTER - -void GLCanvas3D::Selection::set_volumes(GLVolumePtrs* volumes) -{ - m_volumes = volumes; - _update_valid(); -} - -bool GLCanvas3D::Selection::init(bool useVBOs) -{ - if (!m_arrow.init(useVBOs)) - return false; - - m_arrow.set_scale(5.0 * Vec3d::Ones()); - - if (!m_curved_arrow.init(useVBOs)) - return false; - - m_curved_arrow.set_scale(5.0 * Vec3d::Ones()); - return true; -} - -void GLCanvas3D::Selection::set_model(Model* model) -{ - m_model = model; - _update_valid(); -} - -void GLCanvas3D::Selection::add(unsigned int volume_idx, bool as_single_selection) -{ - if (!m_valid || ((unsigned int)m_volumes->size() <= volume_idx)) - return; - - const GLVolume* volume = (*m_volumes)[volume_idx]; - // wipe tower is already selected - if (is_wipe_tower() && volume->is_wipe_tower) - return; - - // resets the current list if needed - bool needs_reset = as_single_selection; - needs_reset |= volume->is_wipe_tower; - needs_reset |= is_wipe_tower() && !volume->is_wipe_tower; - needs_reset |= !is_modifier() && volume->is_modifier; - needs_reset |= is_modifier() && !volume->is_modifier; - - if (needs_reset) - clear(); - - if (volume->is_modifier) - m_mode = Volume; - else if (!contains_volume(volume_idx)) - m_mode = Instance; - // else -> keep current mode - - switch (m_mode) - { - case Volume: - { - if (volume->volume_idx() >= 0 && (is_empty() || (volume->instance_idx() == get_instance_idx()))) - _add_volume(volume_idx); - - break; - } - case Instance: - { - _add_instance(volume->object_idx(), volume->instance_idx()); - break; - } - } - - _update_type(); - m_bounding_box_dirty = true; -} - -void GLCanvas3D::Selection::remove(unsigned int volume_idx) -{ - if (!m_valid || ((unsigned int)m_volumes->size() <= volume_idx)) - return; - - GLVolume* volume = (*m_volumes)[volume_idx]; - - switch (m_mode) - { - case Volume: - { - _remove_volume(volume_idx); - break; - } - case Instance: - { - _remove_instance(volume->object_idx(), volume->instance_idx()); - break; - } - } - - _update_type(); - m_bounding_box_dirty = true; -} - -void GLCanvas3D::Selection::add_object(unsigned int object_idx, bool as_single_selection) -{ - if (!m_valid) - return; - - // resets the current list if needed - if (as_single_selection) - clear(); - - m_mode = Instance; - - _add_object(object_idx); - - _update_type(); - m_bounding_box_dirty = true; -} - -void GLCanvas3D::Selection::remove_object(unsigned int object_idx) -{ - if (!m_valid) - return; - - _remove_object(object_idx); - - _update_type(); - m_bounding_box_dirty = true; -} - -void GLCanvas3D::Selection::add_instance(unsigned int object_idx, unsigned int instance_idx, bool as_single_selection) -{ - if (!m_valid) - return; - - // resets the current list if needed - if (as_single_selection) - clear(); - - m_mode = Instance; - - _add_instance(object_idx, instance_idx); - - _update_type(); - m_bounding_box_dirty = true; -} - -void GLCanvas3D::Selection::remove_instance(unsigned int object_idx, unsigned int instance_idx) -{ - if (!m_valid) - return; - - _remove_instance(object_idx, instance_idx); - - _update_type(); - m_bounding_box_dirty = true; -} - -void GLCanvas3D::Selection::add_volume(unsigned int object_idx, unsigned int volume_idx, int instance_idx, bool as_single_selection) -{ - if (!m_valid) - return; - - // resets the current list if needed - if (as_single_selection) - clear(); - - m_mode = Volume; - - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) - { - GLVolume* v = (*m_volumes)[i]; - if ((v->object_idx() == object_idx) && (v->volume_idx() == volume_idx)) - { - if ((instance_idx != -1) && (v->instance_idx() == instance_idx)) - _add_volume(i); - } - } - - _update_type(); - m_bounding_box_dirty = true; -} - -void GLCanvas3D::Selection::remove_volume(unsigned int object_idx, unsigned int volume_idx) -{ - if (!m_valid) - return; - - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) - { - GLVolume* v = (*m_volumes)[i]; - if ((v->object_idx() == object_idx) && (v->volume_idx() == volume_idx)) - _remove_volume(i); - } - - _update_type(); - m_bounding_box_dirty = true; -} - -void GLCanvas3D::Selection::add_all() -{ - if (!m_valid) - return; - - m_mode = Instance; - clear(); - - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) - { - if (!(*m_volumes)[i]->is_wipe_tower) - _add_volume(i); - } - - _update_type(); - m_bounding_box_dirty = true; -} - -void GLCanvas3D::Selection::clear() -{ - if (!m_valid) - return; - - for (unsigned int i : m_list) - { - (*m_volumes)[i]->selected = false; - } - - m_list.clear(); - - _update_type(); - m_bounding_box_dirty = true; - - // resets the cache in the sidebar - wxGetApp().obj_manipul()->reset_cache(); -} - -// Update the selection based on the map from old indices to new indices after m_volumes changed. -// If the current selection is by instance, this call may select newly added volumes, if they belong to already selected instances. -void GLCanvas3D::Selection::volumes_changed(const std::vector &map_volume_old_to_new) -{ - assert(m_valid); - - // 1) Update the selection set. - IndicesList list_new; - std::vector> model_instances; - for (unsigned int idx : m_list) { - if (map_volume_old_to_new[idx] != size_t(-1)) { - unsigned int new_idx = (unsigned int)map_volume_old_to_new[idx]; - list_new.insert(new_idx); - if (m_mode == Instance) { - // Save the object_idx / instance_idx pair of selected old volumes, - // so we may add the newly added volumes of the same object_idx / instance_idx pair - // to the selection. - const GLVolume *volume = (*m_volumes)[new_idx]; - model_instances.emplace_back(volume->object_idx(), volume->instance_idx()); - } - } - } - m_list = std::move(list_new); - - if (! model_instances.empty()) { - // Instance selection mode. Add the newly added volumes of the same object_idx / instance_idx pair - // to the selection. - assert(m_mode == Instance); - sort_remove_duplicates(model_instances); - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++ i) { - const GLVolume* volume = (*m_volumes)[i]; - for (const std::pair &model_instance : model_instances) - if (volume->object_idx() == model_instance.first && volume->instance_idx() == model_instance.second) - this->_add_volume(i); - } - } - - _update_type(); - m_bounding_box_dirty = true; -} - -bool GLCanvas3D::Selection::is_single_full_instance() const -{ - if (m_type == SingleFullInstance) - return true; - - if (m_type == SingleFullObject) - return get_instance_idx() != -1; - - if (m_list.empty() || m_volumes->empty()) - return false; - - int object_idx = m_valid ? get_object_idx() : -1; - if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx)) - return false; - - int instance_idx = (*m_volumes)[*m_list.begin()]->instance_idx(); - - std::set volumes_idxs; - for (unsigned int i : m_list) - { - const GLVolume* v = (*m_volumes)[i]; - if ((object_idx != v->object_idx()) || (instance_idx != v->instance_idx())) - return false; - - int volume_idx = v->volume_idx(); - if (volume_idx >= 0) - volumes_idxs.insert(volume_idx); - } - - return m_model->objects[object_idx]->volumes.size() == volumes_idxs.size(); -} - -bool GLCanvas3D::Selection::is_from_single_object() const -{ - int idx = get_object_idx(); - return (0 <= idx) && (idx < 1000); -} - -bool GLCanvas3D::Selection::requires_uniform_scale() const -{ - if (is_single_full_instance() || is_single_modifier() || is_single_volume()) - return false; - - return true; -} - -int GLCanvas3D::Selection::get_object_idx() const -{ - return (m_cache.content.size() == 1) ? m_cache.content.begin()->first : -1; -} - -int GLCanvas3D::Selection::get_instance_idx() const -{ - if (m_cache.content.size() == 1) - { - const InstanceIdxsList& idxs = m_cache.content.begin()->second; - if (idxs.size() == 1) - return *idxs.begin(); - } - - return -1; -} - -const GLCanvas3D::Selection::InstanceIdxsList& GLCanvas3D::Selection::get_instance_idxs() const -{ - assert(m_cache.content.size() == 1); - return m_cache.content.begin()->second; -} - -const GLVolume* GLCanvas3D::Selection::get_volume(unsigned int volume_idx) const -{ - return (m_valid && (volume_idx < (unsigned int)m_volumes->size())) ? (*m_volumes)[volume_idx] : nullptr; -} - -const BoundingBoxf3& GLCanvas3D::Selection::get_bounding_box() const -{ - if (m_bounding_box_dirty) - _calc_bounding_box(); - - return m_bounding_box; -} - -void GLCanvas3D::Selection::start_dragging() -{ - if (!m_valid) - return; - - _set_caches(); -} - -void GLCanvas3D::Selection::translate(const Vec3d& displacement, bool local) -{ - if (!m_valid) - return; - - for (unsigned int i : m_list) - { - if ((m_mode == Volume) || (*m_volumes)[i]->is_wipe_tower) - { - if (local) - (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement); - else - { - Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; - (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement); - } - } - else if (m_mode == Instance) - (*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement); - } - -#if !DISABLE_INSTANCES_SYNCH - if (m_mode == Instance) - _synchronize_unselected_instances(SYNC_ROTATION_NONE); - else if (m_mode == Volume) - _synchronize_unselected_volumes(); -#endif // !DISABLE_INSTANCES_SYNCH - - m_bounding_box_dirty = true; -} - -static Eigen::Quaterniond rotation_xyz_diff(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) -{ - return - // From the current coordinate system to world. - Eigen::AngleAxisd(rot_xyz_to(2), Vec3d::UnitZ()) * Eigen::AngleAxisd(rot_xyz_to(1), Vec3d::UnitY()) * Eigen::AngleAxisd(rot_xyz_to(0), Vec3d::UnitX()) * - // From world to the initial coordinate system. - Eigen::AngleAxisd(-rot_xyz_from(0), Vec3d::UnitX()) * Eigen::AngleAxisd(-rot_xyz_from(1), Vec3d::UnitY()) * Eigen::AngleAxisd(-rot_xyz_from(2), Vec3d::UnitZ()); -} - -// This should only be called if it is known, that the two rotations only differ in rotation around the Z axis. -static double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) -{ - Eigen::AngleAxisd angle_axis(rotation_xyz_diff(rot_xyz_from, rot_xyz_to)); - Vec3d axis = angle_axis.axis(); - double angle = angle_axis.angle(); -#ifndef NDEBUG - if (std::abs(angle) > 1e-8) { - assert(std::abs(axis.x()) < 1e-8); - assert(std::abs(axis.y()) < 1e-8); - } -#endif /* NDEBUG */ - return (axis.z() < 0) ? -angle : angle; -} - -// Rotate an object around one of the axes. Only one rotation component is expected to be changing. -void GLCanvas3D::Selection::rotate(const Vec3d& rotation, GLCanvas3D::TransformationType transformation_type) -{ - if (!m_valid) - return; - - // Only relative rotation values are allowed in the world coordinate system. - assert(! transformation_type.world() || transformation_type.relative()); - - int rot_axis_max = 0; - if (rotation.isApprox(Vec3d::Zero())) - { - for (unsigned int i : m_list) - { - GLVolume &volume = *(*m_volumes)[i]; - if (m_mode == Instance) - { - volume.set_instance_rotation(m_cache.volumes_data[i].get_instance_rotation()); - volume.set_instance_offset(m_cache.volumes_data[i].get_instance_position()); - } - else if (m_mode == Volume) - { - volume.set_volume_rotation(m_cache.volumes_data[i].get_volume_rotation()); - volume.set_volume_offset(m_cache.volumes_data[i].get_volume_position()); - } - } - } - else - { - //FIXME this does not work for absolute rotations (transformation_type.absolute() is true) - rotation.cwiseAbs().maxCoeff(&rot_axis_max); - - // For generic rotation, we want to rotate the first volume in selection, and then to synchronize the other volumes with it. - std::vector object_instance_first(m_model->objects.size(), -1); - auto rotate_instance = [this, &rotation, &object_instance_first, rot_axis_max, transformation_type](GLVolume &volume, int i) { - int first_volume_idx = object_instance_first[volume.object_idx()]; - if (rot_axis_max != 2 && first_volume_idx != -1) { - // Generic rotation, but no rotation around the Z axis. - // Always do a local rotation (do not consider the selection to be a rigid body). - assert(is_approx(rotation.z(), 0.0)); - const GLVolume &first_volume = *(*m_volumes)[first_volume_idx]; - const Vec3d &rotation = first_volume.get_instance_rotation(); - double z_diff = rotation_diff_z(m_cache.volumes_data[first_volume_idx].get_instance_rotation(), m_cache.volumes_data[i].get_instance_rotation()); - volume.set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2) + z_diff)); - } else { - // extracts rotations from the composed transformation - Vec3d new_rotation = transformation_type.world() ? - Geometry::extract_euler_angles(Geometry::assemble_transform(Vec3d::Zero(), rotation) * m_cache.volumes_data[i].get_instance_rotation_matrix()) : - transformation_type.absolute() ? rotation : rotation + m_cache.volumes_data[i].get_instance_rotation(); - if (rot_axis_max == 2 && transformation_type.joint()) { - // Only allow rotation of multiple instances as a single rigid body when rotating around the Z axis. - Vec3d offset = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0.0, 0.0, new_rotation(2) - m_cache.volumes_data[i].get_instance_rotation()(2))) * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center); - volume.set_instance_offset(m_cache.dragging_center + offset); - } - volume.set_instance_rotation(new_rotation); - object_instance_first[volume.object_idx()] = i; - } - }; - - for (unsigned int i : m_list) - { - GLVolume &volume = *(*m_volumes)[i]; - if (is_single_full_instance()) - rotate_instance(volume, i); - else if (is_single_volume() || is_single_modifier()) - { - if (transformation_type.independent()) - volume.set_volume_rotation(volume.get_volume_rotation() + rotation); - else - { - Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); - Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); - volume.set_volume_rotation(new_rotation); - } - } - else - { - if (m_mode == Instance) - rotate_instance(volume, i); - else if (m_mode == Volume) - { - // extracts rotations from the composed transformation - Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); - Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); - if (transformation_type.joint()) - { - Vec3d local_pivot = m_cache.volumes_data[i].get_instance_full_matrix().inverse() * m_cache.dragging_center; - Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() - local_pivot); - volume.set_volume_offset(local_pivot + offset); - } - volume.set_volume_rotation(new_rotation); - } - } - } - } - -#if !DISABLE_INSTANCES_SYNCH - if (m_mode == Instance) - _synchronize_unselected_instances((rot_axis_max == 2) ? SYNC_ROTATION_NONE : SYNC_ROTATION_GENERAL); - else if (m_mode == Volume) - _synchronize_unselected_volumes(); -#endif // !DISABLE_INSTANCES_SYNCH - - m_bounding_box_dirty = true; -} - -void GLCanvas3D::Selection::flattening_rotate(const Vec3d& normal) -{ - // We get the normal in untransformed coordinates. We must transform it using the instance matrix, find out - // how to rotate the instance so it faces downwards and do the rotation. All that for all selected instances. - // The function assumes that is_from_single_object() holds. - - if (!m_valid) - return; - - for (unsigned int i : m_list) - { - Transform3d wst = m_cache.volumes_data[i].get_instance_scale_matrix(); - Vec3d scaling_factor = Vec3d(1./wst(0,0), 1./wst(1,1), 1./wst(2,2)); - - Transform3d wmt = m_cache.volumes_data[i].get_instance_mirror_matrix(); - Vec3d mirror(wmt(0,0), wmt(1,1), wmt(2,2)); - - Vec3d rotation = Geometry::extract_euler_angles(m_cache.volumes_data[i].get_instance_rotation_matrix()); - Vec3d transformed_normal = Geometry::assemble_transform(Vec3d::Zero(), rotation, scaling_factor, mirror) * normal; - transformed_normal.normalize(); - - Vec3d axis = transformed_normal(2) > 0.999f ? Vec3d(1., 0., 0.) : Vec3d(transformed_normal.cross(Vec3d(0., 0., -1.))); - axis.normalize(); - - Transform3d extra_rotation = Transform3d::Identity(); - extra_rotation.rotate(Eigen::AngleAxisd(acos(-transformed_normal(2)), axis)); - - Vec3d new_rotation = Geometry::extract_euler_angles(extra_rotation * m_cache.volumes_data[i].get_instance_rotation_matrix() ); - (*m_volumes)[i]->set_instance_rotation(new_rotation); - } - -#if !DISABLE_INSTANCES_SYNCH - // we want to synchronize z-rotation as well, otherwise the flattening behaves funny - // when applied on one of several identical instances - if (m_mode == Instance) - _synchronize_unselected_instances(SYNC_ROTATION_FULL); -#endif // !DISABLE_INSTANCES_SYNCH - - m_bounding_box_dirty = true; -} - -void GLCanvas3D::Selection::scale(const Vec3d& scale, bool local) -{ - if (!m_valid) - return; - - for (unsigned int i : m_list) - { - if (is_single_full_instance()) - (*m_volumes)[i]->set_instance_scaling_factor(scale); - else if (is_single_volume() || is_single_modifier()) - (*m_volumes)[i]->set_volume_scaling_factor(scale); - else - { - Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); - if (m_mode == Instance) - { - Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3); - // extracts scaling factors from the composed transformation - Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); - if (!local) - (*m_volumes)[i]->set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); - - (*m_volumes)[i]->set_instance_scaling_factor(new_scale); - } - else if (m_mode == Volume) - { - Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_volume_scale_matrix()).matrix().block(0, 0, 3, 3); - // extracts scaling factors from the composed transformation - Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); - if (!local) - { - Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() + m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center); - (*m_volumes)[i]->set_volume_offset(m_cache.dragging_center - m_cache.volumes_data[i].get_instance_position() + offset); - } - (*m_volumes)[i]->set_volume_scaling_factor(new_scale); - } - } - } - -#if !DISABLE_INSTANCES_SYNCH - if (m_mode == Instance) - _synchronize_unselected_instances(SYNC_ROTATION_NONE); - else if (m_mode == Volume) - _synchronize_unselected_volumes(); -#endif // !DISABLE_INSTANCES_SYNCH - - _ensure_on_bed(); - - m_bounding_box_dirty = true; -} - -void GLCanvas3D::Selection::mirror(Axis axis) -{ - if (!m_valid) - return; - - bool single_full_instance = is_single_full_instance(); - - for (unsigned int i : m_list) - { - if (single_full_instance) - (*m_volumes)[i]->set_instance_mirror(axis, -(*m_volumes)[i]->get_instance_mirror(axis)); - else if (m_mode == Volume) - (*m_volumes)[i]->set_volume_mirror(axis, -(*m_volumes)[i]->get_volume_mirror(axis)); - } - -#if !DISABLE_INSTANCES_SYNCH - if (m_mode == Instance) - _synchronize_unselected_instances(SYNC_ROTATION_NONE); - else if (m_mode == Volume) - _synchronize_unselected_volumes(); -#endif // !DISABLE_INSTANCES_SYNCH - - m_bounding_box_dirty = true; -} - -void GLCanvas3D::Selection::translate(unsigned int object_idx, const Vec3d& displacement) -{ - if (!m_valid) - return; - - for (unsigned int i : m_list) - { - GLVolume* v = (*m_volumes)[i]; - if (v->object_idx() == object_idx) - v->set_instance_offset(v->get_instance_offset() + displacement); - } - - std::set done; // prevent processing volumes twice - done.insert(m_list.begin(), m_list.end()); - - for (unsigned int i : m_list) - { - if (done.size() == m_volumes->size()) - break; - - int object_idx = (*m_volumes)[i]->object_idx(); - if (object_idx >= 1000) - continue; - - // Process unselected volumes of the object. - for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) - { - if (done.size() == m_volumes->size()) - break; - - if (done.find(j) != done.end()) - continue; - - GLVolume* v = (*m_volumes)[j]; - if (v->object_idx() != object_idx) - continue; - - v->set_instance_offset(v->get_instance_offset() + displacement); - done.insert(j); - } - } - - m_bounding_box_dirty = true; -} - -void GLCanvas3D::Selection::translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement) -{ - if (!m_valid) - return; - - for (unsigned int i : m_list) - { - GLVolume* v = (*m_volumes)[i]; - if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) - v->set_instance_offset(v->get_instance_offset() + displacement); - } - - std::set done; // prevent processing volumes twice - done.insert(m_list.begin(), m_list.end()); - - for (unsigned int i : m_list) - { - if (done.size() == m_volumes->size()) - break; - - int object_idx = (*m_volumes)[i]->object_idx(); - if (object_idx >= 1000) - continue; - - // Process unselected volumes of the object. - for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) - { - if (done.size() == m_volumes->size()) - break; - - if (done.find(j) != done.end()) - continue; - - GLVolume* v = (*m_volumes)[j]; - if ((v->object_idx() != object_idx) || (v->instance_idx() != instance_idx)) - continue; - - v->set_instance_offset(v->get_instance_offset() + displacement); - done.insert(j); - } - } - - m_bounding_box_dirty = true; -} - -void GLCanvas3D::Selection::erase() -{ - if (!m_valid) - return; - - if (is_single_full_object()) - wxGetApp().obj_list()->delete_from_model_and_list(ItemType::itObject, get_object_idx(), 0); - else if (is_multiple_full_object()) - { - std::vector items; - items.reserve(m_cache.content.size()); - for (ObjectIdxsToInstanceIdxsMap::iterator it = m_cache.content.begin(); it != m_cache.content.end(); ++it) - { - items.emplace_back(ItemType::itObject, it->first, 0); - } - wxGetApp().obj_list()->delete_from_model_and_list(items); - } - else if (is_multiple_full_instance()) - { - std::set> instances_idxs; - for (ObjectIdxsToInstanceIdxsMap::iterator obj_it = m_cache.content.begin(); obj_it != m_cache.content.end(); ++obj_it) - { - for (InstanceIdxsList::reverse_iterator inst_it = obj_it->second.rbegin(); inst_it != obj_it->second.rend(); ++inst_it) - { - instances_idxs.insert(std::make_pair(obj_it->first, *inst_it)); - } - } - - std::vector items; - items.reserve(instances_idxs.size()); - for (const std::pair& i : instances_idxs) - { - items.emplace_back(ItemType::itInstance, i.first, i.second); - } - wxGetApp().obj_list()->delete_from_model_and_list(items); - } - else if (is_single_full_instance()) - wxGetApp().obj_list()->delete_from_model_and_list(ItemType::itInstance, get_object_idx(), get_instance_idx()); - else if (is_mixed()) - { - std::set items_set; - std::map volumes_in_obj; - - for (auto i : m_list) { - const auto gl_vol = (*m_volumes)[i]; - const auto glv_obj_idx = gl_vol->object_idx(); - const auto model_object = m_model->objects[glv_obj_idx]; - - if (model_object->instances.size() == 1) { - if (model_object->volumes.size() == 1) - items_set.insert(ItemForDelete(ItemType::itObject, glv_obj_idx, -1)); - else { - items_set.insert(ItemForDelete(ItemType::itVolume, glv_obj_idx, gl_vol->volume_idx())); - int idx = (volumes_in_obj.find(glv_obj_idx) == volumes_in_obj.end()) ? 0 : volumes_in_obj.at(glv_obj_idx); - volumes_in_obj[glv_obj_idx] = ++idx; - } - continue; - } - - const auto glv_ins_idx = gl_vol->instance_idx(); - - for (auto obj_ins : m_cache.content) { - if (obj_ins.first == glv_obj_idx) { - if (obj_ins.second.find(glv_ins_idx) != obj_ins.second.end()) { - if (obj_ins.second.size() == model_object->instances.size()) - items_set.insert(ItemForDelete(ItemType::itVolume, glv_obj_idx, gl_vol->volume_idx())); - else - items_set.insert(ItemForDelete(ItemType::itInstance, glv_obj_idx, glv_ins_idx)); - - break; - } - } - } - } - - std::vector items; - items.reserve(items_set.size()); - for (const ItemForDelete& i : items_set) { - if (i.type == ItemType::itVolume ) { - const int vol_in_obj_cnt = volumes_in_obj.find(i.obj_idx) == volumes_in_obj.end() ? 0 : volumes_in_obj.at(i.obj_idx); - if (vol_in_obj_cnt == m_model->objects[i.obj_idx]->volumes.size()) { - if (i.sub_obj_idx == vol_in_obj_cnt - 1) - items.emplace_back(ItemType::itObject, i.obj_idx, 0); - continue; - } - } - items.emplace_back(i.type, i.obj_idx, i.sub_obj_idx); - } - - wxGetApp().obj_list()->delete_from_model_and_list(items); - } - else - { - std::set> volumes_idxs; - for (unsigned int i : m_list) - { - const GLVolume* v = (*m_volumes)[i]; - // Only remove volumes associated with ModelVolumes from the object list. - // Temporary meshes (SLA supports or pads) are not managed by the object list. - if (v->volume_idx() >= 0) - volumes_idxs.insert(std::make_pair(v->object_idx(), v->volume_idx())); - } - - std::vector items; - items.reserve(volumes_idxs.size()); - for (const std::pair& v : volumes_idxs) - { - items.emplace_back(ItemType::itVolume, v.first, v.second); - } - - wxGetApp().obj_list()->delete_from_model_and_list(items); - } -} - -void GLCanvas3D::Selection::render(float scale_factor) const -{ - if (!m_valid || is_empty()) - return; - - m_scale_factor = scale_factor; - - // render cumulative bounding box of selected volumes - _render_selected_volumes(); - _render_synchronized_volumes(); -} - -#if ENABLE_RENDER_SELECTION_CENTER -void GLCanvas3D::Selection::render_center() const -{ - if (!m_valid || is_empty() || (m_quadric == nullptr)) - return; - - const Vec3d& center = get_bounding_box().center(); - - ::glDisable(GL_DEPTH_TEST); - - ::glEnable(GL_LIGHTING); - - ::glColor3f(1.0f, 1.0f, 1.0f); - ::glPushMatrix(); - ::glTranslated(center(0), center(1), center(2)); - ::gluSphere(m_quadric, 0.75, 32, 32); - ::glPopMatrix(); - - ::glDisable(GL_LIGHTING); -} -#endif // ENABLE_RENDER_SELECTION_CENTER - -void GLCanvas3D::Selection::render_sidebar_hints(const std::string& sidebar_field) const -{ - if (sidebar_field.empty()) - return; - - ::glClear(GL_DEPTH_BUFFER_BIT); - ::glEnable(GL_DEPTH_TEST); - - ::glEnable(GL_LIGHTING); - - ::glPushMatrix(); - - const Vec3d& center = get_bounding_box().center(); - - if (is_single_full_instance()) - { - ::glTranslated(center(0), center(1), center(2)); - if (!boost::starts_with(sidebar_field, "position")) - { - Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); - ::glMultMatrixd(orient_matrix.data()); - } - } - else if (is_single_volume() || is_single_modifier()) - { - ::glTranslated(center(0), center(1), center(2)); - Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); - if (!boost::starts_with(sidebar_field, "position")) - orient_matrix = orient_matrix * (*m_volumes)[*m_list.begin()]->get_volume_transformation().get_matrix(true, false, true, true); - - ::glMultMatrixd(orient_matrix.data()); - } - else - { - ::glTranslated(center(0), center(1), center(2)); - if (requires_local_axes()) - { - Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); - ::glMultMatrixd(orient_matrix.data()); - } - } - - if (boost::starts_with(sidebar_field, "position")) - _render_sidebar_position_hints(sidebar_field); - else if (boost::starts_with(sidebar_field, "rotation")) - _render_sidebar_rotation_hints(sidebar_field); - else if (boost::starts_with(sidebar_field, "scale")) - _render_sidebar_scale_hints(sidebar_field); - else if (boost::starts_with(sidebar_field, "size")) - _render_sidebar_size_hints(sidebar_field); - - ::glPopMatrix(); - - ::glDisable(GL_LIGHTING); -} - -bool GLCanvas3D::Selection::requires_local_axes() const -{ - return (m_mode == Volume) && is_from_single_instance(); -} - -void GLCanvas3D::Selection::_update_valid() -{ - m_valid = (m_volumes != nullptr) && (m_model != nullptr); -} - -void GLCanvas3D::Selection::_update_type() -{ - m_cache.content.clear(); - m_type = Mixed; - - for (unsigned int i : m_list) - { - const GLVolume* volume = (*m_volumes)[i]; - int obj_idx = volume->object_idx(); - int inst_idx = volume->instance_idx(); - ObjectIdxsToInstanceIdxsMap::iterator obj_it = m_cache.content.find(obj_idx); - if (obj_it == m_cache.content.end()) - obj_it = m_cache.content.insert(ObjectIdxsToInstanceIdxsMap::value_type(obj_idx, InstanceIdxsList())).first; - - obj_it->second.insert(inst_idx); - } - - bool requires_disable = false; - - if (!m_valid) - m_type = Invalid; - else - { - if (m_list.empty()) - m_type = Empty; - else if (m_list.size() == 1) - { - const GLVolume* first = (*m_volumes)[*m_list.begin()]; - if (first->is_wipe_tower) - m_type = WipeTower; - else if (first->is_modifier) - { - m_type = SingleModifier; - requires_disable = true; - } - else - { - const ModelObject* model_object = m_model->objects[first->object_idx()]; - unsigned int volumes_count = (unsigned int)model_object->volumes.size(); - unsigned int instances_count = (unsigned int)model_object->instances.size(); - if (volumes_count * instances_count == 1) - { - m_type = SingleFullObject; - // ensures the correct mode is selected - m_mode = Instance; - } - else if (volumes_count == 1) // instances_count > 1 - { - m_type = SingleFullInstance; - // ensures the correct mode is selected - m_mode = Instance; - } - else - { - m_type = SingleVolume; - requires_disable = true; - } - } - } - else - { - if (m_cache.content.size() == 1) // single object - { - const ModelObject* model_object = m_model->objects[m_cache.content.begin()->first]; - unsigned int model_volumes_count = (unsigned int)model_object->volumes.size(); - unsigned int sla_volumes_count = 0; - for (unsigned int i : m_list) - { - if ((*m_volumes)[i]->volume_idx() < 0) - ++sla_volumes_count; - } - unsigned int volumes_count = model_volumes_count + sla_volumes_count; - unsigned int instances_count = (unsigned int)model_object->instances.size(); - unsigned int selected_instances_count = (unsigned int)m_cache.content.begin()->second.size(); - if (volumes_count * instances_count == (unsigned int)m_list.size()) - { - m_type = SingleFullObject; - // ensures the correct mode is selected - m_mode = Instance; - } - else if (selected_instances_count == 1) - { - if (volumes_count == (unsigned int)m_list.size()) - { - m_type = SingleFullInstance; - // ensures the correct mode is selected - m_mode = Instance; - } - else - { - unsigned int modifiers_count = 0; - for (unsigned int i : m_list) - { - if ((*m_volumes)[i]->is_modifier) - ++modifiers_count; - } - - if (modifiers_count == 0) - { - m_type = MultipleVolume; - requires_disable = true; - } - else if (modifiers_count == (unsigned int)m_list.size()) - { - m_type = MultipleModifier; - requires_disable = true; - } - } - } - else if ((selected_instances_count > 1) && (selected_instances_count * volumes_count == (unsigned int)m_list.size())) - { - m_type = MultipleFullInstance; - // ensures the correct mode is selected - m_mode = Instance; - } - } - else - { - int sels_cntr = 0; - for (ObjectIdxsToInstanceIdxsMap::iterator it = m_cache.content.begin(); it != m_cache.content.end(); ++it) - { - const ModelObject* model_object = m_model->objects[it->first]; - unsigned int volumes_count = (unsigned int)model_object->volumes.size(); - unsigned int instances_count = (unsigned int)model_object->instances.size(); - sels_cntr += volumes_count * instances_count; - } - if (sels_cntr == (unsigned int)m_list.size()) - { - m_type = MultipleFullObject; - // ensures the correct mode is selected - m_mode = Instance; - } - } - } - } - - int object_idx = get_object_idx(); - int instance_idx = get_instance_idx(); - for (GLVolume* v : *m_volumes) - { - v->disabled = requires_disable ? (v->object_idx() != object_idx) || (v->instance_idx() != instance_idx) : false; - } - -#if ENABLE_SELECTION_DEBUG_OUTPUT - std::cout << "Selection: "; - std::cout << "mode: "; - switch (m_mode) - { - case Volume: - { - std::cout << "Volume"; - break; - } - case Instance: - { - std::cout << "Instance"; - break; - } - } - - std::cout << " - type: "; - - switch (m_type) - { - case Invalid: - { - std::cout << "Invalid" << std::endl; - break; - } - case Empty: - { - std::cout << "Empty" << std::endl; - break; - } - case WipeTower: - { - std::cout << "WipeTower" << std::endl; - break; - } - case SingleModifier: - { - std::cout << "SingleModifier" << std::endl; - break; - } - case MultipleModifier: - { - std::cout << "MultipleModifier" << std::endl; - break; - } - case SingleVolume: - { - std::cout << "SingleVolume" << std::endl; - break; - } - case MultipleVolume: - { - std::cout << "MultipleVolume" << std::endl; - break; - } - case SingleFullObject: - { - std::cout << "SingleFullObject" << std::endl; - break; - } - case MultipleFullObject: - { - std::cout << "MultipleFullObject" << std::endl; - break; - } - case SingleFullInstance: - { - std::cout << "SingleFullInstance" << std::endl; - break; - } - case MultipleFullInstance: - { - std::cout << "MultipleFullInstance" << std::endl; - break; - } - case Mixed: - { - std::cout << "Mixed" << std::endl; - break; - } - } -#endif // ENABLE_SELECTION_DEBUG_OUTPUT -} - -void GLCanvas3D::Selection::_set_caches() -{ - m_cache.volumes_data.clear(); - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) - { - const GLVolume* v = (*m_volumes)[i]; - m_cache.volumes_data.emplace(i, VolumeCache(v->get_volume_transformation(), v->get_instance_transformation())); - } - m_cache.dragging_center = get_bounding_box().center(); -} - -void GLCanvas3D::Selection::_add_volume(unsigned int volume_idx) -{ - m_list.insert(volume_idx); - (*m_volumes)[volume_idx]->selected = true; -} - -void GLCanvas3D::Selection::_add_instance(unsigned int object_idx, unsigned int instance_idx) -{ - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) - { - GLVolume* v = (*m_volumes)[i]; - if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) - _add_volume(i); - } -} - -void GLCanvas3D::Selection::_add_object(unsigned int object_idx) -{ - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) - { - GLVolume* v = (*m_volumes)[i]; - if (v->object_idx() == object_idx) - _add_volume(i); - } -} - -void GLCanvas3D::Selection::_remove_volume(unsigned int volume_idx) -{ - IndicesList::iterator v_it = m_list.find(volume_idx); - if (v_it == m_list.end()) - return; - - m_list.erase(v_it); - - (*m_volumes)[volume_idx]->selected = false; -} - -void GLCanvas3D::Selection::_remove_instance(unsigned int object_idx, unsigned int instance_idx) -{ - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) - { - GLVolume* v = (*m_volumes)[i]; - if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) - _remove_volume(i); - } -} - -void GLCanvas3D::Selection::_remove_object(unsigned int object_idx) -{ - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) - { - GLVolume* v = (*m_volumes)[i]; - if (v->object_idx() == object_idx) - _remove_volume(i); - } -} - -void GLCanvas3D::Selection::_calc_bounding_box() const -{ - m_bounding_box = BoundingBoxf3(); - if (m_valid) - { - for (unsigned int i : m_list) - { - m_bounding_box.merge((*m_volumes)[i]->transformed_convex_hull_bounding_box()); - } - } - m_bounding_box_dirty = false; -} - -void GLCanvas3D::Selection::_render_selected_volumes() const -{ - float color[3] = { 1.0f, 1.0f, 1.0f }; - _render_bounding_box(get_bounding_box(), color); -} - -void GLCanvas3D::Selection::_render_synchronized_volumes() const -{ - if (m_mode == Instance) - return; - - float color[3] = { 1.0f, 1.0f, 0.0f }; - - for (unsigned int i : m_list) - { - const GLVolume* volume = (*m_volumes)[i]; - int object_idx = volume->object_idx(); - int instance_idx = volume->instance_idx(); - int volume_idx = volume->volume_idx(); - for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) - { - if (i == j) - continue; - - const GLVolume* v = (*m_volumes)[j]; - if ((v->object_idx() != object_idx) || (v->volume_idx() != volume_idx)) - continue; - - _render_bounding_box(v->transformed_convex_hull_bounding_box(), color); - } - } -} - -void GLCanvas3D::Selection::_render_bounding_box(const BoundingBoxf3& box, float* color) const -{ - if (color == nullptr) - return; - - Vec3f b_min = box.min.cast(); - Vec3f b_max = box.max.cast(); - Vec3f size = 0.2f * box.size().cast(); - - ::glEnable(GL_DEPTH_TEST); - - ::glColor3fv(color); - ::glLineWidth(2.0f * m_scale_factor); - - ::glBegin(GL_LINES); - - ::glVertex3f(b_min(0), b_min(1), b_min(2)); ::glVertex3f(b_min(0) + size(0), b_min(1), b_min(2)); - ::glVertex3f(b_min(0), b_min(1), b_min(2)); ::glVertex3f(b_min(0), b_min(1) + size(1), b_min(2)); - ::glVertex3f(b_min(0), b_min(1), b_min(2)); ::glVertex3f(b_min(0), b_min(1), b_min(2) + size(2)); - - ::glVertex3f(b_max(0), b_min(1), b_min(2)); ::glVertex3f(b_max(0) - size(0), b_min(1), b_min(2)); - ::glVertex3f(b_max(0), b_min(1), b_min(2)); ::glVertex3f(b_max(0), b_min(1) + size(1), b_min(2)); - ::glVertex3f(b_max(0), b_min(1), b_min(2)); ::glVertex3f(b_max(0), b_min(1), b_min(2) + size(2)); - - ::glVertex3f(b_max(0), b_max(1), b_min(2)); ::glVertex3f(b_max(0) - size(0), b_max(1), b_min(2)); - ::glVertex3f(b_max(0), b_max(1), b_min(2)); ::glVertex3f(b_max(0), b_max(1) - size(1), b_min(2)); - ::glVertex3f(b_max(0), b_max(1), b_min(2)); ::glVertex3f(b_max(0), b_max(1), b_min(2) + size(2)); - - ::glVertex3f(b_min(0), b_max(1), b_min(2)); ::glVertex3f(b_min(0) + size(0), b_max(1), b_min(2)); - ::glVertex3f(b_min(0), b_max(1), b_min(2)); ::glVertex3f(b_min(0), b_max(1) - size(1), b_min(2)); - ::glVertex3f(b_min(0), b_max(1), b_min(2)); ::glVertex3f(b_min(0), b_max(1), b_min(2) + size(2)); - - ::glVertex3f(b_min(0), b_min(1), b_max(2)); ::glVertex3f(b_min(0) + size(0), b_min(1), b_max(2)); - ::glVertex3f(b_min(0), b_min(1), b_max(2)); ::glVertex3f(b_min(0), b_min(1) + size(1), b_max(2)); - ::glVertex3f(b_min(0), b_min(1), b_max(2)); ::glVertex3f(b_min(0), b_min(1), b_max(2) - size(2)); - - ::glVertex3f(b_max(0), b_min(1), b_max(2)); ::glVertex3f(b_max(0) - size(0), b_min(1), b_max(2)); - ::glVertex3f(b_max(0), b_min(1), b_max(2)); ::glVertex3f(b_max(0), b_min(1) + size(1), b_max(2)); - ::glVertex3f(b_max(0), b_min(1), b_max(2)); ::glVertex3f(b_max(0), b_min(1), b_max(2) - size(2)); - - ::glVertex3f(b_max(0), b_max(1), b_max(2)); ::glVertex3f(b_max(0) - size(0), b_max(1), b_max(2)); - ::glVertex3f(b_max(0), b_max(1), b_max(2)); ::glVertex3f(b_max(0), b_max(1) - size(1), b_max(2)); - ::glVertex3f(b_max(0), b_max(1), b_max(2)); ::glVertex3f(b_max(0), b_max(1), b_max(2) - size(2)); - - ::glVertex3f(b_min(0), b_max(1), b_max(2)); ::glVertex3f(b_min(0) + size(0), b_max(1), b_max(2)); - ::glVertex3f(b_min(0), b_max(1), b_max(2)); ::glVertex3f(b_min(0), b_max(1) - size(1), b_max(2)); - ::glVertex3f(b_min(0), b_max(1), b_max(2)); ::glVertex3f(b_min(0), b_max(1), b_max(2) - size(2)); - - ::glEnd(); -} - -void GLCanvas3D::Selection::_render_sidebar_position_hints(const std::string& sidebar_field) const -{ - if (boost::ends_with(sidebar_field, "x")) - { - ::glRotated(-90.0, 0.0, 0.0, 1.0); - _render_sidebar_position_hint(X); - } - else if (boost::ends_with(sidebar_field, "y")) - _render_sidebar_position_hint(Y); - else if (boost::ends_with(sidebar_field, "z")) - { - ::glRotated(90.0, 1.0, 0.0, 0.0); - _render_sidebar_position_hint(Z); - } -} - -void GLCanvas3D::Selection::_render_sidebar_rotation_hints(const std::string& sidebar_field) const -{ - if (boost::ends_with(sidebar_field, "x")) - { - ::glRotated(90.0, 0.0, 1.0, 0.0); - _render_sidebar_rotation_hint(X); - } - else if (boost::ends_with(sidebar_field, "y")) - { - ::glRotated(-90.0, 1.0, 0.0, 0.0); - _render_sidebar_rotation_hint(Y); - } - else if (boost::ends_with(sidebar_field, "z")) - _render_sidebar_rotation_hint(Z); -} - -void GLCanvas3D::Selection::_render_sidebar_scale_hints(const std::string& sidebar_field) const -{ - bool uniform_scale = requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling(); - - if (boost::ends_with(sidebar_field, "x") || uniform_scale) - { - ::glPushMatrix(); - ::glRotated(-90.0, 0.0, 0.0, 1.0); - _render_sidebar_scale_hint(X); - ::glPopMatrix(); - } - - if (boost::ends_with(sidebar_field, "y") || uniform_scale) - { - ::glPushMatrix(); - _render_sidebar_scale_hint(Y); - ::glPopMatrix(); - } - - if (boost::ends_with(sidebar_field, "z") || uniform_scale) - { - ::glPushMatrix(); - ::glRotated(90.0, 1.0, 0.0, 0.0); - _render_sidebar_scale_hint(Z); - ::glPopMatrix(); - } -} - -void GLCanvas3D::Selection::_render_sidebar_size_hints(const std::string& sidebar_field) const -{ - _render_sidebar_scale_hints(sidebar_field); -} - -void GLCanvas3D::Selection::_render_sidebar_position_hint(Axis axis) const -{ - m_arrow.set_color(AXES_COLOR[axis], 3); - m_arrow.render(); -} - -void GLCanvas3D::Selection::_render_sidebar_rotation_hint(Axis axis) const -{ - m_curved_arrow.set_color(AXES_COLOR[axis], 3); - m_curved_arrow.render(); - - ::glRotated(180.0, 0.0, 0.0, 1.0); - m_curved_arrow.render(); -} - -void GLCanvas3D::Selection::_render_sidebar_scale_hint(Axis axis) const -{ - m_arrow.set_color(((requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling()) ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis]), 3); - - ::glTranslated(0.0, 5.0, 0.0); - m_arrow.render(); - - ::glTranslated(0.0, -10.0, 0.0); - ::glRotated(180.0, 0.0, 0.0, 1.0); - m_arrow.render(); -} - -void GLCanvas3D::Selection::_render_sidebar_size_hint(Axis axis, double length) const -{ -} - -#ifndef NDEBUG -static bool is_rotation_xy_synchronized(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) -{ - Eigen::AngleAxisd angle_axis(rotation_xyz_diff(rot_xyz_from, rot_xyz_to)); - Vec3d axis = angle_axis.axis(); - double angle = angle_axis.angle(); - if (std::abs(angle) < 1e-8) - return true; - assert(std::abs(axis.x()) < 1e-8); - assert(std::abs(axis.y()) < 1e-8); - assert(std::abs(std::abs(axis.z()) - 1.) < 1e-8); - return std::abs(axis.x()) < 1e-8 && std::abs(axis.y()) < 1e-8 && std::abs(std::abs(axis.z()) - 1.) < 1e-8; -} -static void verify_instances_rotation_synchronized(const Model &model, const GLVolumePtrs &volumes) -{ - for (size_t idx_object = 0; idx_object < model.objects.size(); ++ idx_object) { - int idx_volume_first = -1; - for (int i = 0; i < (int)volumes.size(); ++ i) { - if (volumes[i]->object_idx() == idx_object) { - idx_volume_first = i; - break; - } - } - assert(idx_volume_first != -1); // object without instances? - if (idx_volume_first == -1) - continue; - const Vec3d &rotation0 = volumes[idx_volume_first]->get_instance_rotation(); - for (int i = idx_volume_first + 1; i < (int)volumes.size(); ++ i) - if (volumes[i]->object_idx() == idx_object) { - const Vec3d &rotation = volumes[i]->get_instance_rotation(); - assert(is_rotation_xy_synchronized(rotation, rotation0)); - } - } -} -#endif /* NDEBUG */ - -void GLCanvas3D::Selection::_synchronize_unselected_instances(SyncRotationType sync_rotation_type) -{ - std::set done; // prevent processing volumes twice - done.insert(m_list.begin(), m_list.end()); - - for (unsigned int i : m_list) - { - if (done.size() == m_volumes->size()) - break; - - const GLVolume* volume = (*m_volumes)[i]; - int object_idx = volume->object_idx(); - if (object_idx >= 1000) - continue; - - int instance_idx = volume->instance_idx(); - const Vec3d& rotation = volume->get_instance_rotation(); - const Vec3d& scaling_factor = volume->get_instance_scaling_factor(); - const Vec3d& mirror = volume->get_instance_mirror(); - - // Process unselected instances. - for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) - { - if (done.size() == m_volumes->size()) - break; - - if (done.find(j) != done.end()) - continue; - - GLVolume* v = (*m_volumes)[j]; - if ((v->object_idx() != object_idx) || (v->instance_idx() == instance_idx)) - continue; - - assert(is_rotation_xy_synchronized(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation())); - switch (sync_rotation_type) { - case SYNC_ROTATION_NONE: - // z only rotation -> keep instance z - // The X,Y rotations should be synchronized from start to end of the rotation. - assert(is_rotation_xy_synchronized(rotation, v->get_instance_rotation())); - break; - case SYNC_ROTATION_FULL: - // rotation comes from place on face -> force given z - v->set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2))); - break; - case SYNC_ROTATION_GENERAL: - // generic rotation -> update instance z with the delta of the rotation. - double z_diff = rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation()); - v->set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2) + z_diff)); - break; - } - - v->set_instance_scaling_factor(scaling_factor); - v->set_instance_mirror(mirror); - - done.insert(j); - } - } - -#ifndef NDEBUG - verify_instances_rotation_synchronized(*m_model, *m_volumes); -#endif /* NDEBUG */ -} - -void GLCanvas3D::Selection::_synchronize_unselected_volumes() -{ - for (unsigned int i : m_list) - { - const GLVolume* volume = (*m_volumes)[i]; - int object_idx = volume->object_idx(); - if (object_idx >= 1000) - continue; - - int volume_idx = volume->volume_idx(); - const Vec3d& offset = volume->get_volume_offset(); - const Vec3d& rotation = volume->get_volume_rotation(); - const Vec3d& scaling_factor = volume->get_volume_scaling_factor(); - const Vec3d& mirror = volume->get_volume_mirror(); - - // Process unselected volumes. - for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) - { - if (j == i) - continue; - - GLVolume* v = (*m_volumes)[j]; - if ((v->object_idx() != object_idx) || (v->volume_idx() != volume_idx)) - continue; - - v->set_volume_offset(offset); - v->set_volume_rotation(rotation); - v->set_volume_scaling_factor(scaling_factor); - v->set_volume_mirror(mirror); - } - } -} - -void GLCanvas3D::Selection::_ensure_on_bed() -{ - typedef std::map, double> InstancesToZMap; - InstancesToZMap instances_min_z; - - for (GLVolume* volume : *m_volumes) - { - if (!volume->is_wipe_tower && !volume->is_modifier) - { - double min_z = volume->transformed_convex_hull_bounding_box().min(2); - std::pair instance = std::make_pair(volume->object_idx(), volume->instance_idx()); - InstancesToZMap::iterator it = instances_min_z.find(instance); - if (it == instances_min_z.end()) - it = instances_min_z.insert(InstancesToZMap::value_type(instance, DBL_MAX)).first; - - it->second = std::min(it->second, min_z); - } - } - - for (GLVolume* volume : *m_volumes) - { - std::pair instance = std::make_pair(volume->object_idx(), volume->instance_idx()); - InstancesToZMap::iterator it = instances_min_z.find(instance); - if (it != instances_min_z.end()) - volume->set_instance_offset(Z, volume->get_instance_offset(Z) - it->second); - } -} - #if ENABLE_SVG_ICONS const float GLCanvas3D::Gizmos::Default_Icons_Size = 64; #endif // ENABLE_SVG_ICONS @@ -2576,7 +922,7 @@ void GLCanvas3D::Gizmos::set_overlay_scale(float scale) #endif // ENABLE_SVG_ICONS } -std::string GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const GLCanvas3D::Selection& selection) +std::string GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection) { std::string name = ""; @@ -2622,7 +968,7 @@ std::string GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, con return name; } -void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const GLCanvas3D::Selection& selection) +void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection) { if (!m_enabled) return; @@ -2860,7 +1206,7 @@ bool GLCanvas3D::Gizmos::is_dragging() const return (curr != nullptr) ? curr->is_dragging() : false; } -void GLCanvas3D::Gizmos::start_dragging(const GLCanvas3D::Selection& selection) +void GLCanvas3D::Gizmos::start_dragging(const Selection& selection) { if (!m_enabled) return; @@ -2946,7 +1292,7 @@ void GLCanvas3D::Gizmos::set_flattening_data(const ModelObject* model_object) reinterpret_cast(it->second)->set_flattening_data(model_object); } -void GLCanvas3D::Gizmos::set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection) +void GLCanvas3D::Gizmos::set_sla_support_data(ModelObject* model_object, const Selection& selection) { if (!m_enabled) return; @@ -2970,7 +1316,7 @@ bool GLCanvas3D::Gizmos::mouse_event(SLAGizmoEventType action, const Vec2d& mous return false; } -void GLCanvas3D::Gizmos::render_current_gizmo(const GLCanvas3D::Selection& selection) const +void GLCanvas3D::Gizmos::render_current_gizmo(const Selection& selection) const { if (!m_enabled) return; @@ -2978,7 +1324,7 @@ void GLCanvas3D::Gizmos::render_current_gizmo(const GLCanvas3D::Selection& selec do_render_current_gizmo(selection); } -void GLCanvas3D::Gizmos::render_current_gizmo_for_picking_pass(const GLCanvas3D::Selection& selection) const +void GLCanvas3D::Gizmos::render_current_gizmo_for_picking_pass(const Selection& selection) const { if (!m_enabled) return; @@ -2988,7 +1334,7 @@ void GLCanvas3D::Gizmos::render_current_gizmo_for_picking_pass(const GLCanvas3D: curr->render_for_picking(selection); } -void GLCanvas3D::Gizmos::render_overlay(const GLCanvas3D& canvas, const GLCanvas3D::Selection& selection) const +void GLCanvas3D::Gizmos::render_overlay(const GLCanvas3D& canvas, const Selection& selection) const { if (!m_enabled) return; @@ -3020,7 +1366,7 @@ void GLCanvas3D::Gizmos::reset() m_gizmos.clear(); } -void GLCanvas3D::Gizmos::do_render_overlay(const GLCanvas3D& canvas, const GLCanvas3D::Selection& selection) const +void GLCanvas3D::Gizmos::do_render_overlay(const GLCanvas3D& canvas, const Selection& selection) const { if (m_gizmos.empty()) return; @@ -3180,7 +1526,7 @@ void GLCanvas3D::Gizmos::do_render_overlay(const GLCanvas3D& canvas, const GLCan } } -void GLCanvas3D::Gizmos::do_render_current_gizmo(const GLCanvas3D::Selection& selection) const +void GLCanvas3D::Gizmos::do_render_current_gizmo(const Selection& selection) const { GLGizmoBase* curr = get_current(); if (curr != nullptr) @@ -3718,7 +2064,6 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar , m_use_VBOs(false) , m_apply_zoom_to_volumes_filter(false) , m_hover_volume_id(-1) - , m_toolbar_action_running(false) , m_legend_texture_enabled(false) , m_picking_enabled(false) , m_moving_enabled(false) @@ -4022,19 +2367,6 @@ void GLCanvas3D::allow_multisample(bool allow) m_multisample_allowed = allow; } -void GLCanvas3D::enable_toolbar_item(const std::string& name, bool enable) -{ - if (enable) - m_toolbar.enable_item(name); - else - m_toolbar.disable_item(name); -} - -bool GLCanvas3D::is_toolbar_item_pressed(const std::string& name) const -{ - return m_toolbar.is_item_pressed(name); -} - void GLCanvas3D::zoom_to_bed() { _zoom_to_bounding_box(m_bed.get_bounding_box()); @@ -4087,16 +2419,6 @@ void GLCanvas3D::update_volumes_colors_by_extruder() m_volumes.update_colors_by_extruder(m_config); } -void GLCanvas3D::update_toolbar_items_visibility() -{ - ConfigOptionMode mode = wxGetApp().get_mode(); - m_toolbar.set_item_visible("more", mode != comSimple); - m_toolbar.set_item_visible("fewer", mode != comSimple); - m_toolbar.set_item_visible("splitvolumes", mode != comSimple); - m_dirty = true; -} - - void GLCanvas3D::render() { wxCHECK_RET(!m_in_render, "GLCanvas3D::render() called recursively"); @@ -4749,6 +3071,9 @@ void GLCanvas3D::on_size(wxSizeEvent& evt) void GLCanvas3D::on_idle(wxIdleEvent& evt) { + m_dirty |= m_toolbar.update_items_state(); + m_dirty |= m_view_toolbar.update_items_state(); + if (!m_dirty) return; @@ -5027,6 +3352,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) #endif /* SLIC3R_DEBUG_MOUSE_EVENTS */ } + bool processed_by_toolbar = m_toolbar.on_mouse(evt, *this); + processed_by_toolbar |= m_view_toolbar.on_mouse(evt, *this); + + if (processed_by_toolbar) + { + m_mouse.set_start_position_3D_as_invalid(); + return; + } + if (m_picking_enabled) _set_current(); @@ -5034,16 +3368,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1; m_layers_editing.select_object(*m_model, layer_editing_object_idx); bool gizmos_overlay_contains_mouse = m_gizmos.overlay_contains_mouse(*this, m_mouse.position); - int toolbar_contains_mouse = m_toolbar.contains_mouse(m_mouse.position, *this); - int view_toolbar_contains_mouse = m_view_toolbar.contains_mouse(m_mouse.position, *this); -#if ENABLE_MOVE_MIN_THRESHOLD if (m_mouse.drag.move_requires_threshold && m_mouse.is_move_start_threshold_position_2D_defined() && m_mouse.is_move_threshold_met(pos)) { m_mouse.drag.move_requires_threshold = false; m_mouse.set_move_start_threshold_position_2D_as_invalid(); } -#endif // ENABLE_MOVE_MIN_THRESHOLD if (evt.ButtonDown() && wxWindow::FindFocus() != this->m_canvas) // Grab keyboard focus on any mouse click event. @@ -5077,24 +3407,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { // to remove hover on objects when the mouse goes out of this canvas m_mouse.position = Vec2d(-1.0, -1.0); - // ensure m_mouse.left_down is reset (it may happen when switching canvas) - m_mouse.left_down = false; m_dirty = true; } - else if (evt.LeftDClick() && (toolbar_contains_mouse != -1)) - { - m_toolbar_action_running = true; - m_mouse.set_start_position_3D_as_invalid(); - m_toolbar.do_action((unsigned int)toolbar_contains_mouse, *this); - } else if (evt.LeftDClick() && (m_gizmos.get_current_type() != Gizmos::Undefined)) { m_mouse.ignore_up_event = true; } else if (evt.LeftDown() || evt.RightDown()) { - m_mouse.left_down = evt.LeftDown(); - // If user pressed left or right button we first check whether this happened // on a volume or not. m_layers_editing.state = LayersEditing::Unknown; @@ -5146,15 +3466,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { // event was taken care of by the SlaSupports gizmo } - else if (evt.LeftDown() && (view_toolbar_contains_mouse != -1)) - m_view_toolbar.do_action((unsigned int)view_toolbar_contains_mouse, *this); - else if (evt.LeftDown() && (toolbar_contains_mouse != -1)) - { - m_toolbar_action_running = true; - m_mouse.set_start_position_3D_as_invalid(); - m_toolbar.do_action((unsigned int)toolbar_contains_mouse, *this); - m_mouse.left_down = false; - } else { // Select volume in this 3D canvas. @@ -5176,13 +3487,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { bool add_as_single = !already_selected && !shift_down; m_selection.add(m_hover_volume_id, add_as_single); -#if ENABLE_MOVE_MIN_THRESHOLD m_mouse.drag.move_requires_threshold = !already_selected; if (already_selected) m_mouse.set_move_start_threshold_position_2D_as_invalid(); else m_mouse.drag.move_start_threshold_position_2D = pos; -#endif // ENABLE_MOVE_MIN_THRESHOLD } if (curr_idxs != m_selection.get_volume_idxs()) @@ -5219,10 +3528,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.move_volume_idx != -1) && m_gizmos.get_current_type() != Gizmos::SlaSupports /* don't allow dragging objects with the Sla gizmo on */) { -#if ENABLE_MOVE_MIN_THRESHOLD if (!m_mouse.drag.move_requires_threshold) { -#endif // ENABLE_MOVE_MIN_THRESHOLD m_mouse.dragging = true; Vec3d cur_pos = m_mouse.drag.start_position_3D; @@ -5270,9 +3577,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) wxGetApp().obj_manipul()->update_settings_value(m_selection); m_dirty = true; -#if ENABLE_MOVE_MIN_THRESHOLD } -#endif // ENABLE_MOVE_MIN_THRESHOLD } else if (evt.Dragging() && m_gizmos.is_dragging()) { @@ -5320,7 +3625,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } // do not process dragging if the mouse is into any of the HUD elements - else if (evt.Dragging() && !gizmos_overlay_contains_mouse && (toolbar_contains_mouse == -1) && (view_toolbar_contains_mouse == -1)) + else if (evt.Dragging() && !gizmos_overlay_contains_mouse) { m_mouse.dragging = true; @@ -5330,14 +3635,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) _perform_layer_editing_action(&evt); } // do not process the dragging if the left mouse was set down in another canvas - else if (m_mouse.left_down && evt.LeftIsDown()) + else if (evt.LeftIsDown()) { // if dragging over blank area with left button, rotate -#if ENABLE_MOVE_MIN_THRESHOLD if ((m_hover_volume_id == -1) && m_mouse.is_start_position_3D_defined()) -#else - if (m_mouse.is_start_position_3D_defined()) -#endif // ENABLE_MOVE_MIN_THRESHOLD { const Vec3d& orig = m_mouse.drag.start_position_3D; m_camera.phi += (((float)pos(0) - (float)orig(0)) * TRACKBALLSIZE); @@ -5391,7 +3692,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // that's why the mouse_event function was called so that the gizmo can refuse the deselection in manual editing mode // deselect and propagate event through callback - if (!evt.ShiftDown() && m_picking_enabled && !m_toolbar_action_running && !m_mouse.ignore_up_event) + if (!evt.ShiftDown() && m_picking_enabled && !m_mouse.ignore_up_event) { m_selection.clear(); m_selection.set_mode(Selection::Instance); @@ -5471,8 +3772,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.set_start_position_3D_as_invalid(); m_mouse.set_start_position_2D_as_invalid(); m_mouse.dragging = false; - m_mouse.left_down = false; - m_toolbar_action_running = false; m_dirty = true; if (m_canvas->HasCapture()) @@ -5488,17 +3787,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_selection.is_empty()) m_gizmos.reset_all_states(); - // updates toolbar overlay if (tooltip.empty()) - tooltip = m_toolbar.update_hover_state(m_mouse.position, *this); + tooltip = m_toolbar.get_tooltip(); - // updates view toolbar overlay if (tooltip.empty()) - { - tooltip = m_view_toolbar.update_hover_state(m_mouse.position, *this); - if (!tooltip.empty()) - m_dirty = true; - } + tooltip = m_view_toolbar.get_tooltip(); set_tooltip(tooltip); @@ -5896,7 +4189,7 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("Add...") + " [" + GUI::shortkey_ctrl_prefix() + "I]"; item.sprite_id = 0; - item.action_event = EVT_GLTOOLBAR_ADD; + item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ADD)); }; if (!m_toolbar.add_item(item)) return false; @@ -5906,7 +4199,8 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("Delete") + " [Del]"; item.sprite_id = 1; - item.action_event = EVT_GLTOOLBAR_DELETE; + item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE)); }; + item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_delete(); }; if (!m_toolbar.add_item(item)) return false; @@ -5916,7 +4210,8 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("Delete all") + " [" + GUI::shortkey_ctrl_prefix() + "Del]"; item.sprite_id = 2; - item.action_event = EVT_GLTOOLBAR_DELETE_ALL; + item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); }; + item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_delete_all(); }; if (!m_toolbar.add_item(item)) return false; @@ -5926,7 +4221,8 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("Arrange [A]"); item.sprite_id = 3; - item.action_event = EVT_GLTOOLBAR_ARRANGE; + item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ARRANGE)); }; + item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_arrange(); }; if (!m_toolbar.add_item(item)) return false; @@ -5939,7 +4235,9 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("Add instance [+]"); item.sprite_id = 4; - item.action_event = EVT_GLTOOLBAR_MORE; + item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_MORE)); }; + item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; }; + item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_increase_instances(); }; if (!m_toolbar.add_item(item)) return false; @@ -5949,7 +4247,9 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("Remove instance [-]"); item.sprite_id = 5; - item.action_event = EVT_GLTOOLBAR_FEWER; + item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_FEWER)); }; + item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; }; + item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_decrease_instances(); }; if (!m_toolbar.add_item(item)) return false; @@ -5962,7 +4262,9 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("Split to objects"); item.sprite_id = 6; - item.action_event = EVT_GLTOOLBAR_SPLIT_OBJECTS; + item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); }; + item.visibility_callback = GLToolbarItem::Default_Visibility_Callback; + item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_split_to_objects(); }; if (!m_toolbar.add_item(item)) return false; @@ -5972,7 +4274,9 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("Split to parts"); item.sprite_id = 7; - item.action_event = EVT_GLTOOLBAR_SPLIT_VOLUMES; + item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); }; + item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; }; + item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_split_to_volumes(); }; if (!m_toolbar.add_item(item)) return false; @@ -5986,14 +4290,12 @@ bool GLCanvas3D::_init_toolbar() item.tooltip = GUI::L_str("Layers editing"); item.sprite_id = 8; item.is_toggable = true; - item.action_event = EVT_GLTOOLBAR_LAYERSEDITING; + item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); }; + item.visibility_callback = GLToolbarItem::Default_Visibility_Callback; + item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); }; if (!m_toolbar.add_item(item)) return false; - enable_toolbar_item("add", true); - - update_toolbar_items_visibility(); - return true; } @@ -6195,7 +4497,7 @@ void GLCanvas3D::_picking_pass() const { const Vec2d& pos = m_mouse.position; - if (m_picking_enabled && !m_mouse.dragging && !m_mouse.left_down && (pos != Vec2d(DBL_MAX, DBL_MAX))) + if (m_picking_enabled && !m_mouse.dragging && (pos != Vec2d(DBL_MAX, DBL_MAX))) { // Render the object for picking. // FIXME This cannot possibly work in a multi - sampled context as the color gets mangled by the anti - aliasing. diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 4ec0f076b2..4cbabc4384 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -4,12 +4,12 @@ #include #include -#include "libslic3r/Technologies.hpp" #include "3DScene.hpp" #include "GLToolbar.hpp" #include "Event.hpp" #include "3DBed.hpp" #include "Camera.hpp" +#include "Selection.hpp" #include @@ -297,24 +297,19 @@ class GLCanvas3D { static const Point Invalid_2D_Point; static const Vec3d Invalid_3D_Point; -#if ENABLE_MOVE_MIN_THRESHOLD static const int MoveThresholdPx; -#endif // ENABLE_MOVE_MIN_THRESHOLD Point start_position_2D; Vec3d start_position_3D; int move_volume_idx; -#if ENABLE_MOVE_MIN_THRESHOLD bool move_requires_threshold; Point move_start_threshold_position_2D; -#endif // ENABLE_MOVE_MIN_THRESHOLD public: Drag(); }; bool dragging; - bool left_down; Vec2d position; Vec3d scene_position; Drag drag; @@ -324,309 +319,18 @@ class GLCanvas3D void set_start_position_2D_as_invalid() { drag.start_position_2D = Drag::Invalid_2D_Point; } void set_start_position_3D_as_invalid() { drag.start_position_3D = Drag::Invalid_3D_Point; } -#if ENABLE_MOVE_MIN_THRESHOLD void set_move_start_threshold_position_2D_as_invalid() { drag.move_start_threshold_position_2D = Drag::Invalid_2D_Point; } -#endif // ENABLE_MOVE_MIN_THRESHOLD bool is_start_position_2D_defined() const { return (drag.start_position_2D != Drag::Invalid_2D_Point); } bool is_start_position_3D_defined() const { return (drag.start_position_3D != Drag::Invalid_3D_Point); } -#if ENABLE_MOVE_MIN_THRESHOLD bool is_move_start_threshold_position_2D_defined() const { return (drag.move_start_threshold_position_2D != Drag::Invalid_2D_Point); } bool is_move_threshold_met(const Point& mouse_pos) const { return (std::abs(mouse_pos(0) - drag.move_start_threshold_position_2D(0)) > Drag::MoveThresholdPx) || (std::abs(mouse_pos(1) - drag.move_start_threshold_position_2D(1)) > Drag::MoveThresholdPx); } -#endif // ENABLE_MOVE_MIN_THRESHOLD }; public: - class TransformationType - { - public: - enum Enum { - // Transforming in a world coordinate system - World = 0, - // Transforming in a local coordinate system - Local = 1, - // Absolute transformations, allowed in local coordinate system only. - Absolute = 0, - // Relative transformations, allowed in both local and world coordinate system. - Relative = 2, - // For group selection, the transformation is performed as if the group made a single solid body. - Joint = 0, - // For group selection, the transformation is performed on each object independently. - Independent = 4, - - World_Relative_Joint = World | Relative | Joint, - World_Relative_Independent = World | Relative | Independent, - Local_Absolute_Joint = Local | Absolute | Joint, - Local_Absolute_Independent = Local | Absolute | Independent, - Local_Relative_Joint = Local | Relative | Joint, - Local_Relative_Independent = Local | Relative | Independent, - }; - - TransformationType() : m_value(World) {} - TransformationType(Enum value) : m_value(value) {} - TransformationType& operator=(Enum value) { m_value = value; return *this; } - - Enum operator()() const { return m_value; } - bool has(Enum v) const { return ((unsigned int)m_value & (unsigned int)v) != 0; } - - void set_world() { this->remove(Local); } - void set_local() { this->add(Local); } - void set_absolute() { this->remove(Relative); } - void set_relative() { this->add(Relative); } - void set_joint() { this->remove(Independent); } - void set_independent() { this->add(Independent); } - - bool world() const { return ! this->has(Local); } - bool local() const { return this->has(Local); } - bool absolute() const { return ! this->has(Relative); } - bool relative() const { return this->has(Relative); } - bool joint() const { return ! this->has(Independent); } - bool independent() const { return this->has(Independent); } - - private: - void add(Enum v) { m_value = Enum((unsigned int)m_value | (unsigned int)v); } - void remove(Enum v) { m_value = Enum((unsigned int)m_value & (~(unsigned int)v)); } - - Enum m_value; - }; - - class Selection - { - public: - typedef std::set IndicesList; - - enum EMode : unsigned char - { - Volume, - Instance - }; - - enum EType : unsigned char - { - Invalid, - Empty, - WipeTower, - SingleModifier, - MultipleModifier, - SingleVolume, - MultipleVolume, - SingleFullObject, - MultipleFullObject, - SingleFullInstance, - MultipleFullInstance, - Mixed - }; - - private: - struct VolumeCache - { - private: - struct TransformCache - { - Vec3d position; - Vec3d rotation; - Vec3d scaling_factor; - Vec3d mirror; - Transform3d rotation_matrix; - Transform3d scale_matrix; - Transform3d mirror_matrix; - Transform3d full_matrix; - - TransformCache(); - explicit TransformCache(const Geometry::Transformation& transform); - }; - - TransformCache m_volume; - TransformCache m_instance; - - public: - VolumeCache() {} - VolumeCache(const Geometry::Transformation& volume_transform, const Geometry::Transformation& instance_transform); - - const Vec3d& get_volume_position() const { return m_volume.position; } - const Vec3d& get_volume_rotation() const { return m_volume.rotation; } - const Vec3d& get_volume_scaling_factor() const { return m_volume.scaling_factor; } - const Vec3d& get_volume_mirror() const { return m_volume.mirror; } - const Transform3d& get_volume_rotation_matrix() const { return m_volume.rotation_matrix; } - const Transform3d& get_volume_scale_matrix() const { return m_volume.scale_matrix; } - const Transform3d& get_volume_mirror_matrix() const { return m_volume.mirror_matrix; } - const Transform3d& get_volume_full_matrix() const { return m_volume.full_matrix; } - - const Vec3d& get_instance_position() const { return m_instance.position; } - const Vec3d& get_instance_rotation() const { return m_instance.rotation; } - const Vec3d& get_instance_scaling_factor() const { return m_instance.scaling_factor; } - const Vec3d& get_instance_mirror() const { return m_instance.mirror; } - const Transform3d& get_instance_rotation_matrix() const { return m_instance.rotation_matrix; } - const Transform3d& get_instance_scale_matrix() const { return m_instance.scale_matrix; } - const Transform3d& get_instance_mirror_matrix() const { return m_instance.mirror_matrix; } - const Transform3d& get_instance_full_matrix() const { return m_instance.full_matrix; } - }; - - typedef std::map VolumesCache; - typedef std::set InstanceIdxsList; - typedef std::map ObjectIdxsToInstanceIdxsMap; - - struct Cache - { - // Cache of GLVolume derived transformation matrices, valid during mouse dragging. - VolumesCache volumes_data; - // Center of the dragged selection, valid during mouse dragging. - Vec3d dragging_center; - // Map from indices of ModelObject instances in Model::objects - // to a set of indices of ModelVolume instances in ModelObject::instances - // Here the index means a position inside the respective std::vector, not ModelID. - ObjectIdxsToInstanceIdxsMap content; - }; - - // Volumes owned by GLCanvas3D. - GLVolumePtrs* m_volumes; - // Model, not owned. - Model* m_model; - - bool m_valid; - EMode m_mode; - EType m_type; - // set of indices to m_volumes - IndicesList m_list; - Cache m_cache; - mutable BoundingBoxf3 m_bounding_box; - mutable bool m_bounding_box_dirty; - -#if ENABLE_RENDER_SELECTION_CENTER - GLUquadricObj* m_quadric; -#endif // ENABLE_RENDER_SELECTION_CENTER - mutable GLArrow m_arrow; - mutable GLCurvedArrow m_curved_arrow; - - mutable float m_scale_factor; - - public: - Selection(); -#if ENABLE_RENDER_SELECTION_CENTER - ~Selection(); -#endif // ENABLE_RENDER_SELECTION_CENTER - - void set_volumes(GLVolumePtrs* volumes); - bool init(bool useVBOs); - - Model* get_model() const { return m_model; } - void set_model(Model* model); - - EMode get_mode() const { return m_mode; } - void set_mode(EMode mode) { m_mode = mode; } - - void add(unsigned int volume_idx, bool as_single_selection = true); - void remove(unsigned int volume_idx); - - void add_object(unsigned int object_idx, bool as_single_selection = true); - void remove_object(unsigned int object_idx); - - void add_instance(unsigned int object_idx, unsigned int instance_idx, bool as_single_selection = true); - void remove_instance(unsigned int object_idx, unsigned int instance_idx); - - void add_volume(unsigned int object_idx, unsigned int volume_idx, int instance_idx, bool as_single_selection = true); - void remove_volume(unsigned int object_idx, unsigned int volume_idx); - - void add_all(); - - // Update the selection based on the map from old indices to new indices after m_volumes changed. - // If the current selection is by instance, this call may select newly added volumes, if they belong to already selected instances. - void volumes_changed(const std::vector &map_volume_old_to_new); - void clear(); - - bool is_empty() const { return m_type == Empty; } - bool is_wipe_tower() const { return m_type == WipeTower; } - bool is_modifier() const { return (m_type == SingleModifier) || (m_type == MultipleModifier); } - bool is_single_modifier() const { return m_type == SingleModifier; } - bool is_multiple_modifier() const { return m_type == MultipleModifier; } - bool is_single_full_instance() const; - bool is_multiple_full_instance() const { return m_type == MultipleFullInstance; } - bool is_single_full_object() const { return m_type == SingleFullObject; } - bool is_multiple_full_object() const { return m_type == MultipleFullObject; } - bool is_single_volume() const { return m_type == SingleVolume; } - bool is_multiple_volume() const { return m_type == MultipleVolume; } - bool is_mixed() const { return m_type == Mixed; } - bool is_from_single_instance() const { return get_instance_idx() != -1; } - bool is_from_single_object() const; - - bool contains_volume(unsigned int volume_idx) const { return std::find(m_list.begin(), m_list.end(), volume_idx) != m_list.end(); } - bool requires_uniform_scale() const; - - // Returns the the object id if the selection is from a single object, otherwise is -1 - int get_object_idx() const; - // Returns the instance id if the selection is from a single object and from a single instance, otherwise is -1 - int get_instance_idx() const; - // Returns the indices of selected instances. - // Can only be called if selection is from a single object. - const InstanceIdxsList& get_instance_idxs() const; - - const IndicesList& get_volume_idxs() const { return m_list; } - const GLVolume* get_volume(unsigned int volume_idx) const; - - const ObjectIdxsToInstanceIdxsMap& get_content() const { return m_cache.content; } - - unsigned int volumes_count() const { return (unsigned int)m_list.size(); } - const BoundingBoxf3& get_bounding_box() const; - - void start_dragging(); - - void translate(const Vec3d& displacement, bool local = false); - void rotate(const Vec3d& rotation, TransformationType transformation_type); - void flattening_rotate(const Vec3d& normal); - void scale(const Vec3d& scale, bool local); - void mirror(Axis axis); - - void translate(unsigned int object_idx, const Vec3d& displacement); - void translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement); - - void erase(); - - void render(float scale_factor = 1.0) const; -#if ENABLE_RENDER_SELECTION_CENTER - void render_center() const; -#endif // ENABLE_RENDER_SELECTION_CENTER - void render_sidebar_hints(const std::string& sidebar_field) const; - - bool requires_local_axes() const; - - private: - void _update_valid(); - void _update_type(); - void _set_caches(); - void _add_volume(unsigned int volume_idx); - void _add_instance(unsigned int object_idx, unsigned int instance_idx); - void _add_object(unsigned int object_idx); - void _remove_volume(unsigned int volume_idx); - void _remove_instance(unsigned int object_idx, unsigned int instance_idx); - void _remove_object(unsigned int object_idx); - void _calc_bounding_box() const; - void _render_selected_volumes() const; - void _render_synchronized_volumes() const; - void _render_bounding_box(const BoundingBoxf3& box, float* color) const; - void _render_sidebar_position_hints(const std::string& sidebar_field) const; - void _render_sidebar_rotation_hints(const std::string& sidebar_field) const; - void _render_sidebar_scale_hints(const std::string& sidebar_field) const; - void _render_sidebar_size_hints(const std::string& sidebar_field) const; - void _render_sidebar_position_hint(Axis axis) const; - void _render_sidebar_rotation_hint(Axis axis) const; - void _render_sidebar_scale_hint(Axis axis) const; - void _render_sidebar_size_hint(Axis axis, double length) const; - enum SyncRotationType { - // Do not synchronize rotation. Either not rotating at all, or rotating by world Z axis. - SYNC_ROTATION_NONE = 0, - // Synchronize fully. Used from "place on bed" feature. - SYNC_ROTATION_FULL = 1, - // Synchronize after rotation by an axis not parallel with Z. - SYNC_ROTATION_GENERAL = 2, - }; - void _synchronize_unselected_instances(SyncRotationType sync_rotation_type); - void _synchronize_unselected_volumes(); - void _ensure_on_bed(); - }; - class ClippingPlane { double m_data[4]; @@ -741,7 +445,7 @@ private: void set_flattening_data(const ModelObject* model_object); - void set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection); + void set_sla_support_data(ModelObject* model_object, const Selection& selection); bool mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false); void delete_current_grabber(bool delete_all = false); @@ -872,7 +576,6 @@ private: bool m_use_VBOs; bool m_apply_zoom_to_volumes_filter; mutable int m_hover_volume_id; - bool m_toolbar_action_running; bool m_warning_texture_enabled; bool m_legend_texture_enabled; bool m_picking_enabled; @@ -951,9 +654,6 @@ public: void enable_dynamic_background(bool enable); void allow_multisample(bool allow); - void enable_toolbar_item(const std::string& name, bool enable); - bool is_toolbar_item_pressed(const std::string& name) const; - void zoom_to_bed(); void zoom_to_volumes(); void zoom_to_selection(); @@ -961,8 +661,6 @@ public: void update_volumes_colors_by_extruder(); - void update_toolbar_items_visibility(); - bool is_dragging() const { return m_gizmos.is_dragging() || m_moving; } void render(); diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 1b8ccc1658..5ac5bf92f5 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -30,6 +30,10 @@ wxDEFINE_EVENT(EVT_GLTOOLBAR_LAYERSEDITING, SimpleEvent); wxDEFINE_EVENT(EVT_GLVIEWTOOLBAR_3D, SimpleEvent); wxDEFINE_EVENT(EVT_GLVIEWTOOLBAR_PREVIEW, SimpleEvent); +const GLToolbarItem::ActionCallback GLToolbarItem::Default_Action_Callback = [](){}; +const GLToolbarItem::VisibilityCallback GLToolbarItem::Default_Visibility_Callback = []()->bool { return true; }; +const GLToolbarItem::EnabledStateCallback GLToolbarItem::Default_Enabled_State_Callback = []()->bool { return true; }; + GLToolbarItem::Data::Data() : name("") #if ENABLE_SVG_ICONS @@ -39,19 +43,37 @@ GLToolbarItem::Data::Data() , sprite_id(-1) , is_toggable(false) , visible(true) + , action_callback(Default_Action_Callback) + , visibility_callback(Default_Visibility_Callback) + , enabled_state_callback(Default_Enabled_State_Callback) { } GLToolbarItem::GLToolbarItem(GLToolbarItem::EType type, const GLToolbarItem::Data& data) : m_type(type) - , m_state(Disabled) + , m_state(Normal) , m_data(data) { } -void GLToolbarItem::do_action(wxEvtHandler *target) +bool GLToolbarItem::update_visibility() { - wxPostEvent(target, SimpleEvent(m_data.action_event)); + bool visible = m_data.visibility_callback(); + bool ret = (m_data.visible != visible); + if (ret) + m_data.visible = visible; + + return ret; +} + +bool GLToolbarItem::update_enabled_state() +{ + bool enabled = m_data.enabled_state_callback(); + bool ret = (is_enabled() != enabled); + if (ret) + m_state = enabled ? GLToolbarItem::Normal : GLToolbarItem::Disabled; + + return ret; } void GLToolbarItem::render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const @@ -135,6 +157,8 @@ GLToolbar::GLToolbar(GLToolbar::EType type) #if ENABLE_SVG_ICONS , m_icons_texture_dirty(true) #endif // ENABLE_SVG_ICONS + , m_mouse_capture({false, false, false}) + , m_tooltip("") { } @@ -299,30 +323,6 @@ float GLToolbar::get_height() const return m_layout.height; } -void GLToolbar::enable_item(const std::string& name) -{ - for (GLToolbarItem* item : m_items) - { - if ((item->get_name() == name) && (item->get_state() == GLToolbarItem::Disabled)) - { - item->set_state(GLToolbarItem::Normal); - return; - } - } -} - -void GLToolbar::disable_item(const std::string& name) -{ - for (GLToolbarItem* item : m_items) - { - if (item->get_name() == name) - { - item->set_state(GLToolbarItem::Disabled); - return; - } - } -} - void GLToolbar::select_item(const std::string& name) { if (is_item_disabled(name)) @@ -371,95 +371,12 @@ bool GLToolbar::is_item_visible(const std::string& name) const return false; } -void GLToolbar::set_item_visible(const std::string& name, bool visible) +bool GLToolbar::update_items_state() { - for (GLToolbarItem* item : m_items) - { - if ((item->get_name() == name) && (item->is_visible() != visible)) - { - item->set_visible(visible); - m_layout.dirty = true; - break; - } - } - - // updates separators visibility to avoid having two consecutive - bool any_item_visible = false; - for (GLToolbarItem* item : m_items) - { - if (!item->is_separator()) - any_item_visible |= item->is_visible(); - else - { - item->set_visible(any_item_visible); - any_item_visible = false; - } - } - -} - -std::string GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent) -{ - if (!m_enabled) - return ""; - - switch (m_layout.type) - { - default: - case Layout::Horizontal: { return update_hover_state_horizontal(mouse_pos, parent); } - case Layout::Vertical: { return update_hover_state_vertical(mouse_pos, parent); } - } -} - -int GLToolbar::contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const -{ - if (!m_enabled) - return -1; - - switch (m_layout.type) - { - default: - case Layout::Horizontal: { return contains_mouse_horizontal(mouse_pos, parent); } - case Layout::Vertical: { return contains_mouse_vertical(mouse_pos, parent); } - } -} - -void GLToolbar::do_action(unsigned int item_id, GLCanvas3D& parent) -{ - if (item_id < (unsigned int)m_items.size()) - { - GLToolbarItem* item = m_items[item_id]; - if ((item != nullptr) && !item->is_separator() && item->is_hovered()) - { - if (item->is_toggable()) - { - GLToolbarItem::EState state = item->get_state(); - if (state == GLToolbarItem::Hover) - item->set_state(GLToolbarItem::HoverPressed); - else if (state == GLToolbarItem::HoverPressed) - item->set_state(GLToolbarItem::Hover); - - parent.render(); - item->do_action(parent.get_wxglcanvas()); - } - else - { - if (m_type == Radio) - select_item(item->get_name()); - else - item->set_state(GLToolbarItem::HoverPressed); - - parent.render(); - item->do_action(parent.get_wxglcanvas()); - if ((m_type == Normal) && (item->get_state() != GLToolbarItem::Disabled)) - { - // the item may get disabled during the action, if not, set it back to hover state - item->set_state(GLToolbarItem::Hover); - parent.render(); - } - } - } - } + bool ret = false; + ret |= update_items_visibility(); + ret |= update_items_enabled_state(); + return ret; } void GLToolbar::render(const GLCanvas3D& parent) const @@ -487,6 +404,53 @@ void GLToolbar::render(const GLCanvas3D& parent) const ::glPopMatrix(); } +bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) +{ + Vec2d mouse_pos((double)evt.GetX(), (double)evt.GetY()); + bool processed = false; + + // mouse anywhere + if (evt.Moving()) + m_tooltip = update_hover_state(mouse_pos, parent); + else if (evt.LeftUp()) + m_mouse_capture.left = false; + else if (evt.MiddleUp()) + m_mouse_capture.middle = false; + else if (evt.RightUp()) + m_mouse_capture.right = false; + else if (m_mouse_capture.any() && evt.Dragging()) + processed = true; + + int item_id = contains_mouse(mouse_pos, parent); + if (item_id == -1) + { + // mouse is outside the toolbar + m_tooltip = ""; + } + else + { + // mouse inside toolbar only + if (evt.LeftDown() || evt.LeftDClick()) + { + m_mouse_capture.left = true; + if ((item_id != -2) && !m_items[item_id]->is_separator()) + { + // mouse is inside an icon + do_action((unsigned int)item_id, parent); + processed = true; + } + } + else if (evt.MiddleDown()) + m_mouse_capture.middle = true; + else if (evt.RightDown()) + m_mouse_capture.right = true; + else if (evt.LeftUp()) + processed = true; + } + + return processed; +} + void GLToolbar::calc_layout() const { switch (m_layout.type) @@ -576,6 +540,57 @@ float GLToolbar::get_main_size() const return size; } +void GLToolbar::do_action(unsigned int item_id, GLCanvas3D& parent) +{ + if (item_id < (unsigned int)m_items.size()) + { + GLToolbarItem* item = m_items[item_id]; + if ((item != nullptr) && !item->is_separator() && item->is_hovered()) + { + if (item->is_toggable()) + { + GLToolbarItem::EState state = item->get_state(); + if (state == GLToolbarItem::Hover) + item->set_state(GLToolbarItem::HoverPressed); + else if (state == GLToolbarItem::HoverPressed) + item->set_state(GLToolbarItem::Hover); + + parent.render(); + item->do_action(); + } + else + { + if (m_type == Radio) + select_item(item->get_name()); + else + item->set_state(GLToolbarItem::HoverPressed); + + parent.render(); + item->do_action(); + if ((m_type == Normal) && (item->get_state() != GLToolbarItem::Disabled)) + { + // the item may get disabled during the action, if not, set it back to hover state + item->set_state(GLToolbarItem::Hover); + parent.render(); + } + } + } + } +} + +std::string GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent) +{ + if (!m_enabled) + return ""; + + switch (m_layout.type) + { + default: + case Layout::Horizontal: { return update_hover_state_horizontal(mouse_pos, parent); } + case Layout::Vertical: { return update_hover_state_vertical(mouse_pos, parent); } + } +} + std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent) { // NB: mouse_pos is already scaled appropriately @@ -785,6 +800,19 @@ std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCan return tooltip; } +int GLToolbar::contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const +{ + if (!m_enabled) + return -1; + + switch (m_layout.type) + { + default: + case Layout::Horizontal: { return contains_mouse_horizontal(mouse_pos, parent); } + case Layout::Vertical: { return contains_mouse_vertical(mouse_pos, parent); } + } +} + int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const { // NB: mouse_pos is already scaled appropriately @@ -808,8 +836,6 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3 float scaled_separator_size = m_layout.separator_size * factor; float scaled_gap_size = m_layout.gap_size * factor; float scaled_border = m_layout.border * factor; - float separator_stride = scaled_separator_size + scaled_gap_size; - float icon_stride = scaled_icons_size + scaled_gap_size; float left = m_layout.left + scaled_border; float top = m_layout.top - scaled_border; @@ -824,16 +850,46 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3 continue; if (item->is_separator()) - left += separator_stride; + { + float right = left + scaled_separator_size; + float bottom = top - scaled_icons_size; + + // mouse inside the separator + if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) + return id; + + left = right; + right += scaled_gap_size; + + if (id < m_items.size() - 1) + { + // mouse inside the gap + if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) + return -2; + } + + left = right; + } else { float right = left + scaled_icons_size; float bottom = top - scaled_icons_size; + // mouse inside the icon if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) return id; - left += icon_stride; + left = right; + right += scaled_gap_size; + + if (id < m_items.size() - 1) + { + // mouse inside the gap + if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) + return -2; + } + + left = right; } } @@ -864,9 +920,6 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& float scaled_gap_size = m_layout.gap_size * factor; float scaled_border = m_layout.border * factor; - float separator_stride = scaled_separator_size + scaled_gap_size; - float icon_stride = scaled_icons_size + scaled_gap_size; - float left = m_layout.left + scaled_border; float top = m_layout.top - scaled_border; @@ -880,16 +933,46 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& continue; if (item->is_separator()) - top -= separator_stride; + { + float right = left + scaled_icons_size; + float bottom = top - scaled_separator_size; + + // mouse inside the separator + if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) + return id; + + top = bottom; + bottom -= scaled_gap_size; + + if (id < m_items.size() - 1) + { + // mouse inside the gap + if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) + return -2; + } + + top = bottom; + } else { float right = left + scaled_icons_size; float bottom = top - scaled_icons_size; + // mouse inside the icon if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) return id; - top -= icon_stride; + top = bottom; + bottom -= scaled_gap_size; + + if (id < m_items.size() - 1) + { + // mouse inside the gap + if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) + return -2; + } + + top = bottom; } } @@ -1244,5 +1327,48 @@ bool GLToolbar::generate_icons_texture() const } #endif // ENABLE_SVG_ICONS +bool GLToolbar::update_items_visibility() +{ + bool ret = false; + + for (GLToolbarItem* item : m_items) + { + ret |= item->update_visibility(); + } + + if (ret) + m_layout.dirty = true; + + // updates separators visibility to avoid having two of them consecutive + bool any_item_visible = false; + for (GLToolbarItem* item : m_items) + { + if (!item->is_separator()) + any_item_visible |= item->is_visible(); + else + { + item->set_visible(any_item_visible); + any_item_visible = false; + } + } + + return ret; +} + +bool GLToolbar::update_items_enabled_state() +{ + bool ret = false; + + for (GLToolbarItem* item : m_items) + { + ret |= item->update_enabled_state(); + } + + if (ret) + m_layout.dirty = true; + + return ret; +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 4ca72fc4ac..07d1fcb92f 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -31,6 +31,10 @@ wxDECLARE_EVENT(EVT_GLVIEWTOOLBAR_PREVIEW, SimpleEvent); class GLToolbarItem { public: + typedef std::function ActionCallback; + typedef std::function VisibilityCallback; + typedef std::function EnabledStateCallback; + enum EType : unsigned char { Action, @@ -57,12 +61,18 @@ public: std::string tooltip; unsigned int sprite_id; bool is_toggable; - wxEventType action_event; bool visible; + ActionCallback action_callback; + VisibilityCallback visibility_callback; + EnabledStateCallback enabled_state_callback; Data(); }; + static const ActionCallback Default_Action_Callback; + static const VisibilityCallback Default_Visibility_Callback; + static const EnabledStateCallback Default_Enabled_State_Callback; + private: EType m_type; EState m_state; @@ -80,7 +90,7 @@ public: #endif // ENABLE_SVG_ICONS const std::string& get_tooltip() const { return m_data.tooltip; } - void do_action(wxEvtHandler *target); + void do_action() { m_data.action_callback(); } bool is_enabled() const { return m_state != Disabled; } bool is_disabled() const { return m_state == Disabled; } @@ -89,13 +99,20 @@ public: bool is_toggable() const { return m_data.is_toggable; } bool is_visible() const { return m_data.visible; } - void set_visible(bool visible) { m_data.visible = visible; } bool is_separator() const { return m_type == Separator; } + // returns true if the state changes + bool update_visibility(); + // returns true if the state changes + bool update_enabled_state(); + void render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const; private: GLTexture::Quad_UVs get_uvs(unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const; + void set_visible(bool visible) { m_data.visible = visible; } + + friend class GLToolbar; }; #if !ENABLE_SVG_ICONS @@ -214,6 +231,18 @@ private: ItemsList m_items; + struct MouseCapture + { + bool left; + bool middle; + bool right; + + bool any() const { return left || middle || right; } + }; + + MouseCapture m_mouse_capture; + std::string m_tooltip; + public: #if ENABLE_SVG_ICONS GLToolbar(EType type, const std::string& name); @@ -253,24 +282,22 @@ public: float get_width() const; float get_height() const; - void enable_item(const std::string& name); - void disable_item(const std::string& name); void select_item(const std::string& name); bool is_item_pressed(const std::string& name) const; bool is_item_disabled(const std::string& name) const; bool is_item_visible(const std::string& name) const; - void set_item_visible(const std::string& name, bool visible); - std::string update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent); + const std::string& get_tooltip() const { return m_tooltip; } - // returns the id of the item under the given mouse position or -1 if none - int contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; - void do_action(unsigned int item_id, GLCanvas3D& parent); + // returns true if any item changed its state + bool update_items_state(); void render(const GLCanvas3D& parent) const; + bool on_mouse(wxMouseEvent& evt, GLCanvas3D& parent); + private: void calc_layout() const; float get_width_horizontal() const; @@ -278,8 +305,12 @@ private: float get_height_horizontal() const; float get_height_vertical() const; float get_main_size() const; + void do_action(unsigned int item_id, GLCanvas3D& parent); + std::string update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent); std::string update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent); std::string update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent); + // returns the id of the item under the given mouse position or -1 if none + int contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; int contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; int contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; @@ -289,6 +320,11 @@ private: #if ENABLE_SVG_ICONS bool generate_icons_texture() const; #endif // ENABLE_SVG_ICONS + + // returns true if any item changed its state + bool update_items_visibility(); + // returns true if any item changed its state + bool update_items_enabled_state(); }; } // namespace GUI diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 51019eca63..dae06d6b10 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -10,6 +10,7 @@ #include "libslic3r/Model.hpp" #include "LambdaObjectDialog.hpp" #include "GLCanvas3D.hpp" +#include "Selection.hpp" #include #include "slic3r/Utils/FixModelByWin10.hpp" @@ -1274,7 +1275,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode if (obj_idx < 0) return; - const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); assert(obj_idx == selection.get_object_idx()); /** Any changes of the Object's composition is duplicated for all Object's Instances @@ -1562,7 +1563,7 @@ bool ObjectList::selected_instances_of_same_object() bool ObjectList::can_split_instances() { - const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); return selection.is_multiple_full_instance() || selection.is_single_full_instance(); } @@ -1872,7 +1873,7 @@ bool ObjectList::multiple_selection() const void ObjectList::update_selections() { - const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); wxDataViewItemArray sels; // We doesn't update selection if SettingsItem for the current object/part is selected @@ -1970,7 +1971,7 @@ void ObjectList::update_selections() void ObjectList::update_selections_on_canvas() { - GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); const int sel_cnt = GetSelectedItemsCount(); if (sel_cnt == 0) { @@ -1979,8 +1980,8 @@ void ObjectList::update_selections_on_canvas() return; } - auto add_to_selection = [this](const wxDataViewItem& item, GLCanvas3D::Selection& selection, bool as_single_selection) - { + auto add_to_selection = [this](const wxDataViewItem& item, Selection& selection, bool as_single_selection) + { if (m_objects_model->GetParent(item) == wxDataViewItem(0)) { selection.add_object(m_objects_model->GetIdByItem(item), as_single_selection); return; @@ -2239,7 +2240,7 @@ void ObjectList::instances_to_separated_object(const int obj_idx, const std::set void ObjectList::split_instances() { - const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); const int obj_idx = selection.get_object_idx(); if (obj_idx == -1) return; diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index ab83df05dd..f759250b35 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -7,6 +7,7 @@ #include "PresetBundle.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/Geometry.hpp" +#include "Selection.hpp" #include @@ -155,7 +156,7 @@ void ObjectManipulation::UpdateAndShow(const bool show) OG_Settings::UpdateAndShow(show); } -void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& selection) +void ObjectManipulation::update_settings_value(const Selection& selection) { m_new_move_label_string = L("Position"); m_new_rotate_label_string = L("Rotation"); @@ -348,7 +349,7 @@ void ObjectManipulation::reset_settings_value() void ObjectManipulation::change_position_value(const Vec3d& position) { auto canvas = wxGetApp().plater()->canvas3D(); - GLCanvas3D::Selection& selection = canvas->get_selection(); + Selection& selection = canvas->get_selection(); selection.start_dragging(); selection.translate(position - m_cache.position, selection.requires_local_axes()); canvas->do_move(); @@ -359,13 +360,13 @@ void ObjectManipulation::change_position_value(const Vec3d& position) void ObjectManipulation::change_rotation_value(const Vec3d& rotation) { GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); - const GLCanvas3D::Selection& selection = canvas->get_selection(); + const Selection& selection = canvas->get_selection(); - GLCanvas3D::TransformationType transformation_type(GLCanvas3D::TransformationType::World_Relative_Joint); - if (selection.is_single_full_instance() || selection.requires_local_axes()) + TransformationType transformation_type(TransformationType::World_Relative_Joint); + if (selection.is_single_full_instance() || selection.requires_local_axes()) transformation_type.set_independent(); if (selection.is_single_full_instance()) { - //FIXME GLCanvas3D::Selection::rotate() does not process absoulte rotations correctly: It does not recognize the axis index, which was changed. + //FIXME Selection::rotate() does not process absoulte rotations correctly: It does not recognize the axis index, which was changed. // transformation_type.set_absolute(); transformation_type.set_local(); } @@ -384,7 +385,7 @@ void ObjectManipulation::change_rotation_value(const Vec3d& rotation) void ObjectManipulation::change_scale_value(const Vec3d& scale) { Vec3d scaling_factor = scale; - const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); if (m_uniform_scale || selection.requires_uniform_scale()) { Vec3d abs_scale_diff = (scale - m_cache.scale).cwiseAbs(); @@ -418,7 +419,7 @@ void ObjectManipulation::change_scale_value(const Vec3d& scale) void ObjectManipulation::change_size_value(const Vec3d& size) { - const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); Vec3d ref_size = m_cache.size; if (selection.is_single_volume() || selection.is_single_modifier()) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index 16160c84d1..a5a180a56b 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -12,6 +12,7 @@ class PrusaLockButton; namespace Slic3r { namespace GUI { +class Selection; class ObjectManipulation : public OG_Settings { @@ -90,7 +91,7 @@ public: bool IsShown() override; void UpdateAndShow(const bool show) override; - void update_settings_value(const GLCanvas3D::Selection& selection); + void update_settings_value(const Selection& selection); // Called from the App to update the UI if dirty. void update_if_dirty(); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index a2c2dbc391..be3fad9ece 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -110,18 +110,6 @@ void View3D::mirror_selection(Axis axis) m_canvas->mirror_selection(axis); } -void View3D::update_toolbar_items_visibility() -{ - if (m_canvas != nullptr) - m_canvas->update_toolbar_items_visibility(); -} - -void View3D::enable_toolbar_item(const std::string& name, bool enable) -{ - if (m_canvas != nullptr) - m_canvas->enable_toolbar_item(name, enable); -} - int View3D::check_volumes_outside_state() const { return (m_canvas != nullptr) ? m_canvas->check_volumes_outside_state() : false; diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 2165835805..182eaa9528 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -50,8 +50,6 @@ public: void delete_selected(); void mirror_selection(Axis axis); - void update_toolbar_items_visibility(); - void enable_toolbar_item(const std::string& name, bool enable); int check_volumes_outside_state() const; bool is_layers_editing_enabled() const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 290541a180..1e12674ec8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -184,7 +184,7 @@ void GLGizmoBase::disable_grabber(unsigned int id) on_disable_grabber(id); } -void GLGizmoBase::start_dragging(const GLCanvas3D::Selection& selection) +void GLGizmoBase::start_dragging(const Selection& selection) { m_dragging = true; @@ -208,7 +208,7 @@ void GLGizmoBase::stop_dragging() on_stop_dragging(); } -void GLGizmoBase::update(const UpdateData& data, const GLCanvas3D::Selection& selection) +void GLGizmoBase::update(const UpdateData& data, const Selection& selection) { if (m_hover_id != -1) on_update(data, selection); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index e8328bcd4a..b70c1f8df1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -5,6 +5,7 @@ #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/I18N.hpp" +#include "slic3r/GUI/Selection.hpp" class wxWindow; @@ -126,7 +127,7 @@ public: const std::string& get_icon_filename() const { return m_icon_filename; } #endif // ENABLE_SVG_ICONS - bool is_activable(const GLCanvas3D::Selection& selection) const { return on_is_activable(selection); } + bool is_activable(const Selection& selection) const { return on_is_activable(selection); } bool is_selectable() const { return on_is_selectable(); } unsigned int get_sprite_id() const { return m_sprite_id; } @@ -139,31 +140,31 @@ public: void enable_grabber(unsigned int id); void disable_grabber(unsigned int id); - void start_dragging(const GLCanvas3D::Selection& selection); + void start_dragging(const Selection& selection); void stop_dragging(); bool is_dragging() const { return m_dragging; } - void update(const UpdateData& data, const GLCanvas3D::Selection& selection); + void update(const UpdateData& data, const Selection& selection); - void render(const GLCanvas3D::Selection& selection) const { on_render(selection); } - void render_for_picking(const GLCanvas3D::Selection& selection) const { on_render_for_picking(selection); } - void render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection) { on_render_input_window(x, y, bottom_limit, selection); } + void render(const Selection& selection) const { on_render(selection); } + void render_for_picking(const Selection& selection) const { on_render_for_picking(selection); } + void render_input_window(float x, float y, float bottom_limit, const Selection& selection) { on_render_input_window(x, y, bottom_limit, selection); } protected: virtual bool on_init() = 0; virtual std::string on_get_name() const = 0; virtual void on_set_state() {} virtual void on_set_hover_id() {} - virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return true; } + virtual bool on_is_activable(const Selection& selection) const { return true; } virtual bool on_is_selectable() const { return true; } virtual void on_enable_grabber(unsigned int id) {} virtual void on_disable_grabber(unsigned int id) {} - virtual void on_start_dragging(const GLCanvas3D::Selection& selection) {} + virtual void on_start_dragging(const Selection& selection) {} virtual void on_stop_dragging() {} - virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection) = 0; - virtual void on_render(const GLCanvas3D::Selection& selection) const = 0; - virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const = 0; - virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection) {} + virtual void on_update(const UpdateData& data, const Selection& selection) = 0; + virtual void on_render(const Selection& selection) const = 0; + virtual void on_render_for_picking(const Selection& selection) const = 0; + virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) {} // Returns the picking color for the given id, based on the BASE_ID constant // No check is made for clashing with other picking color (i.e. GLVolumes) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 54a39c6d51..e228706c05 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -101,12 +101,12 @@ void GLGizmoCut::on_set_state() } } -bool GLGizmoCut::on_is_activable(const GLCanvas3D::Selection& selection) const +bool GLGizmoCut::on_is_activable(const Selection& selection) const { return selection.is_single_full_instance() && !selection.is_wipe_tower(); } -void GLGizmoCut::on_start_dragging(const GLCanvas3D::Selection& selection) +void GLGizmoCut::on_start_dragging(const Selection& selection) { if (m_hover_id == -1) { return; } @@ -118,14 +118,14 @@ void GLGizmoCut::on_start_dragging(const GLCanvas3D::Selection& selection) m_drag_center(2) = m_cut_z; } -void GLGizmoCut::on_update(const UpdateData& data, const GLCanvas3D::Selection& selection) +void GLGizmoCut::on_update(const UpdateData& data, const Selection& selection) { if (m_hover_id != -1) { set_cut_z(m_start_z + calc_projection(data.mouse_ray)); } } -void GLGizmoCut::on_render(const GLCanvas3D::Selection& selection) const +void GLGizmoCut::on_render(const Selection& selection) const { if (m_grabbers[0].dragging) { set_tooltip("Z: " + format(m_cut_z, 2)); @@ -176,14 +176,14 @@ void GLGizmoCut::on_render(const GLCanvas3D::Selection& selection) const m_grabbers[0].render(m_hover_id == 0, box.max_size()); } -void GLGizmoCut::on_render_for_picking(const GLCanvas3D::Selection& selection) const +void GLGizmoCut::on_render_for_picking(const Selection& selection) const { ::glDisable(GL_DEPTH_TEST); render_grabbers_for_picking(selection.get_bounding_box()); } -void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection) +void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) { m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); m_imgui->set_next_window_bg_alpha(0.5f); @@ -207,7 +207,7 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit, co } } -void GLGizmoCut::update_max_z(const GLCanvas3D::Selection& selection) const +void GLGizmoCut::update_max_z(const Selection& selection) const { m_max_z = selection.get_bounding_box().size()(2); set_cut_z(m_cut_z); @@ -219,7 +219,7 @@ void GLGizmoCut::set_cut_z(double cut_z) const m_cut_z = std::max(0.0, std::min(m_max_z, cut_z)); } -void GLGizmoCut::perform_cut(const GLCanvas3D::Selection& selection) +void GLGizmoCut::perform_cut(const Selection& selection) { const auto instance_idx = selection.get_instance_idx(); const auto object_idx = selection.get_object_idx(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index beec6d1da7..fd4e8d8dc0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -33,17 +33,17 @@ protected: virtual bool on_init(); virtual std::string on_get_name() const; virtual void on_set_state(); - virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const; - virtual void on_start_dragging(const GLCanvas3D::Selection& selection); - virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection); - virtual void on_render(const GLCanvas3D::Selection& selection) const; - virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; - virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection); + virtual bool on_is_activable(const Selection& selection) const; + virtual void on_start_dragging(const Selection& selection); + virtual void on_update(const UpdateData& data, const Selection& selection); + virtual void on_render(const Selection& selection) const; + virtual void on_render_for_picking(const Selection& selection) const; + virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection); private: - void update_max_z(const GLCanvas3D::Selection& selection) const; + void update_max_z(const Selection& selection) const; void set_cut_z(double cut_z) const; - void perform_cut(const GLCanvas3D::Selection& selection); + void perform_cut(const Selection& selection); double calc_projection(const Linef3& mouse_ray) const; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index d72dc49138..2ed0486772 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -32,12 +32,12 @@ std::string GLGizmoFlatten::on_get_name() const return L("Place on face [F]"); } -bool GLGizmoFlatten::on_is_activable(const GLCanvas3D::Selection& selection) const +bool GLGizmoFlatten::on_is_activable(const Selection& selection) const { return selection.is_single_full_instance(); } -void GLGizmoFlatten::on_start_dragging(const GLCanvas3D::Selection& selection) +void GLGizmoFlatten::on_start_dragging(const Selection& selection) { if (m_hover_id != -1) { @@ -47,7 +47,7 @@ void GLGizmoFlatten::on_start_dragging(const GLCanvas3D::Selection& selection) } } -void GLGizmoFlatten::on_render(const GLCanvas3D::Selection& selection) const +void GLGizmoFlatten::on_render(const Selection& selection) const { ::glClear(GL_DEPTH_BUFFER_BIT); @@ -83,7 +83,7 @@ void GLGizmoFlatten::on_render(const GLCanvas3D::Selection& selection) const ::glDisable(GL_BLEND); } -void GLGizmoFlatten::on_render_for_picking(const GLCanvas3D::Selection& selection) const +void GLGizmoFlatten::on_render_for_picking(const Selection& selection) const { ::glDisable(GL_DEPTH_TEST); ::glDisable(GL_BLEND); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp index c91924c5ac..1bd17e5ef1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp @@ -49,11 +49,11 @@ public: protected: virtual bool on_init(); virtual std::string on_get_name() const; - virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const; - virtual void on_start_dragging(const GLCanvas3D::Selection& selection); - virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection) {} - virtual void on_render(const GLCanvas3D::Selection& selection) const; - virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; + virtual bool on_is_activable(const Selection& selection) const; + virtual void on_start_dragging(const Selection& selection); + virtual void on_update(const UpdateData& data, const Selection& selection) {} + virtual void on_render(const Selection& selection) const; + virtual void on_render_for_picking(const Selection& selection) const; virtual void on_set_state() { if (m_state == On && is_plane_update_necessary()) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index d18d71c83d..03151c4336 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -51,7 +51,7 @@ std::string GLGizmoMove3D::on_get_name() const return L("Move [M]"); } -void GLGizmoMove3D::on_start_dragging(const GLCanvas3D::Selection& selection) +void GLGizmoMove3D::on_start_dragging(const Selection& selection) { if (m_hover_id != -1) { @@ -69,7 +69,7 @@ void GLGizmoMove3D::on_stop_dragging() m_displacement = Vec3d::Zero(); } -void GLGizmoMove3D::on_update(const UpdateData& data, const GLCanvas3D::Selection& selection) +void GLGizmoMove3D::on_update(const UpdateData& data, const Selection& selection) { if (m_hover_id == 0) m_displacement(0) = calc_projection(data); @@ -79,7 +79,7 @@ void GLGizmoMove3D::on_update(const UpdateData& data, const GLCanvas3D::Selectio m_displacement(2) = calc_projection(data); } -void GLGizmoMove3D::on_render(const GLCanvas3D::Selection& selection) const +void GLGizmoMove3D::on_render(const Selection& selection) const { bool show_position = selection.is_single_full_instance(); const Vec3d& position = selection.get_bounding_box().center(); @@ -155,7 +155,7 @@ void GLGizmoMove3D::on_render(const GLCanvas3D::Selection& selection) const } } -void GLGizmoMove3D::on_render_for_picking(const GLCanvas3D::Selection& selection) const +void GLGizmoMove3D::on_render_for_picking(const Selection& selection) const { ::glDisable(GL_DEPTH_TEST); @@ -166,7 +166,7 @@ void GLGizmoMove3D::on_render_for_picking(const GLCanvas3D::Selection& selection render_grabber_extension(Z, box, true); } -void GLGizmoMove3D::on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection) +void GLGizmoMove3D::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) { #if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI bool show_position = selection.is_single_full_instance(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp index d4d97de7f7..ddab2b777d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp @@ -37,12 +37,12 @@ public: protected: virtual bool on_init(); virtual std::string on_get_name() const; - virtual void on_start_dragging(const GLCanvas3D::Selection& selection); + virtual void on_start_dragging(const Selection& selection); virtual void on_stop_dragging(); - virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection); - virtual void on_render(const GLCanvas3D::Selection& selection) const; - virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; - virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection); + virtual void on_update(const UpdateData& data, const Selection& selection); + virtual void on_render(const Selection& selection) const; + virtual void on_render_for_picking(const Selection& selection) const; + virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection); private: double calc_projection(const UpdateData& data) const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 4c6f66f4c1..72b9ad18ac 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -80,7 +80,7 @@ bool GLGizmoRotate::on_init() return true; } -void GLGizmoRotate::on_start_dragging(const GLCanvas3D::Selection& selection) +void GLGizmoRotate::on_start_dragging(const Selection& selection) { const BoundingBoxf3& box = selection.get_bounding_box(); m_center = box.center(); @@ -91,7 +91,7 @@ void GLGizmoRotate::on_start_dragging(const GLCanvas3D::Selection& selection) m_snap_fine_out_radius = m_snap_fine_in_radius + m_radius * ScaleLongTooth; } -void GLGizmoRotate::on_update(const UpdateData& data, const GLCanvas3D::Selection& selection) +void GLGizmoRotate::on_update(const UpdateData& data, const Selection& selection) { Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(data.mouse_ray, selection)); @@ -126,7 +126,7 @@ void GLGizmoRotate::on_update(const UpdateData& data, const GLCanvas3D::Selectio m_angle = theta; } -void GLGizmoRotate::on_render(const GLCanvas3D::Selection& selection) const +void GLGizmoRotate::on_render(const Selection& selection) const { if (!m_grabbers[0].enabled) return; @@ -183,7 +183,7 @@ void GLGizmoRotate::on_render(const GLCanvas3D::Selection& selection) const ::glPopMatrix(); } -void GLGizmoRotate::on_render_for_picking(const GLCanvas3D::Selection& selection) const +void GLGizmoRotate::on_render_for_picking(const Selection& selection) const { ::glDisable(GL_DEPTH_TEST); @@ -347,7 +347,7 @@ void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool pick ::glDisable(GL_LIGHTING); } -void GLGizmoRotate::transform_to_local(const GLCanvas3D::Selection& selection) const +void GLGizmoRotate::transform_to_local(const Selection& selection) const { ::glTranslated(m_center(0), m_center(1), m_center(2)); @@ -380,7 +380,7 @@ void GLGizmoRotate::transform_to_local(const GLCanvas3D::Selection& selection) c } } -Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, const GLCanvas3D::Selection& selection) const +Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, const Selection& selection) const { double half_pi = 0.5 * (double)PI; @@ -457,7 +457,7 @@ std::string GLGizmoRotate3D::on_get_name() const return L("Rotate [R]"); } -void GLGizmoRotate3D::on_start_dragging(const GLCanvas3D::Selection& selection) +void GLGizmoRotate3D::on_start_dragging(const Selection& selection) { if ((0 <= m_hover_id) && (m_hover_id < 3)) m_gizmos[m_hover_id].start_dragging(selection); @@ -469,7 +469,7 @@ void GLGizmoRotate3D::on_stop_dragging() m_gizmos[m_hover_id].stop_dragging(); } -void GLGizmoRotate3D::on_render(const GLCanvas3D::Selection& selection) const +void GLGizmoRotate3D::on_render(const Selection& selection) const { ::glClear(GL_DEPTH_BUFFER_BIT); @@ -483,7 +483,7 @@ void GLGizmoRotate3D::on_render(const GLCanvas3D::Selection& selection) const m_gizmos[Z].render(selection); } -void GLGizmoRotate3D::on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection) +void GLGizmoRotate3D::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) { #if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI Vec3d rotation(Geometry::rad2deg(m_gizmos[0].get_angle()), Geometry::rad2deg(m_gizmos[1].get_angle()), Geometry::rad2deg(m_gizmos[2].get_angle())); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index aca64c94e9..f5946aa562 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -52,10 +52,10 @@ public: protected: virtual bool on_init(); virtual std::string on_get_name() const { return ""; } - virtual void on_start_dragging(const GLCanvas3D::Selection& selection); - virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection); - virtual void on_render(const GLCanvas3D::Selection& selection) const; - virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; + virtual void on_start_dragging(const Selection& selection); + virtual void on_update(const UpdateData& data, const Selection& selection); + virtual void on_render(const Selection& selection) const; + virtual void on_render_for_picking(const Selection& selection) const; private: void render_circle() const; @@ -66,9 +66,9 @@ private: void render_grabber(const BoundingBoxf3& box) const; void render_grabber_extension(const BoundingBoxf3& box, bool picking) const; - void transform_to_local(const GLCanvas3D::Selection& selection) const; + void transform_to_local(const Selection& selection) const; // returns the intersection of the mouse ray with the plane perpendicular to the gizmo axis, in local coordinate - Vec3d mouse_position_in_local_plane(const Linef3& mouse_ray, const GLCanvas3D::Selection& selection) const; + Vec3d mouse_position_in_local_plane(const Linef3& mouse_ray, const Selection& selection) const; }; class GLGizmoRotate3D : public GLGizmoBase @@ -102,7 +102,7 @@ protected: m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1); } } - virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return !selection.is_wipe_tower(); } + virtual bool on_is_activable(const Selection& selection) const { return !selection.is_wipe_tower(); } virtual void on_enable_grabber(unsigned int id) { if ((0 <= id) && (id < 3)) @@ -113,17 +113,17 @@ protected: if ((0 <= id) && (id < 3)) m_gizmos[id].disable_grabber(0); } - virtual void on_start_dragging(const GLCanvas3D::Selection& selection); + virtual void on_start_dragging(const Selection& selection); virtual void on_stop_dragging(); - virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection) + virtual void on_update(const UpdateData& data, const Selection& selection) { for (GLGizmoRotate& g : m_gizmos) { g.update(data, selection); } } - virtual void on_render(const GLCanvas3D::Selection& selection) const; - virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const + virtual void on_render(const Selection& selection) const; + virtual void on_render_for_picking(const Selection& selection) const { for (const GLGizmoRotate& g : m_gizmos) { @@ -131,7 +131,7 @@ protected: } } - virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection); + virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection); }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index acc44e00d1..43152b56c6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -51,7 +51,7 @@ std::string GLGizmoScale3D::on_get_name() const return L("Scale [S]"); } -void GLGizmoScale3D::on_start_dragging(const GLCanvas3D::Selection& selection) +void GLGizmoScale3D::on_start_dragging(const Selection& selection) { if (m_hover_id != -1) { @@ -60,7 +60,7 @@ void GLGizmoScale3D::on_start_dragging(const GLCanvas3D::Selection& selection) } } -void GLGizmoScale3D::on_update(const UpdateData& data, const GLCanvas3D::Selection& selection) +void GLGizmoScale3D::on_update(const UpdateData& data, const Selection& selection) { if ((m_hover_id == 0) || (m_hover_id == 1)) do_scale_x(data); @@ -72,7 +72,7 @@ void GLGizmoScale3D::on_update(const UpdateData& data, const GLCanvas3D::Selecti do_scale_uniform(data); } -void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const +void GLGizmoScale3D::on_render(const Selection& selection) const { bool single_instance = selection.is_single_full_instance(); bool single_volume = selection.is_single_modifier() || selection.is_single_volume(); @@ -121,7 +121,7 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const if (single_instance) { // calculate bounding box in instance local reference system - const GLCanvas3D::Selection::IndicesList& idxs = selection.get_volume_idxs(); + const Selection::IndicesList& idxs = selection.get_volume_idxs(); for (unsigned int idx : idxs) { const GLVolume* vol = selection.get_volume(idx); @@ -267,14 +267,14 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const } } -void GLGizmoScale3D::on_render_for_picking(const GLCanvas3D::Selection& selection) const +void GLGizmoScale3D::on_render_for_picking(const Selection& selection) const { ::glDisable(GL_DEPTH_TEST); render_grabbers_for_picking(selection.get_bounding_box()); } -void GLGizmoScale3D::on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection) +void GLGizmoScale3D::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) { #if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI bool single_instance = selection.is_single_full_instance(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp index 8554e57a74..6e14a361e8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp @@ -37,12 +37,12 @@ public: protected: virtual bool on_init(); virtual std::string on_get_name() const; - virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return !selection.is_wipe_tower(); } - virtual void on_start_dragging(const GLCanvas3D::Selection& selection); - virtual void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection); - virtual void on_render(const GLCanvas3D::Selection& selection) const; - virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; - virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection); + virtual bool on_is_activable(const Selection& selection) const { return !selection.is_wipe_tower(); } + virtual void on_start_dragging(const Selection& selection); + virtual void on_update(const UpdateData& data, const Selection& selection); + virtual void on_render(const Selection& selection) const; + virtual void on_render_for_picking(const Selection& selection) const; + virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection); private: void render_grabbers_connection(unsigned int id_1, unsigned int id_2) const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 695564971a..56452ae730 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -42,7 +42,7 @@ bool GLGizmoSlaSupports::on_init() return true; } -void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection) +void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const Selection& selection) { m_starting_center = Vec3d::Zero(); m_old_model_object = m_model_object; @@ -72,7 +72,7 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const G } } -void GLGizmoSlaSupports::on_render(const GLCanvas3D::Selection& selection) const +void GLGizmoSlaSupports::on_render(const Selection& selection) const { ::glEnable(GL_BLEND); ::glEnable(GL_DEPTH_TEST); @@ -123,14 +123,14 @@ void GLGizmoSlaSupports::render_selection_rectangle() const ::glPopAttrib(); // restore former MatrixMode } -void GLGizmoSlaSupports::on_render_for_picking(const GLCanvas3D::Selection& selection) const +void GLGizmoSlaSupports::on_render_for_picking(const Selection& selection) const { ::glEnable(GL_DEPTH_TEST); render_points(selection, true); } -void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, bool picking) const +void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const { if (m_quadric == nullptr || !selection.is_from_single_instance()) return; @@ -201,12 +201,12 @@ void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, b const float cone_height = 0.75f; ::glPushMatrix(); ::glTranslatef(0.f, 0.f, m_editing_mode_cache[i].support_point.head_front_radius * RenderPointScale); - ::gluCylinder(m_quadric, 0.f, cone_radius, cone_height, 36, 1); + ::gluCylinder(m_quadric, 0.f, cone_radius, cone_height, 24, 1); ::glTranslatef(0.f, 0.f, cone_height); - ::gluDisk(m_quadric, 0.0, cone_radius, 36, 1); + ::gluDisk(m_quadric, 0.0, cone_radius, 24, 1); ::glPopMatrix(); } - ::gluSphere(m_quadric, m_editing_mode_cache[i].support_point.head_front_radius * RenderPointScale, 64, 36); + ::gluSphere(m_quadric, m_editing_mode_cache[i].support_point.head_front_radius * RenderPointScale, 24, 12); ::glPopMatrix(); } @@ -276,7 +276,7 @@ std::pair GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse igl::Hit hit; - const GLCanvas3D::Selection& selection = m_parent.get_selection(); + const Selection& selection = m_parent.get_selection(); const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); double z_offset = volume->get_sla_shift_z(); @@ -380,7 +380,7 @@ bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mous GLdouble projection_matrix[16]; ::glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix); - const GLCanvas3D::Selection& selection = m_parent.get_selection(); + const Selection& selection = m_parent.get_selection(); const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); double z_offset = volume->get_sla_shift_z(); @@ -484,7 +484,7 @@ void GLGizmoSlaSupports::delete_selected_points(bool force) //m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } -void GLGizmoSlaSupports::on_update(const UpdateData& data, const GLCanvas3D::Selection& selection) +void GLGizmoSlaSupports::on_update(const UpdateData& data, const Selection& selection) { if (m_editing_mode && m_hover_id != -1 && data.mouse_pos && (!m_editing_mode_cache[m_hover_id].support_point.is_new_island || !m_lock_unique_islands)) { std::pair pos_and_normal; @@ -542,7 +542,7 @@ void GLGizmoSlaSupports::update_cache_entry_normal(unsigned int i) const -void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection) +void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) { if (!m_model_object) return; @@ -686,14 +686,14 @@ RENDER_AGAIN: m_parent.set_as_dirty(); } -bool GLGizmoSlaSupports::on_is_activable(const GLCanvas3D::Selection& selection) const +bool GLGizmoSlaSupports::on_is_activable(const Selection& selection) const { if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA || !selection.is_from_single_instance()) return false; // Check that none of the selected volumes is outside. - const GLCanvas3D::Selection::IndicesList& list = selection.get_volume_idxs(); + const Selection::IndicesList& list = selection.get_volume_idxs(); for (const auto& idx : list) if (selection.get_volume(idx)->is_outside) return false; @@ -750,7 +750,7 @@ void GLGizmoSlaSupports::on_set_state() -void GLGizmoSlaSupports::on_start_dragging(const GLCanvas3D::Selection& selection) +void GLGizmoSlaSupports::on_start_dragging(const Selection& selection) { if (m_hover_id != -1) { select_point(NoPoints); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index 492bb85ca0..c27ff0b6d7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -58,19 +58,19 @@ public: GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_id); #endif // ENABLE_SVG_ICONS virtual ~GLGizmoSlaSupports(); - void set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection); + void set_sla_support_data(ModelObject* model_object, const Selection& selection); bool mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down); void delete_selected_points(bool force = false); std::pair get_sla_clipping_plane() const; private: bool on_init(); - void on_update(const UpdateData& data, const GLCanvas3D::Selection& selection); - virtual void on_render(const GLCanvas3D::Selection& selection) const; - virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; + void on_update(const UpdateData& data, const Selection& selection); + virtual void on_render(const Selection& selection) const; + virtual void on_render_for_picking(const Selection& selection) const; void render_selection_rectangle() const; - void render_points(const GLCanvas3D::Selection& selection, bool picking = false) const; + void render_points(const Selection& selection, bool picking = false) const; bool is_mesh_update_necessary() const; void update_mesh(); void update_cache_entry_normal(unsigned int i) const; @@ -113,11 +113,11 @@ private: protected: void on_set_state() override; - void on_start_dragging(const GLCanvas3D::Selection& selection) override; - virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection) override; + void on_start_dragging(const Selection& selection) override; + virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) override; virtual std::string on_get_name() const; - virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const; + virtual bool on_is_activable(const Selection& selection) const; virtual bool on_is_selectable() const; }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 52b31b7c1e..5570431181 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -47,6 +47,7 @@ #include "MainFrame.hpp" #include "3DScene.hpp" #include "GLCanvas3D.hpp" +#include "Selection.hpp" #include "GLToolbar.hpp" #include "GUI_Preview.hpp" #include "3DBed.hpp" @@ -1198,8 +1199,8 @@ struct Plater::priv std::vector load_model_objects(const ModelObjectPtrs &model_objects); std::unique_ptr get_export_file(GUI::FileType file_type); - const GLCanvas3D::Selection& get_selection() const; - GLCanvas3D::Selection& get_selection(); + const Selection& get_selection() const; + Selection& get_selection(); int get_selected_object_idx() const; int get_selected_volume_idx() const; void selection_changed(); @@ -1271,6 +1272,15 @@ struct Plater::priv // Sets m_bed.m_polygon to limit the object placement. void set_bed_shape(const Pointfs& shape); + bool can_delete() const; + bool can_delete_all() const; + bool can_increase_instances() const; + bool can_decrease_instances() const; + bool can_split_to_objects() const; + bool can_split_to_volumes() const; + bool can_arrange() const; + bool can_layers_editing() const; + private: bool init_object_menu(); bool init_common_menu(wxMenu* menu, const bool is_part = false); @@ -1279,16 +1289,9 @@ private: bool complit_init_part_menu(); void init_view_toolbar(); - bool can_delete_object() const; - bool can_increase_instances() const; - bool can_decrease_instances() const; bool can_set_instance_to_object() const; - bool can_split_to_objects() const; - bool can_split_to_volumes() const; bool can_split() const; bool layers_height_allowed() const; - bool can_delete_all() const; - bool can_arrange() const; bool can_mirror() const; void update_fff_scene(); @@ -1669,7 +1672,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ // automatic selection of added objects if (!obj_idxs.empty() && (view3D != nullptr)) { - GLCanvas3D::Selection& selection = view3D->get_canvas3d()->get_selection(); + Selection& selection = view3D->get_canvas3d()->get_selection(); selection.clear(); for (size_t idx : obj_idxs) { @@ -1818,12 +1821,12 @@ std::unique_ptr Plater::priv::get_export_file(GUI::FileType return dlg; } -const GLCanvas3D::Selection& Plater::priv::get_selection() const +const Selection& Plater::priv::get_selection() const { return view3D->get_canvas3d()->get_selection(); } -GLCanvas3D::Selection& Plater::priv::get_selection() +Selection& Plater::priv::get_selection() { return view3D->get_canvas3d()->get_selection(); } @@ -1848,12 +1851,6 @@ int Plater::priv::get_selected_volume_idx() const void Plater::priv::selection_changed() { - view3D->enable_toolbar_item("delete", can_delete_object()); - view3D->enable_toolbar_item("more", can_increase_instances()); - view3D->enable_toolbar_item("fewer", can_decrease_instances()); - view3D->enable_toolbar_item("splitobjects", can_split()); - view3D->enable_toolbar_item("splitvolumes", printer_technology == ptFFF && can_split()); - // if the selection is not valid to allow for layer editing, we need to turn off the tool if it is running bool enable_layer_editing = layers_height_allowed(); if (!enable_layer_editing && view3D->is_layers_editing_enabled()) { @@ -1861,18 +1858,12 @@ void Plater::priv::selection_changed() on_action_layersediting(evt); } - view3D->enable_toolbar_item("layersediting", enable_layer_editing); - // forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears) view3D->render(); } void Plater::priv::object_list_changed() { - // Enable/disable buttons depending on whether there are any objects on the platter. - view3D->enable_toolbar_item("deleteall", can_delete_all()); - view3D->enable_toolbar_item("arrange", can_arrange()); - const bool export_in_progress = this->background_process.is_export_scheduled(); // || ! send_gcode_file.empty()); // XXX: is this right? const bool model_fits = view3D->check_volumes_outside_state() == ModelInstance::PVS_Inside; @@ -1951,9 +1942,6 @@ void Plater::priv::arrange() wxBusyCursor wait; - // Disable the arrange button (to prevent reentrancies, we will call wxYied) - view3D->enable_toolbar_item("arrange", can_arrange()); - this->background_process.stop(); unsigned count = 0; for(auto obj : model.objects) count += obj->instances.size(); @@ -2024,9 +2012,6 @@ void Plater::priv::arrange() statusbar()->set_cancel_callback(); // remove cancel button arranging.store(false); - // We enable back the arrange button - view3D->enable_toolbar_item("arrange", can_arrange()); - // Do a full refresh of scene tree, including regenerating all the GLVolumes. //FIXME The update function shall just reload the modified matrices. update(true); @@ -2587,10 +2572,6 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt) void Plater::priv::on_layer_editing_toggled(bool enable) { view3D->enable_layers_editing(enable); - if (enable && !view3D->is_layers_editing_enabled()) { - // Initialization of the OpenGL shaders failed. Disable the tool. - view3D->enable_toolbar_item("layersediting", false); - } view3D->set_as_dirty(); } @@ -2612,10 +2593,7 @@ void Plater::priv::on_action_split_volumes(SimpleEvent&) void Plater::priv::on_action_layersediting(SimpleEvent&) { - bool enable = !view3D->is_layers_editing_enabled(); - view3D->enable_layers_editing(enable); - if (enable && !view3D->is_layers_editing_enabled()) - view3D->enable_toolbar_item("layersediting", false); + view3D->enable_layers_editing(!view3D->is_layers_editing_enabled()); } void Plater::priv::on_object_select(SimpleEvent& evt) @@ -2775,7 +2753,7 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/ if (q != nullptr) { q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_mirror()); }, item_mirror->GetId()); - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_delete_object()); }, item_delete->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_delete()); }, item_delete->GetId()); } return true; @@ -2880,7 +2858,7 @@ void Plater::priv::init_view_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("3D editor view") + " [" + GUI::shortkey_ctrl_prefix() + "5]"; item.sprite_id = 0; - item.action_event = EVT_GLVIEWTOOLBAR_3D; + item.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_3D)); }; item.is_toggable = false; if (!view_toolbar.add_item(item)) return; @@ -2891,57 +2869,21 @@ void Plater::priv::init_view_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("Preview") + " [" + GUI::shortkey_ctrl_prefix() + "6]"; item.sprite_id = 1; - item.action_event = EVT_GLVIEWTOOLBAR_PREVIEW; + item.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_PREVIEW)); }; item.is_toggable = false; if (!view_toolbar.add_item(item)) return; - view_toolbar.enable_item("3D"); - view_toolbar.enable_item("Preview"); - view_toolbar.select_item("3D"); view_toolbar.set_enabled(true); } -bool Plater::priv::can_delete_object() const -{ - int obj_idx = get_selected_object_idx(); - return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()); -} - -bool Plater::priv::can_increase_instances() const -{ - int obj_idx = get_selected_object_idx(); - return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()); -} - bool Plater::priv::can_set_instance_to_object() const { const int obj_idx = get_selected_object_idx(); return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1); } -bool Plater::priv::can_decrease_instances() const -{ - int obj_idx = get_selected_object_idx(); - return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1); -} - -bool Plater::priv::can_split_to_objects() const -{ - int obj_idx = get_selected_object_idx(); - return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && !model.objects[obj_idx]->is_multiparts(); -} - -bool Plater::priv::can_split_to_volumes() const -{ - if (printer_technology == ptSLA) - return false; -// int obj_idx = get_selected_object_idx(); -// return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && !model.objects[obj_idx]->is_multiparts(); - return sidebar->obj_list()->is_splittable(); -} - bool Plater::priv::can_split() const { return sidebar->obj_list()->is_splittable(); @@ -2953,16 +2895,6 @@ bool Plater::priv::layers_height_allowed() const return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && config->opt_bool("variable_layer_height") && view3D->is_layers_editing_allowed(); } -bool Plater::priv::can_delete_all() const -{ - return !model.objects.empty(); -} - -bool Plater::priv::can_arrange() const -{ - return !model.objects.empty() && !arranging.load(); -} - bool Plater::priv::can_mirror() const { return get_selection().is_from_single_instance(); @@ -2978,11 +2910,51 @@ void Plater::priv::set_bed_shape(const Pointfs& shape) } } +bool Plater::priv::can_delete() const +{ + return !get_selection().is_empty(); +} + +bool Plater::priv::can_delete_all() const +{ + return !model.objects.empty(); +} + +bool Plater::priv::can_increase_instances() const +{ + int obj_idx = get_selected_object_idx(); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()); +} + +bool Plater::priv::can_decrease_instances() const +{ + int obj_idx = get_selected_object_idx(); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1); +} + +bool Plater::priv::can_split_to_objects() const +{ + return can_split(); +} + +bool Plater::priv::can_split_to_volumes() const +{ + return (printer_technology != ptSLA) && can_split(); +} + +bool Plater::priv::can_arrange() const +{ + return !model.objects.empty() && !arranging.load(); +} + +bool Plater::priv::can_layers_editing() const +{ + return layers_height_allowed(); +} + void Plater::priv::update_object_menu() { sidebar->obj_list()->append_menu_items_add_volume(&object_menu); - if (view3D != nullptr) - view3D->update_toolbar_items_visibility(); } void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const @@ -3465,14 +3437,10 @@ void Plater::on_config_change(const DynamicPrintConfig &config) } else if(opt_key == "variable_layer_height") { if (p->config->opt_bool("variable_layer_height") != true) { - p->view3D->enable_toolbar_item("layersediting", false); p->view3D->enable_layers_editing(false); p->view3D->set_as_dirty(); } - else if (p->view3D->is_layers_editing_allowed()) { - p->view3D->enable_toolbar_item("layersediting", true); - } - } + } else if(opt_key == "extruder_colour") { update_scheduled = true; p->preview->set_number_extruders(p->config->option(opt_key)->values.size()); @@ -3593,4 +3561,13 @@ void Plater::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/) void Plater::update_object_menu() { p->update_object_menu(); } +bool Plater::can_delete() const { return p->can_delete(); } +bool Plater::can_delete_all() const { return p->can_delete_all(); } +bool Plater::can_increase_instances() const { return p->can_increase_instances(); } +bool Plater::can_decrease_instances() const { return p->can_decrease_instances(); } +bool Plater::can_split_to_objects() const { return p->can_split_to_objects(); } +bool Plater::can_split_to_volumes() const { return p->can_split_to_volumes(); } +bool Plater::can_arrange() const { return p->can_arrange(); } +bool Plater::can_layers_editing() const { return p->can_layers_editing(); } + }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index d24a9bf32c..f3e195ad09 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -178,6 +178,15 @@ public: PrinterTechnology printer_technology() const; void set_printer_technology(PrinterTechnology printer_technology); + bool can_delete() const; + bool can_delete_all() const; + bool can_increase_instances() const; + bool can_decrease_instances() const; + bool can_split_to_objects() const; + bool can_split_to_volumes() const; + bool can_arrange() const; + bool can_layers_editing() const; + private: struct priv; std::unique_ptr p; diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp new file mode 100644 index 0000000000..e5d221a5f0 --- /dev/null +++ b/src/slic3r/GUI/Selection.cpp @@ -0,0 +1,1670 @@ +#include "libslic3r/libslic3r.h" +#include "Selection.hpp" + +#include "GLCanvas3D.hpp" +#include "GUI_App.hpp" +#include "GUI_ObjectManipulation.hpp" +#include "GUI_ObjectList.hpp" +#include "Gizmos/GLGizmoBase.hpp" + +#include + +#include + +static const float UNIFORM_SCALE_COLOR[3] = { 1.0f, 0.38f, 0.0f }; + +namespace Slic3r { +namespace GUI { + +Selection::VolumeCache::TransformCache::TransformCache() + : position(Vec3d::Zero()) + , rotation(Vec3d::Zero()) + , scaling_factor(Vec3d::Ones()) + , mirror(Vec3d::Ones()) + , rotation_matrix(Transform3d::Identity()) + , scale_matrix(Transform3d::Identity()) + , mirror_matrix(Transform3d::Identity()) + , full_matrix(Transform3d::Identity()) +{ +} + +Selection::VolumeCache::TransformCache::TransformCache(const Geometry::Transformation& transform) + : position(transform.get_offset()) + , rotation(transform.get_rotation()) + , scaling_factor(transform.get_scaling_factor()) + , mirror(transform.get_mirror()) + , full_matrix(transform.get_matrix()) +{ + rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation); + scale_matrix = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scaling_factor); + mirror_matrix = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d::Ones(), mirror); +} + +Selection::VolumeCache::VolumeCache(const Geometry::Transformation& volume_transform, const Geometry::Transformation& instance_transform) + : m_volume(volume_transform) + , m_instance(instance_transform) +{ +} + +Selection::Selection() + : m_volumes(nullptr) + , m_model(nullptr) + , m_mode(Instance) + , m_type(Empty) + , m_valid(false) + , m_bounding_box_dirty(true) + , m_curved_arrow(16) + , m_scale_factor(1.0f) +{ +#if ENABLE_RENDER_SELECTION_CENTER + m_quadric = ::gluNewQuadric(); + if (m_quadric != nullptr) + ::gluQuadricDrawStyle(m_quadric, GLU_FILL); +#endif // ENABLE_RENDER_SELECTION_CENTER +} + +#if ENABLE_RENDER_SELECTION_CENTER +Selection::~Selection() +{ + if (m_quadric != nullptr) + ::gluDeleteQuadric(m_quadric); +} +#endif // ENABLE_RENDER_SELECTION_CENTER + +void Selection::set_volumes(GLVolumePtrs* volumes) +{ + m_volumes = volumes; + _update_valid(); +} + +bool Selection::init(bool useVBOs) +{ + if (!m_arrow.init(useVBOs)) + return false; + + m_arrow.set_scale(5.0 * Vec3d::Ones()); + + if (!m_curved_arrow.init(useVBOs)) + return false; + + m_curved_arrow.set_scale(5.0 * Vec3d::Ones()); + return true; +} + +void Selection::set_model(Model* model) +{ + m_model = model; + _update_valid(); +} + +void Selection::add(unsigned int volume_idx, bool as_single_selection) +{ + if (!m_valid || ((unsigned int)m_volumes->size() <= volume_idx)) + return; + + const GLVolume* volume = (*m_volumes)[volume_idx]; + // wipe tower is already selected + if (is_wipe_tower() && volume->is_wipe_tower) + return; + + // resets the current list if needed + bool needs_reset = as_single_selection; + needs_reset |= volume->is_wipe_tower; + needs_reset |= is_wipe_tower() && !volume->is_wipe_tower; + needs_reset |= !is_modifier() && volume->is_modifier; + needs_reset |= is_modifier() && !volume->is_modifier; + + if (needs_reset) + clear(); + + if (volume->is_modifier) + m_mode = Volume; + else if (!contains_volume(volume_idx)) + m_mode = Instance; + // else -> keep current mode + + switch (m_mode) + { + case Volume: + { + if (volume->volume_idx() >= 0 && (is_empty() || (volume->instance_idx() == get_instance_idx()))) + _add_volume(volume_idx); + + break; + } + case Instance: + { + _add_instance(volume->object_idx(), volume->instance_idx()); + break; + } + } + + _update_type(); + m_bounding_box_dirty = true; +} + +void Selection::remove(unsigned int volume_idx) +{ + if (!m_valid || ((unsigned int)m_volumes->size() <= volume_idx)) + return; + + GLVolume* volume = (*m_volumes)[volume_idx]; + + switch (m_mode) + { + case Volume: + { + _remove_volume(volume_idx); + break; + } + case Instance: + { + _remove_instance(volume->object_idx(), volume->instance_idx()); + break; + } + } + + _update_type(); + m_bounding_box_dirty = true; +} + +void Selection::add_object(unsigned int object_idx, bool as_single_selection) +{ + if (!m_valid) + return; + + // resets the current list if needed + if (as_single_selection) + clear(); + + m_mode = Instance; + + _add_object(object_idx); + + _update_type(); + m_bounding_box_dirty = true; +} + +void Selection::remove_object(unsigned int object_idx) +{ + if (!m_valid) + return; + + _remove_object(object_idx); + + _update_type(); + m_bounding_box_dirty = true; +} + +void Selection::add_instance(unsigned int object_idx, unsigned int instance_idx, bool as_single_selection) +{ + if (!m_valid) + return; + + // resets the current list if needed + if (as_single_selection) + clear(); + + m_mode = Instance; + + _add_instance(object_idx, instance_idx); + + _update_type(); + m_bounding_box_dirty = true; +} + +void Selection::remove_instance(unsigned int object_idx, unsigned int instance_idx) +{ + if (!m_valid) + return; + + _remove_instance(object_idx, instance_idx); + + _update_type(); + m_bounding_box_dirty = true; +} + +void Selection::add_volume(unsigned int object_idx, unsigned int volume_idx, int instance_idx, bool as_single_selection) +{ + if (!m_valid) + return; + + // resets the current list if needed + if (as_single_selection) + clear(); + + m_mode = Volume; + + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) + { + GLVolume* v = (*m_volumes)[i]; + if ((v->object_idx() == object_idx) && (v->volume_idx() == volume_idx)) + { + if ((instance_idx != -1) && (v->instance_idx() == instance_idx)) + _add_volume(i); + } + } + + _update_type(); + m_bounding_box_dirty = true; +} + +void Selection::remove_volume(unsigned int object_idx, unsigned int volume_idx) +{ + if (!m_valid) + return; + + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) + { + GLVolume* v = (*m_volumes)[i]; + if ((v->object_idx() == object_idx) && (v->volume_idx() == volume_idx)) + _remove_volume(i); + } + + _update_type(); + m_bounding_box_dirty = true; +} + +void Selection::add_all() +{ + if (!m_valid) + return; + + m_mode = Instance; + clear(); + + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) + { + if (!(*m_volumes)[i]->is_wipe_tower) + _add_volume(i); + } + + _update_type(); + m_bounding_box_dirty = true; +} + +void Selection::clear() +{ + if (!m_valid) + return; + + for (unsigned int i : m_list) + { + (*m_volumes)[i]->selected = false; + } + + m_list.clear(); + + _update_type(); + m_bounding_box_dirty = true; + + // resets the cache in the sidebar + wxGetApp().obj_manipul()->reset_cache(); +} + +// Update the selection based on the map from old indices to new indices after m_volumes changed. +// If the current selection is by instance, this call may select newly added volumes, if they belong to already selected instances. +void Selection::volumes_changed(const std::vector &map_volume_old_to_new) +{ + assert(m_valid); + + // 1) Update the selection set. + IndicesList list_new; + std::vector> model_instances; + for (unsigned int idx : m_list) { + if (map_volume_old_to_new[idx] != size_t(-1)) { + unsigned int new_idx = (unsigned int)map_volume_old_to_new[idx]; + list_new.insert(new_idx); + if (m_mode == Instance) { + // Save the object_idx / instance_idx pair of selected old volumes, + // so we may add the newly added volumes of the same object_idx / instance_idx pair + // to the selection. + const GLVolume *volume = (*m_volumes)[new_idx]; + model_instances.emplace_back(volume->object_idx(), volume->instance_idx()); + } + } + } + m_list = std::move(list_new); + + if (!model_instances.empty()) { + // Instance selection mode. Add the newly added volumes of the same object_idx / instance_idx pair + // to the selection. + assert(m_mode == Instance); + sort_remove_duplicates(model_instances); + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { + const GLVolume* volume = (*m_volumes)[i]; + for (const std::pair &model_instance : model_instances) + if (volume->object_idx() == model_instance.first && volume->instance_idx() == model_instance.second) + this->_add_volume(i); + } + } + + _update_type(); + m_bounding_box_dirty = true; +} + +bool Selection::is_single_full_instance() const +{ + if (m_type == SingleFullInstance) + return true; + + if (m_type == SingleFullObject) + return get_instance_idx() != -1; + + if (m_list.empty() || m_volumes->empty()) + return false; + + int object_idx = m_valid ? get_object_idx() : -1; + if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx)) + return false; + + int instance_idx = (*m_volumes)[*m_list.begin()]->instance_idx(); + + std::set volumes_idxs; + for (unsigned int i : m_list) + { + const GLVolume* v = (*m_volumes)[i]; + if ((object_idx != v->object_idx()) || (instance_idx != v->instance_idx())) + return false; + + int volume_idx = v->volume_idx(); + if (volume_idx >= 0) + volumes_idxs.insert(volume_idx); + } + + return m_model->objects[object_idx]->volumes.size() == volumes_idxs.size(); +} + +bool Selection::is_from_single_object() const +{ + int idx = get_object_idx(); + return (0 <= idx) && (idx < 1000); +} + +bool Selection::requires_uniform_scale() const +{ + if (is_single_full_instance() || is_single_modifier() || is_single_volume()) + return false; + + return true; +} + +int Selection::get_object_idx() const +{ + return (m_cache.content.size() == 1) ? m_cache.content.begin()->first : -1; +} + +int Selection::get_instance_idx() const +{ + if (m_cache.content.size() == 1) + { + const InstanceIdxsList& idxs = m_cache.content.begin()->second; + if (idxs.size() == 1) + return *idxs.begin(); + } + + return -1; +} + +const Selection::InstanceIdxsList& Selection::get_instance_idxs() const +{ + assert(m_cache.content.size() == 1); + return m_cache.content.begin()->second; +} + +const GLVolume* Selection::get_volume(unsigned int volume_idx) const +{ + return (m_valid && (volume_idx < (unsigned int)m_volumes->size())) ? (*m_volumes)[volume_idx] : nullptr; +} + +const BoundingBoxf3& Selection::get_bounding_box() const +{ + if (m_bounding_box_dirty) + _calc_bounding_box(); + + return m_bounding_box; +} + +void Selection::start_dragging() +{ + if (!m_valid) + return; + + _set_caches(); +} + +void Selection::translate(const Vec3d& displacement, bool local) +{ + if (!m_valid) + return; + + for (unsigned int i : m_list) + { + if ((m_mode == Volume) || (*m_volumes)[i]->is_wipe_tower) + { + if (local) + (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement); + else + { + Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; + (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement); + } + } + else if (m_mode == Instance) + (*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement); + } + +#if !DISABLE_INSTANCES_SYNCH + if (m_mode == Instance) + _synchronize_unselected_instances(SYNC_ROTATION_NONE); + else if (m_mode == Volume) + _synchronize_unselected_volumes(); +#endif // !DISABLE_INSTANCES_SYNCH + + m_bounding_box_dirty = true; +} + +static Eigen::Quaterniond rotation_xyz_diff(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) +{ + return + // From the current coordinate system to world. + Eigen::AngleAxisd(rot_xyz_to(2), Vec3d::UnitZ()) * Eigen::AngleAxisd(rot_xyz_to(1), Vec3d::UnitY()) * Eigen::AngleAxisd(rot_xyz_to(0), Vec3d::UnitX()) * + // From world to the initial coordinate system. + Eigen::AngleAxisd(-rot_xyz_from(0), Vec3d::UnitX()) * Eigen::AngleAxisd(-rot_xyz_from(1), Vec3d::UnitY()) * Eigen::AngleAxisd(-rot_xyz_from(2), Vec3d::UnitZ()); +} + +// This should only be called if it is known, that the two rotations only differ in rotation around the Z axis. +static double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) +{ + Eigen::AngleAxisd angle_axis(rotation_xyz_diff(rot_xyz_from, rot_xyz_to)); + Vec3d axis = angle_axis.axis(); + double angle = angle_axis.angle(); +#ifndef NDEBUG + if (std::abs(angle) > 1e-8) { + assert(std::abs(axis.x()) < 1e-8); + assert(std::abs(axis.y()) < 1e-8); + } +#endif /* NDEBUG */ + return (axis.z() < 0) ? -angle : angle; +} + +// Rotate an object around one of the axes. Only one rotation component is expected to be changing. +void Selection::rotate(const Vec3d& rotation, TransformationType transformation_type) +{ + if (!m_valid) + return; + + // Only relative rotation values are allowed in the world coordinate system. + assert(!transformation_type.world() || transformation_type.relative()); + + int rot_axis_max = 0; + if (rotation.isApprox(Vec3d::Zero())) + { + for (unsigned int i : m_list) + { + GLVolume &volume = *(*m_volumes)[i]; + if (m_mode == Instance) + { + volume.set_instance_rotation(m_cache.volumes_data[i].get_instance_rotation()); + volume.set_instance_offset(m_cache.volumes_data[i].get_instance_position()); + } + else if (m_mode == Volume) + { + volume.set_volume_rotation(m_cache.volumes_data[i].get_volume_rotation()); + volume.set_volume_offset(m_cache.volumes_data[i].get_volume_position()); + } + } + } + else + { + //FIXME this does not work for absolute rotations (transformation_type.absolute() is true) + rotation.cwiseAbs().maxCoeff(&rot_axis_max); + + // For generic rotation, we want to rotate the first volume in selection, and then to synchronize the other volumes with it. + std::vector object_instance_first(m_model->objects.size(), -1); + auto rotate_instance = [this, &rotation, &object_instance_first, rot_axis_max, transformation_type](GLVolume &volume, int i) { + int first_volume_idx = object_instance_first[volume.object_idx()]; + if (rot_axis_max != 2 && first_volume_idx != -1) { + // Generic rotation, but no rotation around the Z axis. + // Always do a local rotation (do not consider the selection to be a rigid body). + assert(is_approx(rotation.z(), 0.0)); + const GLVolume &first_volume = *(*m_volumes)[first_volume_idx]; + const Vec3d &rotation = first_volume.get_instance_rotation(); + double z_diff = rotation_diff_z(m_cache.volumes_data[first_volume_idx].get_instance_rotation(), m_cache.volumes_data[i].get_instance_rotation()); + volume.set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2) + z_diff)); + } + else { + // extracts rotations from the composed transformation + Vec3d new_rotation = transformation_type.world() ? + Geometry::extract_euler_angles(Geometry::assemble_transform(Vec3d::Zero(), rotation) * m_cache.volumes_data[i].get_instance_rotation_matrix()) : + transformation_type.absolute() ? rotation : rotation + m_cache.volumes_data[i].get_instance_rotation(); + if (rot_axis_max == 2 && transformation_type.joint()) { + // Only allow rotation of multiple instances as a single rigid body when rotating around the Z axis. + Vec3d offset = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0.0, 0.0, new_rotation(2) - m_cache.volumes_data[i].get_instance_rotation()(2))) * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center); + volume.set_instance_offset(m_cache.dragging_center + offset); + } + volume.set_instance_rotation(new_rotation); + object_instance_first[volume.object_idx()] = i; + } + }; + + for (unsigned int i : m_list) + { + GLVolume &volume = *(*m_volumes)[i]; + if (is_single_full_instance()) + rotate_instance(volume, i); + else if (is_single_volume() || is_single_modifier()) + { + if (transformation_type.independent()) + volume.set_volume_rotation(volume.get_volume_rotation() + rotation); + else + { + Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); + Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); + volume.set_volume_rotation(new_rotation); + } + } + else + { + if (m_mode == Instance) + rotate_instance(volume, i); + else if (m_mode == Volume) + { + // extracts rotations from the composed transformation + Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); + Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); + if (transformation_type.joint()) + { + Vec3d local_pivot = m_cache.volumes_data[i].get_instance_full_matrix().inverse() * m_cache.dragging_center; + Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() - local_pivot); + volume.set_volume_offset(local_pivot + offset); + } + volume.set_volume_rotation(new_rotation); + } + } + } + } + +#if !DISABLE_INSTANCES_SYNCH + if (m_mode == Instance) + _synchronize_unselected_instances((rot_axis_max == 2) ? SYNC_ROTATION_NONE : SYNC_ROTATION_GENERAL); + else if (m_mode == Volume) + _synchronize_unselected_volumes(); +#endif // !DISABLE_INSTANCES_SYNCH + + m_bounding_box_dirty = true; +} + +void Selection::flattening_rotate(const Vec3d& normal) +{ + // We get the normal in untransformed coordinates. We must transform it using the instance matrix, find out + // how to rotate the instance so it faces downwards and do the rotation. All that for all selected instances. + // The function assumes that is_from_single_object() holds. + + if (!m_valid) + return; + + for (unsigned int i : m_list) + { + Transform3d wst = m_cache.volumes_data[i].get_instance_scale_matrix(); + Vec3d scaling_factor = Vec3d(1. / wst(0, 0), 1. / wst(1, 1), 1. / wst(2, 2)); + + Transform3d wmt = m_cache.volumes_data[i].get_instance_mirror_matrix(); + Vec3d mirror(wmt(0, 0), wmt(1, 1), wmt(2, 2)); + + Vec3d rotation = Geometry::extract_euler_angles(m_cache.volumes_data[i].get_instance_rotation_matrix()); + Vec3d transformed_normal = Geometry::assemble_transform(Vec3d::Zero(), rotation, scaling_factor, mirror) * normal; + transformed_normal.normalize(); + + Vec3d axis = transformed_normal(2) > 0.999f ? Vec3d(1., 0., 0.) : Vec3d(transformed_normal.cross(Vec3d(0., 0., -1.))); + axis.normalize(); + + Transform3d extra_rotation = Transform3d::Identity(); + extra_rotation.rotate(Eigen::AngleAxisd(acos(-transformed_normal(2)), axis)); + + Vec3d new_rotation = Geometry::extract_euler_angles(extra_rotation * m_cache.volumes_data[i].get_instance_rotation_matrix()); + (*m_volumes)[i]->set_instance_rotation(new_rotation); + } + +#if !DISABLE_INSTANCES_SYNCH + // we want to synchronize z-rotation as well, otherwise the flattening behaves funny + // when applied on one of several identical instances + if (m_mode == Instance) + _synchronize_unselected_instances(SYNC_ROTATION_FULL); +#endif // !DISABLE_INSTANCES_SYNCH + + m_bounding_box_dirty = true; +} + +void Selection::scale(const Vec3d& scale, bool local) +{ + if (!m_valid) + return; + + for (unsigned int i : m_list) + { + if (is_single_full_instance()) + (*m_volumes)[i]->set_instance_scaling_factor(scale); + else if (is_single_volume() || is_single_modifier()) + (*m_volumes)[i]->set_volume_scaling_factor(scale); + else + { + Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); + if (m_mode == Instance) + { + Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3); + // extracts scaling factors from the composed transformation + Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); + if (!local) + (*m_volumes)[i]->set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); + + (*m_volumes)[i]->set_instance_scaling_factor(new_scale); + } + else if (m_mode == Volume) + { + Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_volume_scale_matrix()).matrix().block(0, 0, 3, 3); + // extracts scaling factors from the composed transformation + Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); + if (!local) + { + Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() + m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center); + (*m_volumes)[i]->set_volume_offset(m_cache.dragging_center - m_cache.volumes_data[i].get_instance_position() + offset); + } + (*m_volumes)[i]->set_volume_scaling_factor(new_scale); + } + } + } + +#if !DISABLE_INSTANCES_SYNCH + if (m_mode == Instance) + _synchronize_unselected_instances(SYNC_ROTATION_NONE); + else if (m_mode == Volume) + _synchronize_unselected_volumes(); +#endif // !DISABLE_INSTANCES_SYNCH + + _ensure_on_bed(); + + m_bounding_box_dirty = true; +} + +void Selection::mirror(Axis axis) +{ + if (!m_valid) + return; + + bool single_full_instance = is_single_full_instance(); + + for (unsigned int i : m_list) + { + if (single_full_instance) + (*m_volumes)[i]->set_instance_mirror(axis, -(*m_volumes)[i]->get_instance_mirror(axis)); + else if (m_mode == Volume) + (*m_volumes)[i]->set_volume_mirror(axis, -(*m_volumes)[i]->get_volume_mirror(axis)); + } + +#if !DISABLE_INSTANCES_SYNCH + if (m_mode == Instance) + _synchronize_unselected_instances(SYNC_ROTATION_NONE); + else if (m_mode == Volume) + _synchronize_unselected_volumes(); +#endif // !DISABLE_INSTANCES_SYNCH + + m_bounding_box_dirty = true; +} + +void Selection::translate(unsigned int object_idx, const Vec3d& displacement) +{ + if (!m_valid) + return; + + for (unsigned int i : m_list) + { + GLVolume* v = (*m_volumes)[i]; + if (v->object_idx() == object_idx) + v->set_instance_offset(v->get_instance_offset() + displacement); + } + + std::set done; // prevent processing volumes twice + done.insert(m_list.begin(), m_list.end()); + + for (unsigned int i : m_list) + { + if (done.size() == m_volumes->size()) + break; + + int object_idx = (*m_volumes)[i]->object_idx(); + if (object_idx >= 1000) + continue; + + // Process unselected volumes of the object. + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) + { + if (done.size() == m_volumes->size()) + break; + + if (done.find(j) != done.end()) + continue; + + GLVolume* v = (*m_volumes)[j]; + if (v->object_idx() != object_idx) + continue; + + v->set_instance_offset(v->get_instance_offset() + displacement); + done.insert(j); + } + } + + m_bounding_box_dirty = true; +} + +void Selection::translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement) +{ + if (!m_valid) + return; + + for (unsigned int i : m_list) + { + GLVolume* v = (*m_volumes)[i]; + if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) + v->set_instance_offset(v->get_instance_offset() + displacement); + } + + std::set done; // prevent processing volumes twice + done.insert(m_list.begin(), m_list.end()); + + for (unsigned int i : m_list) + { + if (done.size() == m_volumes->size()) + break; + + int object_idx = (*m_volumes)[i]->object_idx(); + if (object_idx >= 1000) + continue; + + // Process unselected volumes of the object. + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) + { + if (done.size() == m_volumes->size()) + break; + + if (done.find(j) != done.end()) + continue; + + GLVolume* v = (*m_volumes)[j]; + if ((v->object_idx() != object_idx) || (v->instance_idx() != instance_idx)) + continue; + + v->set_instance_offset(v->get_instance_offset() + displacement); + done.insert(j); + } + } + + m_bounding_box_dirty = true; +} + +void Selection::erase() +{ + if (!m_valid) + return; + + if (is_single_full_object()) + wxGetApp().obj_list()->delete_from_model_and_list(ItemType::itObject, get_object_idx(), 0); + else if (is_multiple_full_object()) + { + std::vector items; + items.reserve(m_cache.content.size()); + for (ObjectIdxsToInstanceIdxsMap::iterator it = m_cache.content.begin(); it != m_cache.content.end(); ++it) + { + items.emplace_back(ItemType::itObject, it->first, 0); + } + wxGetApp().obj_list()->delete_from_model_and_list(items); + } + else if (is_multiple_full_instance()) + { + std::set> instances_idxs; + for (ObjectIdxsToInstanceIdxsMap::iterator obj_it = m_cache.content.begin(); obj_it != m_cache.content.end(); ++obj_it) + { + for (InstanceIdxsList::reverse_iterator inst_it = obj_it->second.rbegin(); inst_it != obj_it->second.rend(); ++inst_it) + { + instances_idxs.insert(std::make_pair(obj_it->first, *inst_it)); + } + } + + std::vector items; + items.reserve(instances_idxs.size()); + for (const std::pair& i : instances_idxs) + { + items.emplace_back(ItemType::itInstance, i.first, i.second); + } + wxGetApp().obj_list()->delete_from_model_and_list(items); + } + else if (is_single_full_instance()) + wxGetApp().obj_list()->delete_from_model_and_list(ItemType::itInstance, get_object_idx(), get_instance_idx()); + else if (is_mixed()) + { + std::set items_set; + std::map volumes_in_obj; + + for (auto i : m_list) { + const auto gl_vol = (*m_volumes)[i]; + const auto glv_obj_idx = gl_vol->object_idx(); + const auto model_object = m_model->objects[glv_obj_idx]; + + if (model_object->instances.size() == 1) { + if (model_object->volumes.size() == 1) + items_set.insert(ItemForDelete(ItemType::itObject, glv_obj_idx, -1)); + else { + items_set.insert(ItemForDelete(ItemType::itVolume, glv_obj_idx, gl_vol->volume_idx())); + int idx = (volumes_in_obj.find(glv_obj_idx) == volumes_in_obj.end()) ? 0 : volumes_in_obj.at(glv_obj_idx); + volumes_in_obj[glv_obj_idx] = ++idx; + } + continue; + } + + const auto glv_ins_idx = gl_vol->instance_idx(); + + for (auto obj_ins : m_cache.content) { + if (obj_ins.first == glv_obj_idx) { + if (obj_ins.second.find(glv_ins_idx) != obj_ins.second.end()) { + if (obj_ins.second.size() == model_object->instances.size()) + items_set.insert(ItemForDelete(ItemType::itVolume, glv_obj_idx, gl_vol->volume_idx())); + else + items_set.insert(ItemForDelete(ItemType::itInstance, glv_obj_idx, glv_ins_idx)); + + break; + } + } + } + } + + std::vector items; + items.reserve(items_set.size()); + for (const ItemForDelete& i : items_set) { + if (i.type == ItemType::itVolume) { + const int vol_in_obj_cnt = volumes_in_obj.find(i.obj_idx) == volumes_in_obj.end() ? 0 : volumes_in_obj.at(i.obj_idx); + if (vol_in_obj_cnt == m_model->objects[i.obj_idx]->volumes.size()) { + if (i.sub_obj_idx == vol_in_obj_cnt - 1) + items.emplace_back(ItemType::itObject, i.obj_idx, 0); + continue; + } + } + items.emplace_back(i.type, i.obj_idx, i.sub_obj_idx); + } + + wxGetApp().obj_list()->delete_from_model_and_list(items); + } + else + { + std::set> volumes_idxs; + for (unsigned int i : m_list) + { + const GLVolume* v = (*m_volumes)[i]; + // Only remove volumes associated with ModelVolumes from the object list. + // Temporary meshes (SLA supports or pads) are not managed by the object list. + if (v->volume_idx() >= 0) + volumes_idxs.insert(std::make_pair(v->object_idx(), v->volume_idx())); + } + + std::vector items; + items.reserve(volumes_idxs.size()); + for (const std::pair& v : volumes_idxs) + { + items.emplace_back(ItemType::itVolume, v.first, v.second); + } + + wxGetApp().obj_list()->delete_from_model_and_list(items); + } +} + +void Selection::render(float scale_factor) const +{ + if (!m_valid || is_empty()) + return; + + m_scale_factor = scale_factor; + + // render cumulative bounding box of selected volumes + _render_selected_volumes(); + _render_synchronized_volumes(); +} + +#if ENABLE_RENDER_SELECTION_CENTER +void Selection::render_center() const +{ + if (!m_valid || is_empty() || (m_quadric == nullptr)) + return; + + const Vec3d& center = get_bounding_box().center(); + + ::glDisable(GL_DEPTH_TEST); + + ::glEnable(GL_LIGHTING); + + ::glColor3f(1.0f, 1.0f, 1.0f); + ::glPushMatrix(); + ::glTranslated(center(0), center(1), center(2)); + ::gluSphere(m_quadric, 0.75, 32, 32); + ::glPopMatrix(); + + ::glDisable(GL_LIGHTING); +} +#endif // ENABLE_RENDER_SELECTION_CENTER + +void Selection::render_sidebar_hints(const std::string& sidebar_field) const +{ + if (sidebar_field.empty()) + return; + + ::glClear(GL_DEPTH_BUFFER_BIT); + ::glEnable(GL_DEPTH_TEST); + + ::glEnable(GL_LIGHTING); + + ::glPushMatrix(); + + const Vec3d& center = get_bounding_box().center(); + + if (is_single_full_instance()) + { + ::glTranslated(center(0), center(1), center(2)); + if (!boost::starts_with(sidebar_field, "position")) + { + Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); + ::glMultMatrixd(orient_matrix.data()); + } + } + else if (is_single_volume() || is_single_modifier()) + { + ::glTranslated(center(0), center(1), center(2)); + Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); + if (!boost::starts_with(sidebar_field, "position")) + orient_matrix = orient_matrix * (*m_volumes)[*m_list.begin()]->get_volume_transformation().get_matrix(true, false, true, true); + + ::glMultMatrixd(orient_matrix.data()); + } + else + { + ::glTranslated(center(0), center(1), center(2)); + if (requires_local_axes()) + { + Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); + ::glMultMatrixd(orient_matrix.data()); + } + } + + if (boost::starts_with(sidebar_field, "position")) + _render_sidebar_position_hints(sidebar_field); + else if (boost::starts_with(sidebar_field, "rotation")) + _render_sidebar_rotation_hints(sidebar_field); + else if (boost::starts_with(sidebar_field, "scale")) + _render_sidebar_scale_hints(sidebar_field); + else if (boost::starts_with(sidebar_field, "size")) + _render_sidebar_size_hints(sidebar_field); + + ::glPopMatrix(); + + ::glDisable(GL_LIGHTING); +} + +bool Selection::requires_local_axes() const +{ + return (m_mode == Volume) && is_from_single_instance(); +} + +void Selection::_update_valid() +{ + m_valid = (m_volumes != nullptr) && (m_model != nullptr); +} + +void Selection::_update_type() +{ + m_cache.content.clear(); + m_type = Mixed; + + for (unsigned int i : m_list) + { + const GLVolume* volume = (*m_volumes)[i]; + int obj_idx = volume->object_idx(); + int inst_idx = volume->instance_idx(); + ObjectIdxsToInstanceIdxsMap::iterator obj_it = m_cache.content.find(obj_idx); + if (obj_it == m_cache.content.end()) + obj_it = m_cache.content.insert(ObjectIdxsToInstanceIdxsMap::value_type(obj_idx, InstanceIdxsList())).first; + + obj_it->second.insert(inst_idx); + } + + bool requires_disable = false; + + if (!m_valid) + m_type = Invalid; + else + { + if (m_list.empty()) + m_type = Empty; + else if (m_list.size() == 1) + { + const GLVolume* first = (*m_volumes)[*m_list.begin()]; + if (first->is_wipe_tower) + m_type = WipeTower; + else if (first->is_modifier) + { + m_type = SingleModifier; + requires_disable = true; + } + else + { + const ModelObject* model_object = m_model->objects[first->object_idx()]; + unsigned int volumes_count = (unsigned int)model_object->volumes.size(); + unsigned int instances_count = (unsigned int)model_object->instances.size(); + if (volumes_count * instances_count == 1) + { + m_type = SingleFullObject; + // ensures the correct mode is selected + m_mode = Instance; + } + else if (volumes_count == 1) // instances_count > 1 + { + m_type = SingleFullInstance; + // ensures the correct mode is selected + m_mode = Instance; + } + else + { + m_type = SingleVolume; + requires_disable = true; + } + } + } + else + { + if (m_cache.content.size() == 1) // single object + { + const ModelObject* model_object = m_model->objects[m_cache.content.begin()->first]; + unsigned int model_volumes_count = (unsigned int)model_object->volumes.size(); + unsigned int sla_volumes_count = 0; + for (unsigned int i : m_list) + { + if ((*m_volumes)[i]->volume_idx() < 0) + ++sla_volumes_count; + } + unsigned int volumes_count = model_volumes_count + sla_volumes_count; + unsigned int instances_count = (unsigned int)model_object->instances.size(); + unsigned int selected_instances_count = (unsigned int)m_cache.content.begin()->second.size(); + if (volumes_count * instances_count == (unsigned int)m_list.size()) + { + m_type = SingleFullObject; + // ensures the correct mode is selected + m_mode = Instance; + } + else if (selected_instances_count == 1) + { + if (volumes_count == (unsigned int)m_list.size()) + { + m_type = SingleFullInstance; + // ensures the correct mode is selected + m_mode = Instance; + } + else + { + unsigned int modifiers_count = 0; + for (unsigned int i : m_list) + { + if ((*m_volumes)[i]->is_modifier) + ++modifiers_count; + } + + if (modifiers_count == 0) + { + m_type = MultipleVolume; + requires_disable = true; + } + else if (modifiers_count == (unsigned int)m_list.size()) + { + m_type = MultipleModifier; + requires_disable = true; + } + } + } + else if ((selected_instances_count > 1) && (selected_instances_count * volumes_count == (unsigned int)m_list.size())) + { + m_type = MultipleFullInstance; + // ensures the correct mode is selected + m_mode = Instance; + } + } + else + { + int sels_cntr = 0; + for (ObjectIdxsToInstanceIdxsMap::iterator it = m_cache.content.begin(); it != m_cache.content.end(); ++it) + { + const ModelObject* model_object = m_model->objects[it->first]; + unsigned int volumes_count = (unsigned int)model_object->volumes.size(); + unsigned int instances_count = (unsigned int)model_object->instances.size(); + sels_cntr += volumes_count * instances_count; + } + if (sels_cntr == (unsigned int)m_list.size()) + { + m_type = MultipleFullObject; + // ensures the correct mode is selected + m_mode = Instance; + } + } + } + } + + int object_idx = get_object_idx(); + int instance_idx = get_instance_idx(); + for (GLVolume* v : *m_volumes) + { + v->disabled = requires_disable ? (v->object_idx() != object_idx) || (v->instance_idx() != instance_idx) : false; + } + +#if ENABLE_SELECTION_DEBUG_OUTPUT + std::cout << "Selection: "; + std::cout << "mode: "; + switch (m_mode) + { + case Volume: + { + std::cout << "Volume"; + break; + } + case Instance: + { + std::cout << "Instance"; + break; + } + } + + std::cout << " - type: "; + + switch (m_type) + { + case Invalid: + { + std::cout << "Invalid" << std::endl; + break; + } + case Empty: + { + std::cout << "Empty" << std::endl; + break; + } + case WipeTower: + { + std::cout << "WipeTower" << std::endl; + break; + } + case SingleModifier: + { + std::cout << "SingleModifier" << std::endl; + break; + } + case MultipleModifier: + { + std::cout << "MultipleModifier" << std::endl; + break; + } + case SingleVolume: + { + std::cout << "SingleVolume" << std::endl; + break; + } + case MultipleVolume: + { + std::cout << "MultipleVolume" << std::endl; + break; + } + case SingleFullObject: + { + std::cout << "SingleFullObject" << std::endl; + break; + } + case MultipleFullObject: + { + std::cout << "MultipleFullObject" << std::endl; + break; + } + case SingleFullInstance: + { + std::cout << "SingleFullInstance" << std::endl; + break; + } + case MultipleFullInstance: + { + std::cout << "MultipleFullInstance" << std::endl; + break; + } + case Mixed: + { + std::cout << "Mixed" << std::endl; + break; + } + } +#endif // ENABLE_SELECTION_DEBUG_OUTPUT +} + +void Selection::_set_caches() +{ + m_cache.volumes_data.clear(); + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) + { + const GLVolume* v = (*m_volumes)[i]; + m_cache.volumes_data.emplace(i, VolumeCache(v->get_volume_transformation(), v->get_instance_transformation())); + } + m_cache.dragging_center = get_bounding_box().center(); +} + +void Selection::_add_volume(unsigned int volume_idx) +{ + m_list.insert(volume_idx); + (*m_volumes)[volume_idx]->selected = true; +} + +void Selection::_add_instance(unsigned int object_idx, unsigned int instance_idx) +{ + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) + { + GLVolume* v = (*m_volumes)[i]; + if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) + _add_volume(i); + } +} + +void Selection::_add_object(unsigned int object_idx) +{ + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) + { + GLVolume* v = (*m_volumes)[i]; + if (v->object_idx() == object_idx) + _add_volume(i); + } +} + +void Selection::_remove_volume(unsigned int volume_idx) +{ + IndicesList::iterator v_it = m_list.find(volume_idx); + if (v_it == m_list.end()) + return; + + m_list.erase(v_it); + + (*m_volumes)[volume_idx]->selected = false; +} + +void Selection::_remove_instance(unsigned int object_idx, unsigned int instance_idx) +{ + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) + { + GLVolume* v = (*m_volumes)[i]; + if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) + _remove_volume(i); + } +} + +void Selection::_remove_object(unsigned int object_idx) +{ + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) + { + GLVolume* v = (*m_volumes)[i]; + if (v->object_idx() == object_idx) + _remove_volume(i); + } +} + +void Selection::_calc_bounding_box() const +{ + m_bounding_box = BoundingBoxf3(); + if (m_valid) + { + for (unsigned int i : m_list) + { + m_bounding_box.merge((*m_volumes)[i]->transformed_convex_hull_bounding_box()); + } + } + m_bounding_box_dirty = false; +} + +void Selection::_render_selected_volumes() const +{ + float color[3] = { 1.0f, 1.0f, 1.0f }; + _render_bounding_box(get_bounding_box(), color); +} + +void Selection::_render_synchronized_volumes() const +{ + if (m_mode == Instance) + return; + + float color[3] = { 1.0f, 1.0f, 0.0f }; + + for (unsigned int i : m_list) + { + const GLVolume* volume = (*m_volumes)[i]; + int object_idx = volume->object_idx(); + int instance_idx = volume->instance_idx(); + int volume_idx = volume->volume_idx(); + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) + { + if (i == j) + continue; + + const GLVolume* v = (*m_volumes)[j]; + if ((v->object_idx() != object_idx) || (v->volume_idx() != volume_idx)) + continue; + + _render_bounding_box(v->transformed_convex_hull_bounding_box(), color); + } + } +} + +void Selection::_render_bounding_box(const BoundingBoxf3& box, float* color) const +{ + if (color == nullptr) + return; + + Vec3f b_min = box.min.cast(); + Vec3f b_max = box.max.cast(); + Vec3f size = 0.2f * box.size().cast(); + + ::glEnable(GL_DEPTH_TEST); + + ::glColor3fv(color); + ::glLineWidth(2.0f * m_scale_factor); + + ::glBegin(GL_LINES); + + ::glVertex3f(b_min(0), b_min(1), b_min(2)); ::glVertex3f(b_min(0) + size(0), b_min(1), b_min(2)); + ::glVertex3f(b_min(0), b_min(1), b_min(2)); ::glVertex3f(b_min(0), b_min(1) + size(1), b_min(2)); + ::glVertex3f(b_min(0), b_min(1), b_min(2)); ::glVertex3f(b_min(0), b_min(1), b_min(2) + size(2)); + + ::glVertex3f(b_max(0), b_min(1), b_min(2)); ::glVertex3f(b_max(0) - size(0), b_min(1), b_min(2)); + ::glVertex3f(b_max(0), b_min(1), b_min(2)); ::glVertex3f(b_max(0), b_min(1) + size(1), b_min(2)); + ::glVertex3f(b_max(0), b_min(1), b_min(2)); ::glVertex3f(b_max(0), b_min(1), b_min(2) + size(2)); + + ::glVertex3f(b_max(0), b_max(1), b_min(2)); ::glVertex3f(b_max(0) - size(0), b_max(1), b_min(2)); + ::glVertex3f(b_max(0), b_max(1), b_min(2)); ::glVertex3f(b_max(0), b_max(1) - size(1), b_min(2)); + ::glVertex3f(b_max(0), b_max(1), b_min(2)); ::glVertex3f(b_max(0), b_max(1), b_min(2) + size(2)); + + ::glVertex3f(b_min(0), b_max(1), b_min(2)); ::glVertex3f(b_min(0) + size(0), b_max(1), b_min(2)); + ::glVertex3f(b_min(0), b_max(1), b_min(2)); ::glVertex3f(b_min(0), b_max(1) - size(1), b_min(2)); + ::glVertex3f(b_min(0), b_max(1), b_min(2)); ::glVertex3f(b_min(0), b_max(1), b_min(2) + size(2)); + + ::glVertex3f(b_min(0), b_min(1), b_max(2)); ::glVertex3f(b_min(0) + size(0), b_min(1), b_max(2)); + ::glVertex3f(b_min(0), b_min(1), b_max(2)); ::glVertex3f(b_min(0), b_min(1) + size(1), b_max(2)); + ::glVertex3f(b_min(0), b_min(1), b_max(2)); ::glVertex3f(b_min(0), b_min(1), b_max(2) - size(2)); + + ::glVertex3f(b_max(0), b_min(1), b_max(2)); ::glVertex3f(b_max(0) - size(0), b_min(1), b_max(2)); + ::glVertex3f(b_max(0), b_min(1), b_max(2)); ::glVertex3f(b_max(0), b_min(1) + size(1), b_max(2)); + ::glVertex3f(b_max(0), b_min(1), b_max(2)); ::glVertex3f(b_max(0), b_min(1), b_max(2) - size(2)); + + ::glVertex3f(b_max(0), b_max(1), b_max(2)); ::glVertex3f(b_max(0) - size(0), b_max(1), b_max(2)); + ::glVertex3f(b_max(0), b_max(1), b_max(2)); ::glVertex3f(b_max(0), b_max(1) - size(1), b_max(2)); + ::glVertex3f(b_max(0), b_max(1), b_max(2)); ::glVertex3f(b_max(0), b_max(1), b_max(2) - size(2)); + + ::glVertex3f(b_min(0), b_max(1), b_max(2)); ::glVertex3f(b_min(0) + size(0), b_max(1), b_max(2)); + ::glVertex3f(b_min(0), b_max(1), b_max(2)); ::glVertex3f(b_min(0), b_max(1) - size(1), b_max(2)); + ::glVertex3f(b_min(0), b_max(1), b_max(2)); ::glVertex3f(b_min(0), b_max(1), b_max(2) - size(2)); + + ::glEnd(); +} + +void Selection::_render_sidebar_position_hints(const std::string& sidebar_field) const +{ + if (boost::ends_with(sidebar_field, "x")) + { + ::glRotated(-90.0, 0.0, 0.0, 1.0); + _render_sidebar_position_hint(X); + } + else if (boost::ends_with(sidebar_field, "y")) + _render_sidebar_position_hint(Y); + else if (boost::ends_with(sidebar_field, "z")) + { + ::glRotated(90.0, 1.0, 0.0, 0.0); + _render_sidebar_position_hint(Z); + } +} + +void Selection::_render_sidebar_rotation_hints(const std::string& sidebar_field) const +{ + if (boost::ends_with(sidebar_field, "x")) + { + ::glRotated(90.0, 0.0, 1.0, 0.0); + _render_sidebar_rotation_hint(X); + } + else if (boost::ends_with(sidebar_field, "y")) + { + ::glRotated(-90.0, 1.0, 0.0, 0.0); + _render_sidebar_rotation_hint(Y); + } + else if (boost::ends_with(sidebar_field, "z")) + _render_sidebar_rotation_hint(Z); +} + +void Selection::_render_sidebar_scale_hints(const std::string& sidebar_field) const +{ + bool uniform_scale = requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling(); + + if (boost::ends_with(sidebar_field, "x") || uniform_scale) + { + ::glPushMatrix(); + ::glRotated(-90.0, 0.0, 0.0, 1.0); + _render_sidebar_scale_hint(X); + ::glPopMatrix(); + } + + if (boost::ends_with(sidebar_field, "y") || uniform_scale) + { + ::glPushMatrix(); + _render_sidebar_scale_hint(Y); + ::glPopMatrix(); + } + + if (boost::ends_with(sidebar_field, "z") || uniform_scale) + { + ::glPushMatrix(); + ::glRotated(90.0, 1.0, 0.0, 0.0); + _render_sidebar_scale_hint(Z); + ::glPopMatrix(); + } +} + +void Selection::_render_sidebar_size_hints(const std::string& sidebar_field) const +{ + _render_sidebar_scale_hints(sidebar_field); +} + +void Selection::_render_sidebar_position_hint(Axis axis) const +{ + m_arrow.set_color(AXES_COLOR[axis], 3); + m_arrow.render(); +} + +void Selection::_render_sidebar_rotation_hint(Axis axis) const +{ + m_curved_arrow.set_color(AXES_COLOR[axis], 3); + m_curved_arrow.render(); + + ::glRotated(180.0, 0.0, 0.0, 1.0); + m_curved_arrow.render(); +} + +void Selection::_render_sidebar_scale_hint(Axis axis) const +{ + m_arrow.set_color(((requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling()) ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis]), 3); + + ::glTranslated(0.0, 5.0, 0.0); + m_arrow.render(); + + ::glTranslated(0.0, -10.0, 0.0); + ::glRotated(180.0, 0.0, 0.0, 1.0); + m_arrow.render(); +} + +void Selection::_render_sidebar_size_hint(Axis axis, double length) const +{ +} + +#ifndef NDEBUG +static bool is_rotation_xy_synchronized(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) +{ + Eigen::AngleAxisd angle_axis(rotation_xyz_diff(rot_xyz_from, rot_xyz_to)); + Vec3d axis = angle_axis.axis(); + double angle = angle_axis.angle(); + if (std::abs(angle) < 1e-8) + return true; + assert(std::abs(axis.x()) < 1e-8); + assert(std::abs(axis.y()) < 1e-8); + assert(std::abs(std::abs(axis.z()) - 1.) < 1e-8); + return std::abs(axis.x()) < 1e-8 && std::abs(axis.y()) < 1e-8 && std::abs(std::abs(axis.z()) - 1.) < 1e-8; +} + +static void verify_instances_rotation_synchronized(const Model &model, const GLVolumePtrs &volumes) +{ + for (size_t idx_object = 0; idx_object < model.objects.size(); ++idx_object) { + int idx_volume_first = -1; + for (int i = 0; i < (int)volumes.size(); ++i) { + if (volumes[i]->object_idx() == idx_object) { + idx_volume_first = i; + break; + } + } + assert(idx_volume_first != -1); // object without instances? + if (idx_volume_first == -1) + continue; + const Vec3d &rotation0 = volumes[idx_volume_first]->get_instance_rotation(); + for (int i = idx_volume_first + 1; i < (int)volumes.size(); ++i) + if (volumes[i]->object_idx() == idx_object) { + const Vec3d &rotation = volumes[i]->get_instance_rotation(); + assert(is_rotation_xy_synchronized(rotation, rotation0)); + } + } +} +#endif /* NDEBUG */ + +void Selection::_synchronize_unselected_instances(SyncRotationType sync_rotation_type) +{ + std::set done; // prevent processing volumes twice + done.insert(m_list.begin(), m_list.end()); + + for (unsigned int i : m_list) + { + if (done.size() == m_volumes->size()) + break; + + const GLVolume* volume = (*m_volumes)[i]; + int object_idx = volume->object_idx(); + if (object_idx >= 1000) + continue; + + int instance_idx = volume->instance_idx(); + const Vec3d& rotation = volume->get_instance_rotation(); + const Vec3d& scaling_factor = volume->get_instance_scaling_factor(); + const Vec3d& mirror = volume->get_instance_mirror(); + + // Process unselected instances. + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) + { + if (done.size() == m_volumes->size()) + break; + + if (done.find(j) != done.end()) + continue; + + GLVolume* v = (*m_volumes)[j]; + if ((v->object_idx() != object_idx) || (v->instance_idx() == instance_idx)) + continue; + + assert(is_rotation_xy_synchronized(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation())); + switch (sync_rotation_type) { + case SYNC_ROTATION_NONE: + // z only rotation -> keep instance z + // The X,Y rotations should be synchronized from start to end of the rotation. + assert(is_rotation_xy_synchronized(rotation, v->get_instance_rotation())); + break; + case SYNC_ROTATION_FULL: + // rotation comes from place on face -> force given z + v->set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2))); + break; + case SYNC_ROTATION_GENERAL: + // generic rotation -> update instance z with the delta of the rotation. + double z_diff = rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation()); + v->set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2) + z_diff)); + break; + } + + v->set_instance_scaling_factor(scaling_factor); + v->set_instance_mirror(mirror); + + done.insert(j); + } + } + +#ifndef NDEBUG + verify_instances_rotation_synchronized(*m_model, *m_volumes); +#endif /* NDEBUG */ +} + +void Selection::_synchronize_unselected_volumes() +{ + for (unsigned int i : m_list) + { + const GLVolume* volume = (*m_volumes)[i]; + int object_idx = volume->object_idx(); + if (object_idx >= 1000) + continue; + + int volume_idx = volume->volume_idx(); + const Vec3d& offset = volume->get_volume_offset(); + const Vec3d& rotation = volume->get_volume_rotation(); + const Vec3d& scaling_factor = volume->get_volume_scaling_factor(); + const Vec3d& mirror = volume->get_volume_mirror(); + + // Process unselected volumes. + for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) + { + if (j == i) + continue; + + GLVolume* v = (*m_volumes)[j]; + if ((v->object_idx() != object_idx) || (v->volume_idx() != volume_idx)) + continue; + + v->set_volume_offset(offset); + v->set_volume_rotation(rotation); + v->set_volume_scaling_factor(scaling_factor); + v->set_volume_mirror(mirror); + } + } +} + +void Selection::_ensure_on_bed() +{ + typedef std::map, double> InstancesToZMap; + InstancesToZMap instances_min_z; + + for (GLVolume* volume : *m_volumes) + { + if (!volume->is_wipe_tower && !volume->is_modifier) + { + double min_z = volume->transformed_convex_hull_bounding_box().min(2); + std::pair instance = std::make_pair(volume->object_idx(), volume->instance_idx()); + InstancesToZMap::iterator it = instances_min_z.find(instance); + if (it == instances_min_z.end()) + it = instances_min_z.insert(InstancesToZMap::value_type(instance, DBL_MAX)).first; + + it->second = std::min(it->second, min_z); + } + } + + for (GLVolume* volume : *m_volumes) + { + std::pair instance = std::make_pair(volume->object_idx(), volume->instance_idx()); + InstancesToZMap::iterator it = instances_min_z.find(instance); + if (it != instances_min_z.end()) + volume->set_instance_offset(Z, volume->get_instance_offset(Z) - it->second); + } +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp new file mode 100644 index 0000000000..3a21122df5 --- /dev/null +++ b/src/slic3r/GUI/Selection.hpp @@ -0,0 +1,301 @@ +#ifndef slic3r_GUI_Selection_hpp_ +#define slic3r_GUI_Selection_hpp_ + +#include +#include "libslic3r/Geometry.hpp" +#include "3DScene.hpp" + +namespace Slic3r { +namespace GUI { + +class TransformationType +{ +public: + enum Enum { + // Transforming in a world coordinate system + World = 0, + // Transforming in a local coordinate system + Local = 1, + // Absolute transformations, allowed in local coordinate system only. + Absolute = 0, + // Relative transformations, allowed in both local and world coordinate system. + Relative = 2, + // For group selection, the transformation is performed as if the group made a single solid body. + Joint = 0, + // For group selection, the transformation is performed on each object independently. + Independent = 4, + + World_Relative_Joint = World | Relative | Joint, + World_Relative_Independent = World | Relative | Independent, + Local_Absolute_Joint = Local | Absolute | Joint, + Local_Absolute_Independent = Local | Absolute | Independent, + Local_Relative_Joint = Local | Relative | Joint, + Local_Relative_Independent = Local | Relative | Independent, + }; + + TransformationType() : m_value(World) {} + TransformationType(Enum value) : m_value(value) {} + TransformationType& operator=(Enum value) { m_value = value; return *this; } + + Enum operator()() const { return m_value; } + bool has(Enum v) const { return ((unsigned int)m_value & (unsigned int)v) != 0; } + + void set_world() { this->remove(Local); } + void set_local() { this->add(Local); } + void set_absolute() { this->remove(Relative); } + void set_relative() { this->add(Relative); } + void set_joint() { this->remove(Independent); } + void set_independent() { this->add(Independent); } + + bool world() const { return !this->has(Local); } + bool local() const { return this->has(Local); } + bool absolute() const { return !this->has(Relative); } + bool relative() const { return this->has(Relative); } + bool joint() const { return !this->has(Independent); } + bool independent() const { return this->has(Independent); } + +private: + void add(Enum v) { m_value = Enum((unsigned int)m_value | (unsigned int)v); } + void remove(Enum v) { m_value = Enum((unsigned int)m_value & (~(unsigned int)v)); } + + Enum m_value; +}; + +class Selection +{ +public: + typedef std::set IndicesList; + + enum EMode : unsigned char + { + Volume, + Instance + }; + + enum EType : unsigned char + { + Invalid, + Empty, + WipeTower, + SingleModifier, + MultipleModifier, + SingleVolume, + MultipleVolume, + SingleFullObject, + MultipleFullObject, + SingleFullInstance, + MultipleFullInstance, + Mixed + }; + +private: + struct VolumeCache + { + private: + struct TransformCache + { + Vec3d position; + Vec3d rotation; + Vec3d scaling_factor; + Vec3d mirror; + Transform3d rotation_matrix; + Transform3d scale_matrix; + Transform3d mirror_matrix; + Transform3d full_matrix; + + TransformCache(); + explicit TransformCache(const Geometry::Transformation& transform); + }; + + TransformCache m_volume; + TransformCache m_instance; + + public: + VolumeCache() {} + VolumeCache(const Geometry::Transformation& volume_transform, const Geometry::Transformation& instance_transform); + + const Vec3d& get_volume_position() const { return m_volume.position; } + const Vec3d& get_volume_rotation() const { return m_volume.rotation; } + const Vec3d& get_volume_scaling_factor() const { return m_volume.scaling_factor; } + const Vec3d& get_volume_mirror() const { return m_volume.mirror; } + const Transform3d& get_volume_rotation_matrix() const { return m_volume.rotation_matrix; } + const Transform3d& get_volume_scale_matrix() const { return m_volume.scale_matrix; } + const Transform3d& get_volume_mirror_matrix() const { return m_volume.mirror_matrix; } + const Transform3d& get_volume_full_matrix() const { return m_volume.full_matrix; } + + const Vec3d& get_instance_position() const { return m_instance.position; } + const Vec3d& get_instance_rotation() const { return m_instance.rotation; } + const Vec3d& get_instance_scaling_factor() const { return m_instance.scaling_factor; } + const Vec3d& get_instance_mirror() const { return m_instance.mirror; } + const Transform3d& get_instance_rotation_matrix() const { return m_instance.rotation_matrix; } + const Transform3d& get_instance_scale_matrix() const { return m_instance.scale_matrix; } + const Transform3d& get_instance_mirror_matrix() const { return m_instance.mirror_matrix; } + const Transform3d& get_instance_full_matrix() const { return m_instance.full_matrix; } + }; + + typedef std::map VolumesCache; + typedef std::set InstanceIdxsList; + typedef std::map ObjectIdxsToInstanceIdxsMap; + + struct Cache + { + // Cache of GLVolume derived transformation matrices, valid during mouse dragging. + VolumesCache volumes_data; + // Center of the dragged selection, valid during mouse dragging. + Vec3d dragging_center; + // Map from indices of ModelObject instances in Model::objects + // to a set of indices of ModelVolume instances in ModelObject::instances + // Here the index means a position inside the respective std::vector, not ModelID. + ObjectIdxsToInstanceIdxsMap content; + }; + + // Volumes owned by GLCanvas3D. + GLVolumePtrs* m_volumes; + // Model, not owned. + Model* m_model; + + bool m_valid; + EMode m_mode; + EType m_type; + // set of indices to m_volumes + IndicesList m_list; + Cache m_cache; + mutable BoundingBoxf3 m_bounding_box; + mutable bool m_bounding_box_dirty; + +#if ENABLE_RENDER_SELECTION_CENTER + GLUquadricObj* m_quadric; +#endif // ENABLE_RENDER_SELECTION_CENTER + mutable GLArrow m_arrow; + mutable GLCurvedArrow m_curved_arrow; + + mutable float m_scale_factor; + +public: + Selection(); +#if ENABLE_RENDER_SELECTION_CENTER + ~Selection(); +#endif // ENABLE_RENDER_SELECTION_CENTER + + void set_volumes(GLVolumePtrs* volumes); + bool init(bool useVBOs); + + Model* get_model() const { return m_model; } + void set_model(Model* model); + + EMode get_mode() const { return m_mode; } + void set_mode(EMode mode) { m_mode = mode; } + + void add(unsigned int volume_idx, bool as_single_selection = true); + void remove(unsigned int volume_idx); + + void add_object(unsigned int object_idx, bool as_single_selection = true); + void remove_object(unsigned int object_idx); + + void add_instance(unsigned int object_idx, unsigned int instance_idx, bool as_single_selection = true); + void remove_instance(unsigned int object_idx, unsigned int instance_idx); + + void add_volume(unsigned int object_idx, unsigned int volume_idx, int instance_idx, bool as_single_selection = true); + void remove_volume(unsigned int object_idx, unsigned int volume_idx); + + void add_all(); + + // Update the selection based on the map from old indices to new indices after m_volumes changed. + // If the current selection is by instance, this call may select newly added volumes, if they belong to already selected instances. + void volumes_changed(const std::vector &map_volume_old_to_new); + void clear(); + + bool is_empty() const { return m_type == Empty; } + bool is_wipe_tower() const { return m_type == WipeTower; } + bool is_modifier() const { return (m_type == SingleModifier) || (m_type == MultipleModifier); } + bool is_single_modifier() const { return m_type == SingleModifier; } + bool is_multiple_modifier() const { return m_type == MultipleModifier; } + bool is_single_full_instance() const; + bool is_multiple_full_instance() const { return m_type == MultipleFullInstance; } + bool is_single_full_object() const { return m_type == SingleFullObject; } + bool is_multiple_full_object() const { return m_type == MultipleFullObject; } + bool is_single_volume() const { return m_type == SingleVolume; } + bool is_multiple_volume() const { return m_type == MultipleVolume; } + bool is_mixed() const { return m_type == Mixed; } + bool is_from_single_instance() const { return get_instance_idx() != -1; } + bool is_from_single_object() const; + + bool contains_volume(unsigned int volume_idx) const { return std::find(m_list.begin(), m_list.end(), volume_idx) != m_list.end(); } + bool requires_uniform_scale() const; + + // Returns the the object id if the selection is from a single object, otherwise is -1 + int get_object_idx() const; + // Returns the instance id if the selection is from a single object and from a single instance, otherwise is -1 + int get_instance_idx() const; + // Returns the indices of selected instances. + // Can only be called if selection is from a single object. + const InstanceIdxsList& get_instance_idxs() const; + + const IndicesList& get_volume_idxs() const { return m_list; } + const GLVolume* get_volume(unsigned int volume_idx) const; + + const ObjectIdxsToInstanceIdxsMap& get_content() const { return m_cache.content; } + + unsigned int volumes_count() const { return (unsigned int)m_list.size(); } + const BoundingBoxf3& get_bounding_box() const; + + void start_dragging(); + + void translate(const Vec3d& displacement, bool local = false); + void rotate(const Vec3d& rotation, TransformationType transformation_type); + void flattening_rotate(const Vec3d& normal); + void scale(const Vec3d& scale, bool local); + void mirror(Axis axis); + + void translate(unsigned int object_idx, const Vec3d& displacement); + void translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement); + + void erase(); + + void render(float scale_factor = 1.0) const; +#if ENABLE_RENDER_SELECTION_CENTER + void render_center() const; +#endif // ENABLE_RENDER_SELECTION_CENTER + void render_sidebar_hints(const std::string& sidebar_field) const; + + bool requires_local_axes() const; + +private: + void _update_valid(); + void _update_type(); + void _set_caches(); + void _add_volume(unsigned int volume_idx); + void _add_instance(unsigned int object_idx, unsigned int instance_idx); + void _add_object(unsigned int object_idx); + void _remove_volume(unsigned int volume_idx); + void _remove_instance(unsigned int object_idx, unsigned int instance_idx); + void _remove_object(unsigned int object_idx); + void _calc_bounding_box() const; + void _render_selected_volumes() const; + void _render_synchronized_volumes() const; + void _render_bounding_box(const BoundingBoxf3& box, float* color) const; + void _render_sidebar_position_hints(const std::string& sidebar_field) const; + void _render_sidebar_rotation_hints(const std::string& sidebar_field) const; + void _render_sidebar_scale_hints(const std::string& sidebar_field) const; + void _render_sidebar_size_hints(const std::string& sidebar_field) const; + void _render_sidebar_position_hint(Axis axis) const; + void _render_sidebar_rotation_hint(Axis axis) const; + void _render_sidebar_scale_hint(Axis axis) const; + void _render_sidebar_size_hint(Axis axis, double length) const; + enum SyncRotationType { + // Do not synchronize rotation. Either not rotating at all, or rotating by world Z axis. + SYNC_ROTATION_NONE = 0, + // Synchronize fully. Used from "place on bed" feature. + SYNC_ROTATION_FULL = 1, + // Synchronize after rotation by an axis not parallel with Z. + SYNC_ROTATION_GENERAL = 2, + }; + void _synchronize_unselected_instances(SyncRotationType sync_rotation_type); + void _synchronize_unselected_volumes(); + void _ensure_on_bed(); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GUI_Selection_hpp_