diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 219f17082..ef60ab3fa 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 68b343ba5..1236ee7b4 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 { @@ -738,1654 +737,6 @@ GLCanvas3D::Mouse::Mouse() { } -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 @@ -2574,7 +925,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 = ""; @@ -2620,7 +971,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; @@ -2858,7 +1209,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; @@ -2944,7 +1295,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; @@ -2968,7 +1319,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; @@ -2976,7 +1327,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; @@ -2986,7 +1337,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; @@ -3018,7 +1369,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; @@ -3178,7 +1529,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) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 639d10120..9653b138b 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 @@ -334,293 +334,6 @@ class GLCanvas3D }; 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]; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 183820aed..2dda6b7cd 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" @@ -1275,7 +1276,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 @@ -1563,7 +1564,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(); } @@ -1873,7 +1874,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 @@ -1971,7 +1972,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) { @@ -1980,8 +1981,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; @@ -2240,7 +2241,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 ab83df05d..d186f7057 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -7,6 +7,8 @@ #include "PresetBundle.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/Geometry.hpp" +#include "GLCanvas3D.hpp" +#include "Selection.hpp" #include @@ -155,7 +157,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 +350,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,10 +361,10 @@ 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. @@ -384,7 +386,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 +420,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 16160c84d..46e8cec81 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -4,7 +4,6 @@ #include #include "GUI_ObjectSettings.hpp" -#include "GLCanvas3D.hpp" class wxStaticText; class PrusaLockButton; @@ -12,6 +11,7 @@ class PrusaLockButton; namespace Slic3r { namespace GUI { +class Selection; class ObjectManipulation : public OG_Settings { @@ -90,7 +90,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/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 290541a18..1e12674ec 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 e8328bcd4..b70c1f8df 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 54a39c6d5..e228706c0 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 beec6d1da..fd4e8d8dc 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 d72dc4913..2ed048677 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 c91924c5a..1bd17e5ef 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 d18d71c83..03151c433 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 d4d97de7f..ddab2b777 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 4c6f66f4c..72b9ad18a 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 aca64c94e..f5946aa56 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 acc44e00d..43152b56c 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 8554e57a7..6e14a361e 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 695564971..cd7e3b985 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; @@ -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 492bb85ca..c27ff0b6d 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 6ddb4c837..630586bb5 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" @@ -1153,8 +1154,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(); @@ -1641,7 +1642,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) { @@ -1790,12 +1791,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(); } diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp new file mode 100644 index 000000000..e5d221a5f --- /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 000000000..3a21122df --- /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_