diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 9fba666b6..93fa586ed 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -447,6 +447,16 @@ const Vec3d &Transformation::get_rotation() const return m_temp_rotation; } +const Vec3d &Transformation::get_rotation_by_quaternion() const +{ + Matrix3d rotation_matrix = m_matrix.matrix().block(0, 0, 3, 3); + Eigen::Quaterniond quaternion(rotation_matrix); + quaternion.normalize(); + m_temp_rotation = quaternion.matrix().eulerAngles(2, 1, 0); + std::swap(m_temp_rotation(0), m_temp_rotation(2)); + return m_temp_rotation; +} + Transform3d Transformation::get_rotation_matrix() const { return extract_rotation_matrix(m_matrix); } void Transformation::set_rotation(const Vec3d &rotation) diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 213550436..02afe33a0 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -395,6 +395,7 @@ public: void set_offset(Axis axis, double offset) { m_matrix.translation()[axis] = offset; } const Vec3d &get_rotation() const; + const Vec3d &get_rotation_by_quaternion() const; double get_rotation(Axis axis) const { return get_rotation()[axis]; } Transform3d get_rotation_matrix() const; diff --git a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp index 407debc7a..5d2667de3 100644 --- a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp +++ b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp @@ -91,12 +91,19 @@ void GizmoObjectManipulation::update_ui_from_settings() update_buffered_value(); } } +void delete_negative_sign(Vec3d& value) { + for (size_t i = 0; i < value.size(); i++) { + if (abs(value[i]) < 0.001) + value[i] = 0.f; + } +} -void GizmoObjectManipulation::update_settings_value(const Selection& selection) +void GizmoObjectManipulation::update_settings_value(const Selection &selection) { m_new_move_label_string = L("Position"); m_new_rotate_label_string = L("Rotate (relative)"); m_new_rotation = Vec3d::Zero(); + m_new_absolute_rotation = Vec3d::Zero(); m_new_scale_label_string = L("Scale ratios"); ObjectList* obj_list = wxGetApp().obj_list(); @@ -104,7 +111,9 @@ void GizmoObjectManipulation::update_settings_value(const Selection& selection) // all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); m_new_position = volume->get_instance_offset(); - + auto rotation = volume->get_instance_transformation().get_rotation_by_quaternion(); + m_new_absolute_rotation = rotation * (180. / M_PI); + delete_negative_sign(m_new_absolute_rotation); if (is_world_coordinates()) {//for move and rotate m_new_size = selection.get_bounding_box_in_current_reference_system().first.size(); m_unscale_size = selection.get_unscaled_instance_bounding_box().size(); @@ -132,6 +141,9 @@ void GizmoObjectManipulation::update_settings_value(const Selection& selection) m_new_title_string = L("Object Operations"); } else if (selection.is_single_volume_or_modifier()) { const GLVolume *volume = selection.get_first_volume(); + auto rotation = volume->get_volume_transformation().get_rotation_by_quaternion(); + m_new_absolute_rotation = rotation * (180. / M_PI); + delete_negative_sign(m_new_absolute_rotation); if (is_world_coordinates()) {//for move and rotate const Geometry::Transformation trafo(volume->world_matrix()); const Vec3d &offset = trafo.get_offset(); @@ -180,7 +192,7 @@ void GizmoObjectManipulation::update_buffered_value() m_buffered_position = this->m_new_position; m_buffered_rotation = this->m_new_rotation; - + m_buffered_absolute_rotation = this->m_new_absolute_rotation; m_buffered_scale = this->m_new_scale; if (this->m_imperial_units) @@ -206,6 +218,7 @@ void GizmoObjectManipulation::update_if_dirty() }; update_label(m_cache.move_label_string, m_new_move_label_string); update_label(m_cache.rotate_label_string, m_new_rotate_label_string); + update_label(m_cache.rotate_label_string, m_new_rotate_label_string); update_label(m_cache.scale_label_string, m_new_scale_label_string); enum ManipulationEditorKey @@ -234,6 +247,7 @@ void GizmoObjectManipulation::update_if_dirty() update(m_cache.scale, m_cache.scale_rounded, m_new_scale); update(m_cache.size, m_cache.size_rounded, m_new_size); update(m_cache.rotation, m_cache.rotation_rounded, m_new_rotation); + update(m_cache.absolute_rotation, m_cache.absolute_rotation_rounded, m_new_absolute_rotation); } update_reset_buttons_visibility(); @@ -274,6 +288,7 @@ void GizmoObjectManipulation::reset_settings_value() { m_new_position = Vec3d::Zero(); m_new_rotation = Vec3d::Zero(); + m_new_absolute_rotation = Vec3d::Zero(); m_new_scale = Vec3d::Ones() * 100.; m_new_size = Vec3d::Zero(); m_new_enabled = false; @@ -345,6 +360,34 @@ void GizmoObjectManipulation::change_rotation_value(int axis, double value) this->UpdateAndShow(true); } +void GizmoObjectManipulation::change_absolute_rotation_value(int axis, double value) { + if (std::abs(m_cache.absolute_rotation_rounded(axis) - value) < EPSILON) + return; + + Vec3d absolute_rotation = m_cache.absolute_rotation; + absolute_rotation(axis) = value; + + Selection &selection = m_glcanvas.get_selection(); + TransformationType transformation_type; + transformation_type.set_relative(); + if (selection.is_single_full_instance()) + transformation_type.set_independent(); + if (is_local_coordinates()) + transformation_type.set_local(); + if (is_instance_coordinates()) + transformation_type.set_instance(); + + selection.setup_cache(); + auto diff_rotation = transformation_type.absolute() ? absolute_rotation : absolute_rotation - m_cache.absolute_rotation; + selection.rotate((M_PI / 180.0) * diff_rotation, transformation_type); + wxGetApp().plater()->take_snapshot("set absolute orientation", UndoRedo::SnapshotType::GizmoAction); + m_glcanvas.do_rotate(""); + + m_cache.absolute_rotation = absolute_rotation; + m_cache.absolute_rotation_rounded(axis) = DBL_MAX; + this->UpdateAndShow(true); +} + void GizmoObjectManipulation::change_scale_value(int axis, double value) { if (value <= 0.0) @@ -441,6 +484,8 @@ void GizmoObjectManipulation::on_change(const std::string &opt_key, int axis, do change_position_value(axis, new_value); else if (opt_key == "rotation") change_rotation_value(axis, new_value); + else if (opt_key == "absolute_rotation") + change_absolute_rotation_value(axis, new_value); else if (opt_key == "scale") change_scale_value(axis, new_value); else if (opt_key == "size") @@ -578,9 +623,10 @@ void GizmoObjectManipulation::set_coordinates_type(ECoordinatesType type) } -static const char* label_values[2][3] = { +static const char* label_values[3][3] = { { "##position_x", "##position_y", "##position_z"}, -{ "##rotation_x", "##rotation_y", "##rotation_z"} +{ "##rotation_x", "##rotation_y", "##rotation_z"}, +{ "##absolute_rotation_x", "##absolute_rotation_y", "##absolute_rotation_z"} }; static const char* label_scale_values[2][3] = { @@ -800,7 +846,6 @@ void GizmoObjectManipulation::set_init_rotation(const Geometry::Transformation & Vec3d display_position = m_buffered_position; // Rotation - Vec3d rotation = this->m_buffered_rotation; float unit_size = imgui_wrapper->calc_text_size(MAX_SIZE).x + space_size; int index = 1; int index_unit = 1; @@ -949,7 +994,7 @@ void GizmoObjectManipulation::do_render_rotate_window(ImGuiWrapper *imgui_wrappe Vec3d display_position = m_buffered_position; // Rotation Vec3d rotation = this->m_buffered_rotation; - + Vec3d absolute_rotation = this->m_buffered_absolute_rotation; float unit_size = imgui_wrapper->calc_text_size(MAX_SIZE).x + space_size; int index = 1; int index_unit = 1; @@ -967,33 +1012,41 @@ void GizmoObjectManipulation::do_render_rotate_window(ImGuiWrapper *imgui_wrappe ImGui::SameLine(caption_max + (++index_unit) * unit_size + (++index) * space_size); ImGui::PushItemWidth(unit_size); ImGui::TextAlignCenter("Z"); - if (m_show_reset_0_rotation) { - ImGui::SameLine(caption_max + 3 * unit_size + 4 * space_size + end_text_size); - if (reset_zero_button(imgui_wrapper, caption_max, unit_size, space_size, end_text_size)) { reset_rotation_value(false); } - if (ImGui::IsItemHovered()) { - float tooltip_size = imgui_wrapper->calc_text_size(_L("Reset current rotation to real zeros.")).x + 3 * space_size; - imgui_wrapper->tooltip(_L("Reset current rotation to real zeros."), tooltip_size); - } - } + index = 1; index_unit = 1; // ImGui::PushItemWidth(unit_size * 2); + bool is_relative_input = false; ImGui::AlignTextToFramePadding(); imgui_wrapper->text(_L("Rotate (relative)")); ImGui::SameLine(caption_max + index * space_size); ImGui::PushItemWidth(unit_size); - ImGui::BBLInputDouble(label_values[1][0], &rotation[0], 0.0f, 0.0f, "%.2f"); + if (ImGui::BBLInputDouble(label_values[1][0], &rotation[0], 0.0f, 0.0f, "%.2f")) { + is_relative_input = true; + } ImGui::SameLine(caption_max + unit_size + (++index) * space_size); ImGui::PushItemWidth(unit_size); - ImGui::BBLInputDouble(label_values[1][1], &rotation[1], 0.0f, 0.0f, "%.2f"); + if (ImGui::BBLInputDouble(label_values[1][1], &rotation[1], 0.0f, 0.0f, "%.2f")) { + is_relative_input = true; + } ImGui::SameLine(caption_max + (++index_unit) * unit_size + (++index) * space_size); ImGui::PushItemWidth(unit_size); - ImGui::BBLInputDouble(label_values[1][2], &rotation[2], 0.0f, 0.0f, "%.2f"); + if (ImGui::BBLInputDouble(label_values[1][2], &rotation[2], 0.0f, 0.0f, "%.2f")) { + is_relative_input = true; + } ImGui::SameLine(caption_max + (++index_unit) * unit_size + (++index) * space_size); imgui_wrapper->text(_L("°")); m_buffered_rotation = rotation; - update(current_active_id, "rotation", this->m_new_rotation, m_buffered_rotation); + if (is_relative_input) { + m_last_rotate_type = RotateType::Relative; + } + if (m_last_rotate_type == RotateType::Relative) { + bool is_valid = update(current_active_id, "rotation", this->m_new_rotation, m_buffered_rotation) >= 0; + if (is_valid) { + m_last_rotate_type = RotateType::None; + } + } if (m_show_clear_rotation) { ImGui::SameLine(caption_max + 3 * unit_size + 4 * space_size + end_text_size); @@ -1008,7 +1061,6 @@ void GizmoObjectManipulation::do_render_rotate_window(ImGuiWrapper *imgui_wrappe ImGui::SameLine(caption_max + 3 * unit_size + 5 * space_size + end_text_size); ImGui::InvisibleButton("", ImVec2(ImGui::GetFontSize(), ImGui::GetFontSize())); } - // send focus to m_glcanvas bool focued_on_text = false; for (int j = 0; j < 3; j++) { @@ -1019,7 +1071,61 @@ void GizmoObjectManipulation::do_render_rotate_window(ImGuiWrapper *imgui_wrappe break; } } - if (!focued_on_text) m_glcanvas.handle_sidebar_focus_event("", false); + + index = 1; + index_unit = 1; + ImGui::AlignTextToFramePadding(); + imgui_wrapper->text(_L("Rotate (absolute)")); + ImGui::SameLine(caption_max + index * space_size); + ImGui::PushItemWidth(unit_size); + bool is_absolute_input = false; + if (ImGui::BBLInputDouble(label_values[2][0], &absolute_rotation[0], 0.0f, 0.0f, "%.2f")) { + is_absolute_input = true; + } + ImGui::SameLine(caption_max + unit_size + (++index) * space_size); + ImGui::PushItemWidth(unit_size); + if (ImGui::BBLInputDouble(label_values[2][1], &absolute_rotation[1], 0.0f, 0.0f, "%.2f")) { + is_absolute_input = true; + } + ImGui::SameLine(caption_max + (++index_unit) * unit_size + (++index) * space_size); + ImGui::PushItemWidth(unit_size); + if (ImGui::BBLInputDouble(label_values[2][2], &absolute_rotation[2], 0.0f, 0.0f, "%.2f")) { + is_absolute_input = true; + } + ImGui::SameLine(caption_max + (++index_unit) * unit_size + (++index) * space_size); + imgui_wrapper->text(_L("°")); + m_buffered_absolute_rotation = absolute_rotation; + if (is_absolute_input) { + m_last_rotate_type = RotateType::Absolute; + } + if (m_last_rotate_type == RotateType::Absolute) { + bool is_valid = update(current_active_id, "absolute_rotation", this->m_new_absolute_rotation, m_buffered_absolute_rotation) >= 0; + if (is_valid) { + m_last_rotate_type = RotateType::None; + } + } + + if (m_show_reset_0_rotation) { + ImGui::SameLine(caption_max + 3 * unit_size + 4 * space_size + end_text_size); + if (reset_zero_button(imgui_wrapper, caption_max, unit_size, space_size, end_text_size)) { reset_rotation_value(false); } + if (ImGui::IsItemHovered()) { + float tooltip_size = imgui_wrapper->calc_text_size(_L("Reset current rotation to real zeros.")).x + 3 * space_size; + imgui_wrapper->tooltip(_L("Reset current rotation to real zeros."), tooltip_size); + } + } + // send focus to m_glcanvas + bool absolute_focued_on_text = false; + for (int j = 0; j < 3; j++) { + unsigned int id = ImGui::GetID(label_values[2][j]); + if (current_active_id == id) { + m_glcanvas.handle_sidebar_focus_event(label_values[2][j] + 2, true); + absolute_focued_on_text = true; + break; + } + } + if (!focued_on_text && !absolute_focued_on_text) + m_glcanvas.handle_sidebar_focus_event("", false); + float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y; float tip_caption_max = 0.f; float total_text_max = 0.f; diff --git a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp index a0ed0a64b..5228bd182 100644 --- a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp +++ b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp @@ -27,6 +27,8 @@ public: Vec3d position_rounded; Vec3d rotation; Vec3d rotation_rounded; + Vec3d absolute_rotation; + Vec3d absolute_rotation_rounded; Vec3d scale; Vec3d scale_rounded; Vec3d size; @@ -72,11 +74,13 @@ public: std::string m_new_unit_string; Vec3d m_new_position; Vec3d m_new_rotation; + Vec3d m_new_absolute_rotation; Vec3d m_new_scale; Vec3d m_new_size; Vec3d m_unscale_size; Vec3d m_buffered_position; Vec3d m_buffered_rotation; + Vec3d m_buffered_absolute_rotation; Vec3d m_buffered_scale; Vec3d m_buffered_size; Vec3d cs_center; @@ -89,6 +93,9 @@ public: bool m_show_clear_rotation { false }; bool m_show_clear_scale { false }; bool m_show_drop_to_bed { false }; + enum class RotateType { None, Relative, Absolute + }; + RotateType m_last_rotate_type{RotateType::None}; // 0:no input 1:relative 2:absolute protected: float last_move_input_window_width = 0.0f; @@ -150,6 +157,7 @@ private: // change values void change_position_value(int axis, double value); void change_rotation_value(int axis, double value); + void change_absolute_rotation_value(int axis, double value); void change_scale_value(int axis, double value); void change_size_value(int axis, double value); void do_scale(int axis, const Vec3d &scale) const; diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index bb3e4d334..b31137ec3 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -2048,6 +2048,8 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field, bool unif 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, "absolute_rotation")) + render_sidebar_rotation_hints(sidebar_field); else if (boost::starts_with(sidebar_field, "scale") || boost::starts_with(sidebar_field, "size")) //BBS: GUI refactor: add uniform_scale from gizmo render_sidebar_scale_hints(sidebar_field, uniform_scale);