From 3d45414b7113a8eef80338a8deb81f2f71b21120 Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Wed, 6 Nov 2024 23:09:02 +0800 Subject: [PATCH] Assembly: Port BBS' assembly gizmo Co-authored-by: zhou.xu --- resources/images/toolbar_assembly.svg | 10 +- resources/images/toolbar_assembly_dark.svg | 10 +- src/libslic3r/Geometry.cpp | 121 +++++---- src/libslic3r/Geometry.hpp | 3 +- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/Gizmos/GLGizmoAssembly.cpp | 167 ++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoAssembly.hpp | 43 ++++ src/slic3r/GUI/Gizmos/GLGizmoBase.cpp | 33 +++ src/slic3r/GUI/Gizmos/GLGizmoBase.hpp | 3 + src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 38 +-- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 1 - src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 286 +++++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 13 + src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 24 +- src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 1 + src/slic3r/GUI/Selection.cpp | 60 +++++ src/slic3r/GUI/Selection.hpp | 4 + 17 files changed, 715 insertions(+), 104 deletions(-) create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoAssembly.cpp create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoAssembly.hpp diff --git a/resources/images/toolbar_assembly.svg b/resources/images/toolbar_assembly.svg index 4fba90b72..1c2a025f5 100644 --- a/resources/images/toolbar_assembly.svg +++ b/resources/images/toolbar_assembly.svg @@ -1,7 +1,7 @@ - - - - - + + + + + diff --git a/resources/images/toolbar_assembly_dark.svg b/resources/images/toolbar_assembly_dark.svg index 302b4d745..f514dcb5c 100644 --- a/resources/images/toolbar_assembly_dark.svg +++ b/resources/images/toolbar_assembly_dark.svg @@ -1,7 +1,7 @@ - - - - - + + + + + diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 49e50a671..3e67d4e1b 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -717,53 +717,6 @@ Transformation Transformation::volume_to_bed_transformation(const Transformation return out; } -TransformationSVD::TransformationSVD(const Transform3d& trafo) -{ - const auto &m0 = trafo.matrix().block<3, 3>(0, 0); - mirror = m0.determinant() < 0.0; - - Matrix3d m; - if (mirror) - m = m0 * Eigen::DiagonalMatrix(-1.0, 1.0, 1.0); - else - m = m0; - const Eigen::JacobiSVD svd(m, Eigen::ComputeFullU | Eigen::ComputeFullV); - u = svd.matrixU(); - v = svd.matrixV(); - s = svd.singularValues().asDiagonal(); - - scale = !s.isApprox(Matrix3d::Identity()); - anisotropic_scale = ! is_approx(s(0, 0), s(1, 1)) || ! is_approx(s(1, 1), s(2, 2)); - rotation = !v.isApprox(u); - - if (anisotropic_scale) { - rotation_90_degrees = true; - for (int i = 0; i < 3; ++i) { - const Vec3d row = v.row(i).cwiseAbs(); - const size_t num_zeros = is_approx(row[0], 0.) + is_approx(row[1], 0.) + is_approx(row[2], 0.); - const size_t num_ones = is_approx(row[0], 1.) + is_approx(row[1], 1.) + is_approx(row[2], 1.); - if (num_zeros != 2 || num_ones != 1) { - rotation_90_degrees = false; - break; - } - } - // Detect skew by brute force: check if the axes are still orthogonal after transformation - const Matrix3d trafo_linear = trafo.linear(); - const std::array axes = { Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ() }; - std::array transformed_axes; - for (int i = 0; i < 3; ++i) { - transformed_axes[i] = trafo_linear * axes[i]; - } - skew = std::abs(transformed_axes[0].dot(transformed_axes[1])) > EPSILON || - std::abs(transformed_axes[1].dot(transformed_axes[2])) > EPSILON || - std::abs(transformed_axes[2].dot(transformed_axes[0])) > EPSILON; - - // This following old code does not work under all conditions. The v matrix can become non diagonal (see SPE-1492) -// skew = ! rotation_90_degrees; - } else - skew = false; -} - // For parsing a transformation matrix from 3MF / AMF. Transform3d transform3d_from_string(const std::string& transform_str) { @@ -812,4 +765,78 @@ double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) return (axis.z() < 0) ? -angle : angle; } +TransformationSVD::TransformationSVD(const Transform3d& trafo) +{ + const auto &m0 = trafo.matrix().block<3, 3>(0, 0); + mirror = m0.determinant() < 0.0; + + Matrix3d m; + if (mirror) + m = m0 * Eigen::DiagonalMatrix(-1.0, 1.0, 1.0); + else + m = m0; + const Eigen::JacobiSVD svd(m, Eigen::ComputeFullU | Eigen::ComputeFullV); + u = svd.matrixU(); + v = svd.matrixV(); + s = svd.singularValues().asDiagonal(); + + scale = !s.isApprox(Matrix3d::Identity()); + anisotropic_scale = ! is_approx(s(0, 0), s(1, 1)) || ! is_approx(s(1, 1), s(2, 2)); + rotation = !v.isApprox(u); + + if (anisotropic_scale) { + rotation_90_degrees = true; + for (int i = 0; i < 3; ++i) { + const Vec3d row = v.row(i).cwiseAbs(); + const size_t num_zeros = is_approx(row[0], 0.) + is_approx(row[1], 0.) + is_approx(row[2], 0.); + const size_t num_ones = is_approx(row[0], 1.) + is_approx(row[1], 1.) + is_approx(row[2], 1.); + if (num_zeros != 2 || num_ones != 1) { + rotation_90_degrees = false; + break; + } + } + // Detect skew by brute force: check if the axes are still orthogonal after transformation + const Matrix3d trafo_linear = trafo.linear(); + const std::array axes = { Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ() }; + std::array transformed_axes; + for (int i = 0; i < 3; ++i) { + transformed_axes[i] = trafo_linear * axes[i]; + } + skew = std::abs(transformed_axes[0].dot(transformed_axes[1])) > EPSILON || + std::abs(transformed_axes[1].dot(transformed_axes[2])) > EPSILON || + std::abs(transformed_axes[2].dot(transformed_axes[0])) > EPSILON; + + // This following old code does not work under all conditions. The v matrix can become non diagonal (see SPE-1492) +// skew = ! rotation_90_degrees; + } else + skew = false; +} + + Transformation mat_around_a_point_rotate(const Transformation &InMat, const Vec3d &pt, const Vec3d &axis, float rotate_theta_radian) +{ + auto xyz = InMat.get_offset(); + Transformation left; + left.set_offset(-xyz); // at world origin + auto curMat = left * InMat; + + auto qua = Eigen::Quaterniond(Eigen::AngleAxisd(rotate_theta_radian, axis)); + qua.normalize(); + Transform3d cur_matrix; + Transformation rotateMat4; + rotateMat4.set_matrix(cur_matrix.fromPositionOrientationScale(Vec3d(0., 0., 0.), qua, Vec3d(1., 1., 1.))); + + curMat = rotateMat4 * curMat; // along_fix_axis + // rotate mat4 along fix pt + Transformation temp_world; + auto qua_world = Eigen::Quaterniond(Eigen::AngleAxisd(0, axis)); + qua_world.normalize(); + Transform3d cur_matrix_world; + temp_world.set_matrix(cur_matrix_world.fromPositionOrientationScale(pt, qua_world, Vec3d(1., 1., 1.))); + auto temp_xyz = temp_world.get_matrix().inverse() * xyz; + auto new_pos = temp_world.get_matrix() * (rotateMat4.get_matrix() * temp_xyz); + curMat.set_offset(new_pos); + + return curMat; +} + }} // namespace Slic3r::Geometry diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 2b027a231..8183dcb7e 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -10,7 +10,7 @@ // Serialization through the Cereal library #include -namespace Slic3r { +namespace Slic3r { namespace ClipperLib { class PolyNode; @@ -544,6 +544,7 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation) return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z()); } +Transformation mat_around_a_point_rotate(const Transformation& innMat, const Vec3d &pt, const Vec3d &axis, float rotate_theta_radian); } } // namespace Slicer::Geometry #endif diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 0c54d835a..10d3f3764 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -145,6 +145,8 @@ set(SLIC3R_GUI_SOURCES #GUI/Gizmos/GLGizmoFaceDetector.hpp GUI/Gizmos/GLGizmoMeasure.cpp GUI/Gizmos/GLGizmoMeasure.hpp + GUI/Gizmos/GLGizmoAssembly.cpp + GUI/Gizmos/GLGizmoAssembly.hpp GUI/Gizmos/GLGizmoSeam.cpp GUI/Gizmos/GLGizmoSeam.hpp #GUI/Gizmos/GLGizmoText.cpp diff --git a/src/slic3r/GUI/Gizmos/GLGizmoAssembly.cpp b/src/slic3r/GUI/Gizmos/GLGizmoAssembly.cpp new file mode 100644 index 000000000..c20ec975c --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoAssembly.cpp @@ -0,0 +1,167 @@ +#include "GLGizmoAssembly.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp" +#include "slic3r/Utils/UndoRedo.hpp" + +#include "libslic3r/PresetBundle.hpp" +#include "libslic3r/MeasureUtils.hpp" + +#include + +#include + +#include + +#include +#include +#include + +namespace Slic3r { +namespace GUI { + +GLGizmoAssembly::GLGizmoAssembly(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : + GLGizmoMeasure(parent, icon_filename, sprite_id) +{ + m_measure_mode = EMeasureMode::ONLY_ASSEMBLY; +} + +std::string GLGizmoAssembly::on_get_name() const +{ + if (!on_is_activable() && m_state == EState::Off) { + if (wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasAssembleView) { + return _u8L("Assemble") + ":\n" + _u8L("Please confirm explosion ratio = 1 and select at least two volumes."); + } + else { + return _u8L("Assemble") + ":\n" + _u8L("Please select at least two volumes."); + } + } else { + return _u8L("Assemble"); + } +} + +bool GLGizmoAssembly::on_is_activable() const +{ + const Selection& selection = m_parent.get_selection(); + if (selection.is_wipe_tower()) { + return false; + } + const int selection_volumes_count = 2; + if (wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasAssembleView) { + if (abs(m_parent.get_explosion_ratio() - 1.0f) < 1e-2 && selection.volumes_count() >= selection_volumes_count) { + return true; + } + return false; + } else { + return selection.volumes_count() >= selection_volumes_count; + } +} + +void GLGizmoAssembly::on_render_input_window(float x, float y, float bottom_limit) +{ + static std::optional last_feature; + static EMode last_mode = EMode::FeatureSelection; + static SelectedFeatures last_selected_features; + + static float last_y = 0.0f; + static float last_h = 0.0f; + + if (m_editing_distance) + return; + m_current_active_imgui_id = ImGui::GetActiveID(); + // adjust window position to avoid overlap the view toolbar + const float win_h = ImGui::GetWindowHeight(); + y = std::min(y, bottom_limit - win_h); + GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 0.0f, 0.0f); + if (last_h != win_h || last_y != y) { + // ask canvas for another frame to render the window in the correct position + m_imgui->set_requires_extra_frame(); + if (last_h != win_h) + last_h = win_h; + if (last_y != y) + last_y = y; + } + // Orca + ImGuiWrapper::push_toolbar_style(m_parent.get_scale()); + GizmoImguiBegin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar); + init_render_input_window(); + + float moving_size = m_imgui->calc_text_size(_L("(Moving)")).x; + float combox_content_size = m_imgui->calc_text_size(_L("Point and point assembly")).x*1.1 + ImGui::GetStyle().FramePadding.x * 18.0f; + float caption_size = moving_size + 2 * m_space_size; + if (render_assembly_mode_combo(caption_size + 0.5 * m_space_size, combox_content_size)) { + ; + } + show_selection_ui(); + show_face_face_assembly_common(); + ImGui::Separator(); + show_face_face_assembly_senior(); + show_distance_xyz_ui(); + render_input_window_warning(m_same_model_object); + ImGui::Separator(); + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f)); + float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y; + float caption_max = 0.f; + float total_text_max = 0.f; + for (const auto &t : std::array{"point_selection", "reset", "unselect"}) { + caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x); + total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x); + } + show_tooltip_information(caption_max, x, get_cur_y); + + float f_scale =m_parent.get_gizmos_manager().get_layout_scale(); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale)); + + ImGui::PopStyleVar(2); + + if (last_feature != m_curr_feature || last_mode != m_mode || last_selected_features != m_selected_features) { + // the dialog may have changed its size, ask for an extra frame to render it properly + last_feature = m_curr_feature; + last_mode = m_mode; + last_selected_features = m_selected_features; + m_imgui->set_requires_extra_frame(); + } + m_last_active_item_imgui = m_current_active_imgui_id; + GizmoImguiEnd(); + // Orca + ImGuiWrapper::pop_toolbar_style(); +} + +void GLGizmoAssembly::render_input_window_warning(bool same_model_object) +{ + if (wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasView3D) { + if (m_hit_different_volumes.size() == 2) { + if (same_model_object == false) { + m_imgui->warning_text(_L("Warning") + ": " + + _L("It is recommended to assemble the objects first,\nbecause the objects is restriced to bed \nand only parts can be lifted.")); + } + } + } +} + +bool GLGizmoAssembly::render_assembly_mode_combo(double label_width, float item_width) +{ + ImGui::AlignTextToFramePadding(); + int selection_idx = int(m_assembly_mode); + std::vector modes = {_u8L("Face and face assembly"), _u8L("Point and point assembly")}; + bool is_changed = false; + + ImGuiWrapper::push_combo_style(m_parent.get_scale()); + if (render_combo(_u8L("Mode"), modes, selection_idx, label_width, item_width)) { + is_changed = true; + switch_to_mode((AssemblyMode) selection_idx); + } + ImGuiWrapper::pop_combo_style(); + return is_changed; +} + +void GLGizmoAssembly::switch_to_mode(AssemblyMode new_mode) +{ + m_assembly_mode = new_mode; + reset_all_feature(); +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoAssembly.hpp b/src/slic3r/GUI/Gizmos/GLGizmoAssembly.hpp new file mode 100644 index 000000000..7046337b6 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoAssembly.hpp @@ -0,0 +1,43 @@ +#ifndef slic3r_GLGizmoAssembly_hpp_ +#define slic3r_GLGizmoAssembly_hpp_ + +#include "GLGizmoMeasure.hpp" + +namespace Slic3r { + +namespace GUI { +class GLGizmoAssembly : public GLGizmoMeasure +{ + +public: + GLGizmoAssembly(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + /// + /// Apply rotation on select plane + /// + /// Keep information about mouse click + /// Return True when use the information otherwise False. + //bool on_mouse(const wxMouseEvent &mouse_event) override; + //void data_changed(bool is_serializing) override; + //bool gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down) override; + + bool wants_enter_leave_snapshots() const override { return true; } + std::string get_gizmo_entering_text() const override { return _u8L("Entering Assembly gizmo"); } + std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Assembly gizmo"); } +protected: + //bool on_init() override; + std::string on_get_name() const override; + bool on_is_activable() const override; + //void on_render() override; + //void on_set_state() override; + virtual void on_render_input_window(float x, float y, float bottom_limit) override; + + void render_input_window_warning(bool same_model_object) override; + bool render_assembly_mode_combo(double label_width, float item_width); + + void switch_to_mode(AssemblyMode new_mode); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLGizmoAssembly_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 0a219a5cc..97875ee60 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -194,6 +194,39 @@ void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color) } } +bool GLGizmoBase::render_combo(const std::string &label, const std::vector &lines, int &selection_idx, float label_width, float item_width) +{ + ImGuiWrapper::push_combo_style(m_parent.get_scale()); + ImGui::AlignTextToFramePadding(); + m_imgui->text(label); + ImGui::SameLine(label_width); + ImGui::PushItemWidth(item_width); + + size_t selection_out = selection_idx; + + const char *selected_str = (selection_idx >= 0 && selection_idx < int(lines.size())) ? lines[selection_idx].c_str() : ""; + if (ImGui::BBLBeginCombo(("##" + label).c_str(), selected_str, 0)) { + for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { + ImGui::PushID(int(line_idx)); + if (ImGui::Selectable("", line_idx == selection_idx)) selection_out = line_idx; + + ImGui::SameLine(); + ImGui::Text("%s", lines[line_idx].c_str()); + ImGui::PopID(); + } + + ImGui::EndCombo(); + } + + bool is_changed = selection_idx != selection_out; + selection_idx = selection_out; + + //if (is_changed) update_connector_shape(); + ImGuiWrapper::pop_combo_style(); + + return is_changed; +} + GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : m_parent(parent) , m_group_id(-1) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index 626b7989a..e78da3150 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -149,6 +149,9 @@ protected: bool m_is_dark_mode = false; + bool render_combo(const std::string &label, const std::vector &lines, + int &selection_idx, float label_width, float item_width); + public: GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index c6fc3d445..53e1ab1ca 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -520,40 +520,6 @@ bool GLGizmoCut3D::render_cut_mode_combo() return is_changed; } -bool GLGizmoCut3D::render_combo(const std::string& label, const std::vector& lines, int& selection_idx) -{ - ImGuiWrapper::push_combo_style(m_parent.get_scale()); - ImGui::AlignTextToFramePadding(); - m_imgui->text(label); - ImGui::SameLine(m_label_width); - ImGui::PushItemWidth(m_editing_window_width); - - size_t selection_out = selection_idx; - - const char* selected_str = (selection_idx >= 0 && selection_idx < int(lines.size())) ? lines[selection_idx].c_str() : ""; - if (ImGui::BBLBeginCombo(("##" + label).c_str(), selected_str, 0)) { - for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { - ImGui::PushID(int(line_idx)); - if (ImGui::Selectable("", line_idx == selection_idx)) - selection_out = line_idx; - - ImGui::SameLine(); - ImGui::Text("%s", lines[line_idx].c_str()); - ImGui::PopID(); - } - - ImGui::EndCombo(); - } - - bool is_changed = selection_idx != selection_out; - selection_idx = selection_out; - - //if (is_changed) update_connector_shape(); - ImGuiWrapper::pop_combo_style(); - - return is_changed; -} - bool GLGizmoCut3D::render_double_input(const std::string& label, double& value_in) { ImGui::AlignTextToFramePadding(); @@ -2299,7 +2265,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors, flo m_connector_style = int(CutConnectorStyle::Prism); apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); }); } - if (render_combo(m_labels_map["Style"], m_connector_styles, m_connector_style)) + if (render_combo(m_labels_map["Style"], m_connector_styles, m_connector_style, m_label_width, m_editing_window_width)) apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); }); m_imgui->disabled_end(); @@ -2308,7 +2274,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors, flo m_connector_shape_id = int(CutConnectorShape::Circle); apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); }); } - if (render_combo(m_labels_map["Shape"], m_connector_shapes, m_connector_shape_id)) + if (render_combo(m_labels_map["Shape"], m_connector_shapes, m_connector_shape_id, m_label_width, m_editing_window_width)) apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); }); m_imgui->disabled_end(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index f09ca7a4b..47a6d47f3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -335,7 +335,6 @@ private: void set_center(const Vec3d¢er, bool update_tbb = false); void switch_to_mode(size_t new_mode); bool render_cut_mode_combo(); - bool render_combo(const std::string&label, const std::vector&lines, int&selection_idx); bool render_double_input(const std::string& label, double& value_in); bool render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float min_val = -0.1f, float max_tolerance = -0.1f); void render_move_center_input(int axis); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 2405756bb..406f88e6d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -265,6 +265,10 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) if (m_selected_features.first.feature.has_value()) { reset_feature2_render(); const SelectedFeatures::Item item = detect_current_item(); + if (!is_pick_meet_assembly_mode(item)) { // assembly deal + m_selected_wrong_feature_waring_tip = true; + return true; + } m_selected_wrong_feature_waring_tip = false; if (m_selected_features.first != item) { bool processed = false; @@ -329,6 +333,10 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) // 1st feature selection reset_feature1_render(); const SelectedFeatures::Item item = detect_current_item(); + if (!is_pick_meet_assembly_mode(item)) {//assembly deal + m_selected_wrong_feature_waring_tip = true; + return true; + } m_selected_wrong_feature_waring_tip = false; m_selected_features.first = item; if (requires_sphere_raycaster_for_picking(item)) { @@ -2035,6 +2043,79 @@ void GLGizmoMeasure::show_distance_xyz_ui() //{ //} +void GLGizmoMeasure::show_face_face_assembly_common() { + if (m_measure_mode == EMeasureMode::ONLY_ASSEMBLY && m_hit_different_volumes.size() == 2 && + m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { + auto &action = m_assembly_action; + auto set_to_parallel_size = m_imgui->calc_button_size(_L("Parallel")).x; + auto set_to_center_coincidence_size = m_imgui->calc_button_size(_L("Center coincidence")).x; + + m_imgui->disabled_begin(!(action.can_set_to_center_coincidence)); + { + ImGui::PushItemWidth(set_to_center_coincidence_size); + ImGui::PushStyleColor(ImGuiCol_Button, m_is_dark_mode ? ImVec4(0 / 255.0, 174 / 255.0, 66 / 255.0, 1.0) : ImVec4(0 / 255.0, 174 / 255.0, 66 / 255.0, 1.0)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, + m_is_dark_mode ? ImVec4(50 / 255.0f, 238 / 255.0f, 61 / 255.0f, 1.00f) : ImVec4(50 / 255.0f, 238 / 255.0f, 61 / 255.0f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, + m_is_dark_mode ? ImVec4(206 / 255.0f, 206 / 255.0f, 206 / 255.0f, 1.00f) : ImVec4(206 / 255.0f, 206 / 255.0f, 206 / 255.0f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_Text, + m_is_dark_mode ? ImVec4(255 / 255.0f, 255 / 255.0f, 255 / 255.0f, 1.00f) : ImVec4(255 / 255.0f, 255 / 255.0f, 255 / 255.0f, 1.00f)); + if (m_imgui->button(_L("Center coincidence"))) { + set_to_center_coincidence(m_same_model_object); + } + ImGui::PopStyleColor(4); + ImGui::SameLine(set_to_center_coincidence_size + m_space_size * 2); + } + m_imgui->disabled_end(); + + m_imgui->disabled_begin(!action.can_set_to_parallel); + { + if (m_imgui->button(_L("Parallel"))) { set_to_parallel(m_same_model_object); } + } + m_imgui->disabled_end(); + } +} + +void GLGizmoMeasure::show_face_face_assembly_senior() +{ + if (m_measure_mode == EMeasureMode::ONLY_ASSEMBLY && m_hit_different_volumes.size() == 2 && + m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { + auto &action = m_assembly_action; + auto feature_text_size = m_imgui->calc_button_size(_L("Featue 1")).x + m_imgui->calc_button_size(":").x; + auto set_to_reverse_rotation_size = m_imgui->calc_button_size(_L("Reverse rotation")).x; + auto rotate_around_center_size = m_imgui->calc_button_size(_L("Rotate around center:")).x; + auto parallel_distance_size = m_imgui->calc_button_size(_L("Parallel_distance:")).x; + + if (m_imgui->bbl_checkbox(_L("Flip by Face 2"), m_flip_volume_2)) { + set_to_reverse_rotation(m_same_model_object, 1); + } + + if (action.has_parallel_distance) { + m_imgui->text(_u8L("Parallel_distance:")); + ImGui::SameLine(parallel_distance_size + m_space_size); + ImGui::PushItemWidth(m_input_size_max); + ImGui::BBLInputDouble("##parallel_distance_z", &m_buffered_parallel_distance, 0.0f, 0.0f, "%.2f"); + if (m_last_active_item_imgui != m_current_active_imgui_id && std::abs(m_buffered_parallel_distance - action.parallel_distance) > EPSILON) { + set_parallel_distance(m_same_model_object, m_buffered_parallel_distance); + } + } + if (action.can_around_center_of_faces) { + m_imgui->text(_u8L("Rotate around center:")); + ImGui::SameLine(rotate_around_center_size + m_space_size); + ImGui::PushItemWidth(m_input_size_max); + ImGui::BBLInputDouble("##rotate_around_center", &m_buffered_around_center, 0.0f, 0.0f, "%.2f"); + if (m_last_active_item_imgui != m_current_active_imgui_id && std::abs(m_buffered_around_center) > EPSILON) { + set_to_around_center_of_faces(m_same_model_object, m_buffered_around_center); + m_buffered_around_center = 0; + } + ImGui::SameLine(rotate_around_center_size + m_space_size + m_input_size_max + m_space_size / 2.0f); + m_imgui->text(_L("°")); + } + } +} + void GLGizmoMeasure::init_render_input_window() { m_use_inches = wxGetApp().app_config->get_bool("use_inches"); @@ -2101,6 +2182,10 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGuiWrapper::pop_toolbar_style(); } +void GLGizmoMeasure::render_input_window_warning(bool same_model_object) +{ +} + void GLGizmoMeasure::remove_selected_sphere_raycaster(int id) { reset_gripper_pick(id == SEL_SPHERE_1_ID ? GripperType::SPHERE_1 : GripperType::SPHERE_2); @@ -2110,9 +2195,11 @@ void GLGizmoMeasure::update_measurement_result() { if (!m_selected_features.first.feature.has_value()) { m_measurement_result = Measure::MeasurementResult(); + m_assembly_action = Measure::AssemblyAction(); } else if (m_selected_features.second.feature.has_value()) { m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature, true); + m_assembly_action = Measure::get_assembly_action(*m_selected_features.first.feature, *m_selected_features.second.feature); m_can_set_xyz_distance = Measure::can_set_xyz_distance(*m_selected_features.first.feature, *m_selected_features.second.feature); //update buffer const Measure::MeasurementResult &measure = m_measurement_result; @@ -2126,6 +2213,9 @@ void GLGizmoMeasure::update_measurement_result() } if (wxGetApp().app_config->get_bool("use_inches")) m_distance = GizmoObjectManipulation::mm_to_in * m_distance; m_buffered_distance = m_distance; + if (m_assembly_action.has_parallel_distance) { + m_buffered_parallel_distance = m_assembly_action.parallel_distance; + } } else if (!m_selected_features.second.feature.has_value() && m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle) m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, Measure::SurfaceFeature(std::get<0>(m_selected_features.first.feature->get_circle()))); @@ -2399,5 +2489,201 @@ void GLGizmoMeasure::set_distance(bool same_model_object, const Vec3d &displacem } } +void GLGizmoMeasure::set_to_parallel(bool same_model_object, bool take_shot, bool is_anti_parallel) +{ + if (m_hit_different_volumes.size() == 2) { + auto &action = m_assembly_action; + auto v = m_hit_different_volumes[1]; + auto selection = const_cast(&m_parent.get_selection()); + selection->setup_cache(); + if (take_shot) { + wxGetApp().plater()->take_snapshot("RotateInMeasure", UndoRedo::SnapshotType::GizmoAction); // avoid storing another snapshot + } + selection->set_mode(same_model_object ? Selection::Volume : Selection::Instance); + const auto [idx1, normal1, pt1] = m_selected_features.first.feature->get_plane(); + const auto [idx2, normal2, pt2] = m_selected_features.second.feature->get_plane(); + if ((is_anti_parallel && normal1.dot(normal2) > -1 + 1e-3) || + (is_anti_parallel == false && (normal1.dot(normal2) < 1 - 1e-3))) { + m_pending_scale ++; + Vec3d axis; + double angle; + Matrix3d rotation_matrix; + Geometry::rotation_from_two_vectors(normal2, -normal1, axis, angle, &rotation_matrix); + Transform3d r_m = (Transform3d) rotation_matrix; + if (same_model_object == false) { + auto new_rotation_tran = r_m * v->get_instance_transformation().get_rotation_matrix(); + Vec3d rotation = Geometry::extract_euler_angles(new_rotation_tran); + v->set_instance_rotation(rotation); + selection->rotate(v->object_idx(), v->instance_idx(), v->get_instance_transformation().get_matrix()); + } else { + Geometry::Transformation world_tran(v->world_matrix()); + auto new_tran = r_m * world_tran.get_rotation_matrix(); + Transform3d volume_rotation_tran = v->get_instance_transformation().get_rotation_matrix().inverse() * new_tran; + Vec3d rotation = Geometry::extract_euler_angles(volume_rotation_tran); + v->set_volume_rotation(rotation); + selection->rotate(v->object_idx(), v->instance_idx(), v->volume_idx(), v->get_volume_transformation().get_matrix()); + } + wxGetApp().plater()->canvas3D()->do_rotate(""); + register_single_mesh_pick(); + if (same_model_object) { + update_feature_by_tran(*m_selected_features.first.feature); + } + update_feature_by_tran(*m_selected_features.second.feature); + } + } +} + +void GLGizmoMeasure::set_to_reverse_rotation(bool same_model_object, int feature_index) +{ + if (m_hit_different_volumes.size() == 2 && feature_index < 2) { + auto &action = m_assembly_action; + auto v = m_hit_different_volumes[feature_index]; + auto selection = const_cast(&m_parent.get_selection()); + selection->setup_cache(); + wxGetApp().plater()->take_snapshot("ReverseRotateInMeasure", UndoRedo::SnapshotType::GizmoAction); // avoid storing another snapshot + + selection->set_mode(same_model_object ? Selection::Volume : Selection::Instance); + m_pending_scale = 1; + Vec3d plane_normal,plane_center; + if (feature_index ==0) {//feature 1 + const auto [idx1, normal1, pt1] = m_selected_features.first.feature->get_plane(); + plane_normal = normal1; + plane_center = pt1; + } + else { // feature 2 + const auto [idx2, normal2, pt2] = m_selected_features.second.feature->get_plane(); + plane_normal = normal2; + plane_center = pt2; + } + auto new_pt = Measure::get_one_point_in_plane(plane_center, plane_normal); + Vec3d axis = (new_pt - plane_center).normalized(); + if (axis.norm() < 0.1) { + throw; + } + if (same_model_object == false) { + Geometry::Transformation inMat(v->get_instance_transformation()); + Geometry::Transformation outMat = Geometry::mat_around_a_point_rotate(inMat, plane_center, axis, PI); + selection->rotate(v->object_idx(), v->instance_idx(), outMat.get_matrix()); + } else { + Geometry::Transformation inMat(v->world_matrix()); + Geometry::Transformation outMat = Geometry::mat_around_a_point_rotate(inMat, plane_center, axis, PI); + Transform3d volume_tran = v->get_instance_transformation().get_matrix().inverse() * outMat.get_matrix(); + selection->rotate(v->object_idx(), v->instance_idx(), v->volume_idx(), volume_tran); + } + wxGetApp().plater()->canvas3D()->do_rotate(""); + register_single_mesh_pick(); + if (same_model_object == false) { + if (feature_index == 0) { // feature 1 + update_feature_by_tran(*m_selected_features.first.feature); + } else { // feature 2 + update_feature_by_tran(*m_selected_features.second.feature); + } + } + else { + update_feature_by_tran(*m_selected_features.first.feature); + update_feature_by_tran(*m_selected_features.second.feature); + } + } +} + +void GLGizmoMeasure::set_to_around_center_of_faces(bool same_model_object, float rotate_degree) +{ + if (m_hit_different_volumes.size() == 2 ) { + auto &action = m_assembly_action; + auto v = m_hit_different_volumes[1]; + auto selection = const_cast(&m_parent.get_selection()); + selection->setup_cache(); + wxGetApp().plater()->take_snapshot("ReverseRotateInMeasure", UndoRedo::SnapshotType::GizmoAction); // avoid storing another snapshot + + selection->set_mode(same_model_object ? Selection::Volume : Selection::Instance); + m_pending_scale = 1; + + auto radian = Geometry::deg2rad(rotate_degree); + Vec3d plane_normal, plane_center; + const auto [idx2, normal2, pt2] = m_selected_features.second.feature->get_plane(); + plane_normal = normal2; + plane_center = pt2; + + if (same_model_object == false) { + Geometry::Transformation inMat(v->get_instance_transformation()); + Geometry::Transformation outMat = Geometry::mat_around_a_point_rotate(inMat, plane_center, plane_normal, radian); + selection->rotate(v->object_idx(), v->instance_idx(), outMat.get_matrix()); + } else { + Geometry::Transformation inMat(v->world_matrix()); + Geometry::Transformation outMat = Geometry::mat_around_a_point_rotate(inMat, plane_center, plane_normal, radian); + Transform3d volume_tran = v->get_instance_transformation().get_matrix().inverse() * outMat.get_matrix(); + selection->rotate(v->object_idx(), v->instance_idx(), v->volume_idx(), volume_tran); + } + wxGetApp().plater()->canvas3D()->do_rotate(""); + register_single_mesh_pick(); + if (same_model_object) { + update_feature_by_tran(*m_selected_features.first.feature); + } + update_feature_by_tran(*m_selected_features.second.feature); + } +} + +void GLGizmoMeasure::set_to_center_coincidence(bool same_model_object) { + auto v = m_hit_different_volumes[1]; + wxGetApp().plater()->take_snapshot("RotateThenMoveInMeasure", UndoRedo::SnapshotType::GizmoAction); + set_to_parallel(same_model_object, false,true); + + const auto [idx1, normal1, pt1] = m_selected_features.first.feature->get_plane(); + const auto [idx2, normal2, pt2] = m_selected_features.second.feature->get_plane(); + set_distance(same_model_object, pt1 - pt2, false); + m_set_center_coincidence = true; +} + +void GLGizmoMeasure::set_parallel_distance(bool same_model_object, float dist) +{ + if (m_hit_different_volumes.size() == 2 && abs(dist) >= 0.0f) { + auto v = m_hit_different_volumes[1]; + auto selection = const_cast(&m_parent.get_selection()); + selection->setup_cache(); + + wxGetApp().plater()->take_snapshot("SetParallelDistanceInMeasure", UndoRedo::SnapshotType::GizmoAction); // avoid storing another snapshot + + selection->set_mode(same_model_object ? Selection::Volume : Selection::Instance); + m_pending_scale = 1; + + const auto [idx1, normal1, pt1] = m_selected_features.first.feature->get_plane(); + const auto [idx2, normal2, pt2] = m_selected_features.second.feature->get_plane(); + Vec3d proj_pt2; + Measure::get_point_projection_to_plane(pt2, pt1, normal1, proj_pt2); + auto new_pt2 = proj_pt2 + normal1 * dist; + + Vec3d displacement = new_pt2 - pt2; + + if (same_model_object == false) { + selection->translate(v->object_idx(), v->instance_idx(), displacement); + } else { + selection->translate(v->object_idx(), v->instance_idx(), v->volume_idx(), displacement); + } + wxGetApp().plater()->canvas3D()->do_move(""); + register_single_mesh_pick(); + if (same_model_object) { + update_feature_by_tran(*m_selected_features.first.feature); + } + update_feature_by_tran(*m_selected_features.second.feature); + } +} + +bool GLGizmoMeasure::is_pick_meet_assembly_mode(const SelectedFeatures::Item &item) { + if (m_measure_mode == EMeasureMode::ONLY_ASSEMBLY) { + if (m_assembly_mode == AssemblyMode::FACE_FACE && item.feature->get_type() == Measure::SurfaceFeatureType::Plane) { + return true; + } + if (m_assembly_mode == AssemblyMode::POINT_POINT && + (item.feature->get_type() == Measure::SurfaceFeatureType::Point|| + item.feature->get_type() == Measure::SurfaceFeatureType::Circle)) { + return true; + } + return false; + } + else { + return true; + } +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 2d9eb93de..7fd54603f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -155,6 +155,7 @@ protected: EMode m_mode{ EMode::FeatureSelection }; Measure::MeasurementResult m_measurement_result; + Measure::AssemblyAction m_assembly_action; std::map> m_mesh_measure_map; std::shared_ptr m_curr_measuring{nullptr}; @@ -198,6 +199,8 @@ protected: unsigned int m_last_active_item_imgui{0}; Vec3d m_buffered_distance; Vec3d m_distance; + double m_buffered_parallel_distance{0}; + double m_buffered_around_center{0}; // used to keep the raycasters for point/center spheres //std::vector> m_selected_sphere_raycasters; std::optional m_curr_feature; @@ -259,9 +262,12 @@ protected: void show_selection_ui(); void show_distance_xyz_ui(); //void show_point_point_assembly(); + void show_face_face_assembly_common(); + void show_face_face_assembly_senior(); void init_render_input_window(); virtual void on_render_input_window(float x, float y, float bottom_limit) override; + virtual void render_input_window_warning(bool same_model_object); void remove_selected_sphere_raycaster(int id); void update_measurement_result(); @@ -289,6 +295,13 @@ protected: void update_world_plane_features(Measure::Measuring *cur_measuring, Measure::SurfaceFeature &feautre); void update_feature_by_tran(Measure::SurfaceFeature & feature); void set_distance(bool same_model_object, const Vec3d &displacement, bool take_shot = true); + void set_to_parallel(bool same_model_object, bool take_shot = true, bool is_anti_parallel = false); + void set_to_reverse_rotation(bool same_model_object,int feature_index); + void set_to_around_center_of_faces(bool same_model_object,float rotate_degree); + void set_to_center_coincidence(bool same_model_object); + void set_parallel_distance(bool same_model_object,float dist); + + bool is_pick_meet_assembly_mode(const SelectedFeatures::Item& item); protected: // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index cb3d952ca..e379ea2f9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -23,8 +23,8 @@ #include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp" #include "slic3r/GUI/Gizmos/GLGizmoEmboss.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSVG.hpp" -#include "slic3r/GUI/Gizmos/GLGizmoMeasure.hpp" #include "slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoAssembly.hpp" #include "libslic3r/format.hpp" #include "libslic3r/Model.hpp" @@ -62,6 +62,7 @@ std::vector GLGizmosManager::get_selectable_idxs() const if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) { for (size_t i = 0; i < m_gizmos.size(); ++i) if (m_gizmos[i]->get_sprite_id() == (unsigned int) Measure || + m_gizmos[i]->get_sprite_id() == (unsigned int) Assembly || m_gizmos[i]->get_sprite_id() == (unsigned int) MmuSegmentation) out.push_back(i); } @@ -162,6 +163,9 @@ void GLGizmosManager::switch_gizmos_icon_filename() case (EType::Measure): gizmo->set_icon_filename(m_is_dark ? "toolbar_measure_dark.svg" : "toolbar_measure.svg"); break; + case (EType::Assembly): + gizmo->set_icon_filename(m_is_dark ? "toolbar_assembly_dark.svg" : "toolbar_assembly.svg"); + break; } } @@ -200,6 +204,7 @@ bool GLGizmosManager::init() m_gizmos.emplace_back(new GLGizmoEmboss(m_parent, m_is_dark ? "toolbar_text_dark.svg" : "toolbar_text.svg", EType::Emboss)); m_gizmos.emplace_back(new GLGizmoSVG(m_parent)); m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, m_is_dark ? "toolbar_measure_dark.svg" : "toolbar_measure.svg", EType::Measure)); + m_gizmos.emplace_back(new GLGizmoAssembly(m_parent, m_is_dark ? "toolbar_assembly_dark.svg" : "toolbar_assembly.svg", EType::Assembly)); m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "reduce_triangles.svg", EType::Simplify)); //m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", sprite_id++)); //m_gizmos.emplace_back(new GLGizmoFaceDetector(m_parent, "face recognition.svg", sprite_id++)); @@ -343,8 +348,8 @@ bool GLGizmosManager::check_gizmos_closed_except(EType type) const void GLGizmosManager::set_hover_id(int id) { - // Measure handles hover by itself - if (m_current == EType::Measure) { return; } + // Measure and assembly handles hover by itself + if (m_current == EType::Measure || m_current == EType::Assembly) { return; } if (!m_enabled || m_current == Undefined) return; @@ -438,7 +443,9 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p else if (m_current == MmuSegmentation) return dynamic_cast(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == Measure) - return dynamic_cast(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + return dynamic_cast(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + else if (m_current == Assembly) + return dynamic_cast(m_gizmos[Assembly].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == Cut) return dynamic_cast(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == MeshBoolean) @@ -701,7 +708,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) case WXK_ESCAPE: { if (m_current != Undefined) { - if (m_current == Measure && gizmo_event(SLAGizmoEventType::Escape)) { + if ((m_current == Measure || m_current == Assembly) && gizmo_event(SLAGizmoEventType::Escape)) { // do nothing } else //if ((m_current != SlaSupports) || !gizmo_event(SLAGizmoEventType::DiscardChanges)) @@ -740,7 +747,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) case WXK_BACK: case WXK_DELETE: { - if ((m_current == Cut || m_current == Measure) && gizmo_event(SLAGizmoEventType::Delete)) + if ((m_current == Cut || m_current == Measure || m_current == Assembly) && gizmo_event(SLAGizmoEventType::Delete)) processed = true; break; } @@ -838,7 +845,7 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) processed = true; } }*/ - if (m_current == Measure) { + if (m_current == Measure || m_current == Assembly) { if (keyCode == WXK_CONTROL) gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown()); else if (keyCode == WXK_SHIFT) @@ -925,8 +932,7 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) // force extra frame to automatically update window size wxGetApp().imgui()->set_requires_extra_frame(); } - } - else if (m_current == Measure) { + } else if (m_current == Measure || m_current == Assembly) { if (keyCode == WXK_CONTROL) gizmo_event(SLAGizmoEventType::CtrlDown, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown()); else if (keyCode == WXK_SHIFT) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 4511f0211..305c767f4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -86,6 +86,7 @@ public: Emboss, Svg, Measure, + Assembly, Simplify, //SlaSupports, // BBS diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 187ec03eb..79feb0e1f 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1720,6 +1720,66 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co this->set_bounding_boxes_dirty(); } +void Selection::translate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Vec3d &displacement) { + if (!m_valid) return; + + for (unsigned int i : m_list) { + GLVolume &v = *(*m_volumes)[i]; + if (v.object_idx() == (int) object_idx && v.instance_idx() == (int) instance_idx && v.volume_idx() == (int) volume_idx) + v.set_volume_offset(v.get_volume_offset() + displacement); + } + + this->set_bounding_boxes_dirty(); +} + +void Selection::rotate(unsigned int object_idx, unsigned int instance_idx, const Transform3d &overwrite_tran) +{ + if (!m_valid) return; + + for (unsigned int i : m_list) { + GLVolume &v = *(*m_volumes)[i]; + if (v.object_idx() == (int) object_idx && v.instance_idx() == (int) instance_idx) { + v.set_instance_transformation(overwrite_tran); + } + } + + 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() != (int) instance_idx) + continue; + + v.set_instance_transformation(overwrite_tran); + done.insert(j); + } + } + this->set_bounding_boxes_dirty(); +} +void Selection::rotate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Transform3d &overwrite_tran) +{ + if (!m_valid) return; + + for (unsigned int i : m_list) { + GLVolume &v = *(*m_volumes)[i]; + if (v.object_idx() == (int) object_idx && v.instance_idx() == (int) instance_idx && v.volume_idx() == (int) volume_idx) { + v.set_volume_transformation(overwrite_tran); + } + } + this->set_bounding_boxes_dirty(); +} + //BBS: add partplate related logic void Selection::notify_instance_update(int object_idx, int instance_idx) { diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 8fc0f8bc6..0e95b4100 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -344,6 +344,10 @@ public: void translate(unsigned int object_idx, const Vec3d& displacement); void translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement); + void translate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Vec3d &displacement); + + void rotate(unsigned int object_idx, unsigned int instance_idx, const Transform3d &overwrite_tran); + void rotate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Transform3d &overwrite_tran); //BBS: add partplate related logic void notify_instance_update(int object_idx, int instance_idx); // BBS