diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 27b961623..c18cd3d49 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -500,6 +500,15 @@ void Transformation::set_rotation(Axis axis, double rotation) } } +Transform3d Transformation::get_scaling_factor_matrix() const +{ + Transform3d scale = extract_scale(m_matrix); + scale(0, 0) = std::abs(scale(0, 0)); + scale(1, 1) = std::abs(scale(1, 1)); + scale(2, 2) = std::abs(scale(2, 2)); + return scale; +} + void Transformation::set_scaling_factor(const Vec3d& scaling_factor) { set_scaling_factor(X, scaling_factor(0)); diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 8202de271..f28eba200 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -406,6 +406,7 @@ public: void set_rotation(const Vec3d& rotation); void set_rotation(Axis axis, double rotation); + Transform3d get_scaling_factor_matrix() const; const Vec3d& get_scaling_factor() const { return m_scaling_factor; } double get_scaling_factor(Axis axis) const { return m_scaling_factor(axis); } @@ -421,6 +422,7 @@ public: void set_mirror(Axis axis, double mirror); void set_from_transform(const Transform3d& transform); + void set_matrix(const Transform3d &transform) { set_from_transform(transform); } void reset(); void reset_offset() { set_offset(Vec3d::Zero()); } diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index a4bce5232..4a06a7213 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -437,7 +437,7 @@ public: const Geometry::Transformation& get_instance_transformation() const { return m_instance_transformation; } void set_instance_transformation(const Geometry::Transformation& transformation) { m_instance_transformation = transformation; set_bounding_boxes_as_dirty(); } - + void set_instance_transformation(const Transform3d &transform){ m_instance_transformation.set_matrix(transform); set_bounding_boxes_as_dirty(); } const Vec3d& get_instance_offset() const { return m_instance_transformation.get_offset(); } double get_instance_offset(Axis axis) const { return m_instance_transformation.get_offset(axis); } @@ -464,6 +464,7 @@ public: const Geometry::Transformation& get_volume_transformation() const { return m_volume_transformation; } void set_volume_transformation(const Geometry::Transformation& transformation) { m_volume_transformation = transformation; set_bounding_boxes_as_dirty(); } + void set_volume_transformation(const Transform3d &transform) { m_volume_transformation.set_matrix(transform); set_bounding_boxes_as_dirty(); } const Vec3d& get_volume_offset() const { return m_volume_transformation.get_offset(); } double get_volume_offset(Axis axis) const { return m_volume_transformation.get_offset(axis); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index d5889665c..0ddfc6c7e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -973,7 +973,26 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) case Rotate: { // Apply new temporary rotations - TransformationType transformation_type(TransformationType::World_Relative_Joint); + TransformationType transformation_type; + if (m_parent.get_selection().is_wipe_tower()) + transformation_type = TransformationType::World_Relative_Joint; + else { + switch (wxGetApp().obj_manipul()->get_coordinates_type()) { + default: + case ECoordinatesType::World: { + transformation_type = TransformationType::World_Relative_Joint; + break; + } + case ECoordinatesType::Instance: { + transformation_type = TransformationType::Instance_Relative_Joint; + break; + } + case ECoordinatesType::Local: { + transformation_type = TransformationType::Local_Relative_Joint; + break; + } + } + } if (evt.AltDown()) transformation_type.set_independent(); selection.rotate(get_rotation(), transformation_type); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 6977fa79a..d26a842e7 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -23,6 +23,9 @@ #include #include +#include +#include +#include static const std::array UNIFORM_SCALE_COLOR = { 0.923f, 0.504f, 0.264f, 1.0f }; namespace Slic3r { @@ -36,7 +39,7 @@ Selection::VolumeCache::TransformCache::TransformCache() , rotation_matrix(Transform3d::Identity()) , scale_matrix(Transform3d::Identity()) , mirror_matrix(Transform3d::Identity()) - , full_matrix(Transform3d::Identity()) + , full_tran(Transform3d::Identity()) { } @@ -45,7 +48,7 @@ Selection::VolumeCache::TransformCache::TransformCache(const Geometry::Transform , rotation(transform.get_rotation()) , scaling_factor(transform.get_scaling_factor()) , mirror(transform.get_mirror()) - , full_matrix(transform.get_matrix()) + , full_tran(transform) { rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation); scale_matrix = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scaling_factor); @@ -1002,6 +1005,40 @@ void Selection::move_to_center(const Vec3d& displacement, bool local) this->set_bounding_boxes_dirty(); } +const std::pair Selection::get_bounding_sphere() const +{ + if (!m_bounding_sphere.has_value()) { + std::optional> *sphere = const_cast> *>(&m_bounding_sphere); + *sphere = {Vec3d::Zero(), 0.0}; + + using K = CGAL::Simple_cartesian; + using Traits = CGAL::Min_sphere_of_points_d_traits_3; + using Min_sphere = CGAL::Min_sphere_of_spheres_d; + using Point = K::Point_3; + + std::vector points; + if (m_valid) { + for (unsigned int i : m_list) { + const GLVolume & volume = *(*m_volumes)[i]; + const TriangleMesh * hull = volume.convex_hull(); + const indexed_triangle_set &its = (hull != nullptr) ? hull->its : m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh().its; + const Transform3d & matrix = volume.world_matrix(); + for (const Vec3f &v : its.vertices) { + const Vec3d vv = matrix * v.cast(); + points.push_back(Point(vv.x(), vv.y(), vv.z())); + } + } + + Min_sphere ms(points.begin(), points.end()); + const float *center_x = ms.center_cartesian_begin(); + (*sphere)->first = {*center_x, *(center_x + 1), *(center_x + 2)}; + (*sphere)->second = ms.radius(); + } + } + + return *m_bounding_sphere; +} + void Selection::setup_cache() { if (!m_valid) @@ -1141,33 +1178,60 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ }; for (unsigned int i : m_list) { + Transform3d rotation_matrix = Geometry::rotation_transform(rotation); GLVolume &v = *(*m_volumes)[i]; - if (is_single_full_instance()) - rotate_instance(v, i); - else if (is_single_volume() || is_single_modifier()) { - if (transformation_type.independent()) - v.set_volume_rotation(m_cache.volumes_data[i].get_volume_rotation() + rotation); - else { - const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); - const Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); - v.set_volume_rotation(new_rotation); + const VolumeCache &volume_data = m_cache.volumes_data[i]; + const Geometry::Transformation &inst_trafo = volume_data.get_instance_transform(); + if (m_mode == Instance ||is_single_full_instance()) { + assert(is_from_fully_selected_instance(i)); + if (transformation_type.instance()) { + // ensure that the instance rotates as a rigid body + Transform3d inst_rotation_matrix = inst_trafo.get_rotation_matrix(); + if (inst_trafo.is_left_handed()) { + Geometry::TransformationSVD inst_svd(inst_trafo); + inst_rotation_matrix = inst_svd.u * inst_svd.v.transpose(); + // ensure the rotation has the proper direction + if (!rotation.normalized().cwiseAbs().isApprox(Vec3d::UnitX())) rotation_matrix = rotation_matrix.inverse(); + } + + const Transform3d inst_matrix_no_offset = inst_trafo.get_matrix_no_offset(); + rotation_matrix = inst_matrix_no_offset.inverse() * inst_rotation_matrix * rotation_matrix * inst_rotation_matrix.inverse() * inst_matrix_no_offset; + + // rotate around selection center + const Vec3d inst_pivot = inst_trafo.get_matrix_no_offset().inverse() * (m_cache.rotation_pivot - inst_trafo.get_offset()); + rotation_matrix = Geometry::translation_transform(inst_pivot) * rotation_matrix * Geometry::translation_transform(-inst_pivot); } + transform_instance_relative(v, volume_data, transformation_type, rotation_matrix, m_cache.rotation_pivot); } + else if (!is_single_volume_or_modifier()) { + assert(transformation_type.world()); + transform_volume_relative(v, volume_data, transformation_type, rotation_matrix, m_cache.rotation_pivot); + } + else { - if (m_mode == Instance) - rotate_instance(v, 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()) { - const Vec3d local_pivot = m_cache.volumes_data[i].get_instance_full_matrix().inverse() * m_cache.dragging_center; - const Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() - local_pivot); - v.set_volume_offset(local_pivot + offset); + if (transformation_type.instance()) {//in object Coordinate System + // ensure that the volume rotates as a rigid body + const Transform3d inst_scale_matrix = inst_trafo.get_scaling_factor_matrix(); + rotation_matrix = inst_scale_matrix.inverse() * rotation_matrix * inst_scale_matrix; + } else { + if (transformation_type.local()) { + // ensure that the volume rotates as a rigid body + const Geometry::Transformation &vol_trafo = volume_data.get_volume_transform(); + const Transform3d vol_matrix_no_offset = vol_trafo.get_matrix_no_offset(); + const Transform3d inst_scale_matrix = inst_trafo.get_scaling_factor_matrix(); + Transform3d vol_rotation_matrix = vol_trafo.get_rotation_matrix(); + if (vol_trafo.is_left_handed()) { + Geometry::TransformationSVD vol_svd(vol_trafo); + vol_rotation_matrix = vol_svd.u * vol_svd.v.transpose(); + // ensure the rotation has the proper direction + if (!rotation.normalized().cwiseAbs().isApprox(Vec3d::UnitX())) rotation_matrix = rotation_matrix.inverse(); + } + rotation_matrix = vol_matrix_no_offset.inverse() * inst_scale_matrix.inverse() * vol_rotation_matrix * rotation_matrix * + vol_rotation_matrix.inverse() * inst_scale_matrix * vol_matrix_no_offset; } - v.set_volume_rotation(new_rotation); } + transform_volume_relative(v, volume_data, transformation_type, rotation_matrix, m_cache.rotation_pivot); } } } @@ -2264,6 +2328,7 @@ void Selection::set_caches() m_cache.sinking_volumes.push_back(i); } m_cache.dragging_center = get_bounding_box().center(); + m_cache.rotation_pivot = get_bounding_sphere().first; } void Selection::do_add_volume(unsigned int volume_idx) @@ -2915,5 +2980,45 @@ void Selection::paste_objects_from_clipboard() #endif /* _DEBUG */ } +void Selection::transform_instance_relative( + GLVolume &volume, const VolumeCache &volume_data, TransformationType transformation_type, const Transform3d &transform, const Vec3d &world_pivot) +{ + assert(transformation_type.relative()); + + const Geometry::Transformation &inst_trafo = volume_data.get_instance_transform(); + if (transformation_type.world()) { + const Vec3d inst_pivot = transformation_type.independent() && !is_from_single_instance() ? inst_trafo.get_offset() : world_pivot; + const Transform3d trafo = Geometry::translation_transform(inst_pivot) * transform * Geometry::translation_transform(-inst_pivot); + volume.set_instance_transformation(trafo * inst_trafo.get_matrix()); + } else if (transformation_type.instance()) + volume.set_instance_transformation(inst_trafo.get_matrix() * transform); + else + assert(false); +} + +void Selection::transform_volume_relative( + GLVolume &volume, const VolumeCache &volume_data, TransformationType transformation_type, const Transform3d &transform, const Vec3d &world_pivot) +{ + assert(transformation_type.relative()); + + const Geometry::Transformation &vol_trafo = volume_data.get_volume_transform(); + const Geometry::Transformation &inst_trafo = volume_data.get_instance_transform(); + + if (transformation_type.world()) { + const Vec3d inst_pivot = transformation_type.independent() ? vol_trafo.get_offset() : (Vec3d) (inst_trafo.get_matrix().inverse() * world_pivot); + const Transform3d inst_matrix_no_offset = inst_trafo.get_matrix_no_offset(); + const Transform3d trafo = Geometry::translation_transform(inst_pivot) * inst_matrix_no_offset.inverse() * transform * inst_matrix_no_offset * + Geometry::translation_transform(-inst_pivot); + volume.set_volume_transformation(trafo * vol_trafo.get_matrix()); + } else if (transformation_type.instance()) { + const Vec3d inst_pivot = transformation_type.independent() ? vol_trafo.get_offset() : (Vec3d) (inst_trafo.get_matrix().inverse() * world_pivot); + const Transform3d trafo = Geometry::translation_transform(inst_pivot) * transform * Geometry::translation_transform(-inst_pivot); + volume.set_volume_transformation(trafo * vol_trafo.get_matrix()); + } else if (transformation_type.local()) + volume.set_volume_transformation(vol_trafo.get_matrix() * transform); + else + assert(false); +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 2f58920c2..90cb94ae6 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -38,45 +38,69 @@ public: enum Enum { // Transforming in a world coordinate system World = 0, + // Transforming in a instance coordinate system + Instance = 1, // Transforming in a local coordinate system - Local = 1, + Local = 2, // Absolute transformations, allowed in local coordinate system only. Absolute = 0, // Relative transformations, allowed in both local and world coordinate system. - Relative = 2, + Relative = 4, // 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, + Independent = 8, - 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, + World_Relative_Joint = World | Relative | Joint, + World_Relative_Independent = World | Relative | Independent, + Instance_Absolute_Joint = Instance | Absolute | Joint, + Instance_Absolute_Independent = Instance | Absolute | Independent, + Instance_Relative_Joint = Instance | Relative | Joint, + Instance_Relative_Independent = Instance | 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; } + 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; } + 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); } + void set_world() + { + this->remove(Instance); + this->remove(Local); + } + void set_instance() + { + this->remove(Local); + this->add(Instance); + } + void set_local() + { + this->remove(Instance); + 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); } + bool world() const { return !this->has(Instance) && !this->has(Local); } + bool instance() const { return this->has(Instance); } + 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); } @@ -125,7 +149,7 @@ private: Transform3d rotation_matrix; Transform3d scale_matrix; Transform3d mirror_matrix; - Transform3d full_matrix; + Geometry::Transformation full_tran; TransformCache(); explicit TransformCache(const Geometry::Transformation& transform); @@ -145,7 +169,7 @@ private: 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 Transform3d &get_volume_full_matrix() const { return m_volume.full_tran.get_matrix(); } const Vec3d& get_instance_position() const { return m_instance.position; } const Vec3d& get_instance_rotation() const { return m_instance.rotation; } @@ -154,7 +178,10 @@ private: 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; } + const Transform3d &get_instance_full_matrix() const { return m_instance.full_tran.get_matrix(); } + + const Geometry::Transformation &get_volume_transform() const { return m_volume.full_tran; } + const Geometry::Transformation &get_instance_transform() const { return m_instance.full_tran; } }; public: @@ -199,6 +226,7 @@ private: ObjectIdxsToInstanceIdxsMap content; // List of ids of the volumes which are sinking when starting dragging std::vector sinking_volumes; + Vec3d rotation_pivot; }; // Volumes owned by GLCanvas3D. @@ -223,6 +251,8 @@ private: // Bounding box aligned to the axis of the currently selected reference system (World/Object/Part) // and transform to place and orient it in world coordinates std::optional> m_bounding_box_in_current_reference_system; + + std::optional> m_bounding_sphere; #if ENABLE_RENDER_SELECTION_CENTER GLModel m_vbo_sphere; #endif // ENABLE_RENDER_SELECTION_CENTER @@ -350,6 +380,8 @@ public: void start_dragging(); void stop_dragging() { m_dragging = false; } bool is_dragging() const { return m_dragging; } + // Returns the bounding sphere: first = center, second = radius + const std::pair get_bounding_sphere() const; void setup_cache(); void translate(const Vec3d& displacement, bool local = false); @@ -425,6 +457,7 @@ private: m_bounding_box.reset(); m_unscaled_instance_bounding_box.reset(); m_scaled_instance_bounding_box.reset(); m_bounding_box_in_current_reference_system.reset(); + m_bounding_sphere.reset(); } void render_selected_volumes() const; void render_synchronized_volumes() const; @@ -452,6 +485,11 @@ private: void paste_volumes_from_clipboard(); void paste_objects_from_clipboard(); + + void transform_instance_relative( + GLVolume &volume, const VolumeCache &volume_data, TransformationType transformation_type, const Transform3d &transform, const Vec3d &world_pivot); + void transform_volume_relative( + GLVolume &volume, const VolumeCache &volume_data, TransformationType transformation_type, const Transform3d &transform, const Vec3d &world_pivot); }; } // namespace GUI