diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index c18cd3d49..47915817a 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -804,5 +804,33 @@ Geometry::TransformationSVD::TransformationSVD(const Transform3d &trafo) } 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_from_transform(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_from_transform(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 Geometry } // namespace Slic3r diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index f28eba200..12e614276 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -503,6 +503,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/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 2bb775b64..2be0532da 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -17,6 +17,14 @@ namespace Slic3r { namespace Measure { +bool get_point_projection_to_plane(const Vec3d &pt, const Vec3d &plane_origin, const Vec3d &plane_normal, Vec3d &intersection_pt) +{ + auto normal = plane_normal.normalized(); + auto BA = plane_origin - pt; + auto length = BA.dot(normal); + intersection_pt = pt + length * normal; + return true; +} constexpr double feature_hover_limit = 0.5; // how close to a feature the mouse must be to highlight it @@ -1273,6 +1281,42 @@ MeasurementResult get_measurement(const SurfaceFeature &a, const SurfaceFeature return result; } +AssemblyAction get_assembly_action(const SurfaceFeature& a, const SurfaceFeature& b) +{ + AssemblyAction action; + const SurfaceFeature &f1 = a; + const SurfaceFeature &f2 = b; + if (f1.get_type() == SurfaceFeatureType::Plane) { + action.can_set_feature_1_reverse_rotation = true; + if (f2.get_type() == SurfaceFeatureType::Plane) { + const auto [idx1, normal1, pt1] = f1.get_plane(); + const auto [idx2, normal2, pt2] = f2.get_plane(); + action.can_set_to_center_coincidence = true; + action.can_set_feature_2_reverse_rotation = true; + if (are_parallel(normal1, normal2)) { + action.can_set_to_parallel = false; + action.has_parallel_distance = true; + action.can_around_center_of_faces = true; + Vec3d proj_pt2; + Measure::get_point_projection_to_plane(pt2, pt1, normal1, proj_pt2); + action.parallel_distance = (pt2 - proj_pt2).norm(); + if ((pt2 - proj_pt2).dot(normal1) < 0) { + action.parallel_distance = -action.parallel_distance; + } + action.angle_radian = 0; + + } else { + action.can_set_to_parallel = true; + action.has_parallel_distance = false; + action.can_around_center_of_faces = false; + action.parallel_distance = 0; + action.angle_radian = std::acos(std::clamp(normal2.dot(-normal1), -1.0, 1.0)); + } + } + } + return action; +} + void SurfaceFeature::translate(const Vec3d& displacement) { switch (get_type()) { case Measure::SurfaceFeatureType::Point: { @@ -1334,14 +1378,8 @@ void SurfaceFeature::translate(const Transform3d &tran) m_pt1 = tran * m_pt1; auto world_center = m_pt1; m_pt2 = (temp_pt2 - m_pt1).normalized(); - auto get_point_projection_to_plane = [](const Vec3d& pt, const Vec3d& plane_origin, const Vec3d& plane_normal,Vec3d& intersection_pt )->bool { - auto normal = plane_normal.normalized(); - auto BA=plane_origin-pt; - auto length = BA.dot(normal); - intersection_pt = pt + length * normal; - return true; - }; - auto calc_world_radius = [&local_center, &local_normal, &tran, &world_center, &get_point_projection_to_plane](const Vec3d &pt, double &value) { + + auto calc_world_radius = [&local_center, &local_normal, &tran, &world_center](const Vec3d &pt, double &value) { Vec3d intersection_pt; get_point_projection_to_plane(pt, local_center, local_normal, intersection_pt); auto local_radius_pt = (intersection_pt - local_center).normalized() * value + local_center; diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index e749f4dd5..80c946364 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -25,7 +25,10 @@ enum class SurfaceFeatureType : int { Plane = 1 << 3 }; -class SurfaceFeature { +bool get_point_projection_to_plane(const Vec3d &pt, const Vec3d &plane_origin, const Vec3d &plane_normal, Vec3d &intersection_pt); + +class SurfaceFeature +{ public: SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional pt3 = std::nullopt, double value = 0.0) : m_type(type), m_pt1(pt1), m_pt2(pt2), m_pt3(pt3), m_value(value) {} @@ -182,6 +185,22 @@ struct MeasurementResult { // Returns distance/angle between two SurfaceFeatures. MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b,bool deal_circle_result =false); +struct AssemblyAction +{ + bool can_set_to_parallel{false}; + bool can_set_to_center_coincidence{false}; + bool can_set_feature_1_reverse_rotation{false}; + bool can_set_feature_2_reverse_rotation{false}; + bool can_around_center_of_faces{false}; + bool has_parallel_distance{false}; + float parallel_distance; + float angle_radian{0}; + Transform3d tran_for_parallel; + Transform3d tran_for_center_coincidence; + Transform3d tran_for_reverse_rotation; +}; +AssemblyAction get_assembly_action(const SurfaceFeature &a, const SurfaceFeature &b); + inline Vec3d edge_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); } inline Vec3d edge_direction(const std::pair& e) { return edge_direction(e.first, e.second); } inline Vec3d edge_direction(const SurfaceFeature& edge) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 22b13ef05..5b37db21b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -445,20 +445,6 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) update_measurement_result(); m_imgui->set_requires_extra_frame(); - - const Measure::MeasurementResult &measure = m_measurement_result; - m_distance = Vec3d::Zero(); - if (measure.distance_xyz.has_value() && measure.distance_xyz->norm() > EPSILON) { - m_distance = *measure.distance_xyz; - } - else if (measure.distance_infinite.has_value()) { - m_distance = measure.distance_infinite->to - measure.distance_infinite->from; - } else if (measure.distance_strict.has_value()) { - m_distance = measure.distance_strict->to - measure.distance_strict->from; - } - if (wxGetApp().app_config->get("use_inches") == "1") - m_distance = GizmoObjectManipulation::mm_to_in * m_distance; - m_buffered_distance = m_distance; return true; } else @@ -497,11 +483,11 @@ void GLGizmoMeasure::data_changed(bool is_serializing) { wxBusyCursor wait; - if (m_pending_scale) { + if (m_pending_scale > 0) { update_if_needed(); register_single_mesh_pick(); update_measurement_result(); - m_pending_scale = false; + m_pending_scale --; } else { m_parent.toggle_selected_volume_visibility(true); @@ -1369,7 +1355,7 @@ void GLGizmoMeasure::render_dimensioning() if (m_selected_features.second.feature.has_value()) update_feature_by_tran(*m_selected_features.second.feature); // update measure on next call to data_changed() - m_pending_scale = true; + m_pending_scale = 1; }; auto action_exit = [this]() { @@ -2010,10 +1996,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } m_imgui->disabled_end(); if (m_last_active_item_imgui != current_active_id && m_hit_different_volumes.size() == 2) { - auto selection = const_cast(&m_parent.get_selection()); - selection->setup_cache(); Vec3d displacement = Vec3d::Zero(); - auto v = m_hit_different_volumes[1]; if (std::abs(m_buffered_distance[0] - distance[0]) > EPSILON) { displacement[0] = m_buffered_distance[0] - distance[0]; distance[0] = m_buffered_distance[0]; @@ -2025,18 +2008,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit distance[2] = m_buffered_distance[2]; } if (displacement.norm() > 0.0f) { - wxGetApp().plater()->take_snapshot(_u8L("MoveInMeasure"), UndoRedo::SnapshotType::GizmoAction); // avoid storing another snapshot - selection->set_mode(same_model_object ? Selection::Volume : Selection::Instance); - auto llo = selection->get_mode(); - 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(""); - update_single_mesh_pick(v); - update_feature_by_tran(*m_selected_features.second.feature); - m_pending_scale = true; + set_distance(same_model_object, displacement); } } }; @@ -2090,6 +2062,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::PopID(); } } + if (m_distance.norm() >0.01) { add_edit_distance_xyz_box(m_distance); } @@ -2099,7 +2072,84 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit add_strings_row_to_table(*m_imgui, " ", ImGuiWrapper::COL_BAMBU, " ", ImGui::GetStyleColorVec4(ImGuiCol_Text)); }*/ ImGui::EndTable(); + if (m_hit_different_volumes.size() == 2) { + ImGui::Separator(); + 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; + 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; + //set_feature_1 + if (action.can_set_feature_1_reverse_rotation) { + m_imgui->text(_L("Featue 1") + ":"); + { + ImGui::SameLine(feature_text_size + space_size); + ImGui::PushItemWidth(set_to_reverse_rotation_size); + if (m_imgui->button(_L("Reverse rotation"))) { + set_to_reverse_rotation(same_model_object, 0); + } + //ImGui::SameLine(set_to_reverse1_rotation_size + 2 * space_size); + } + ImGui::Separator(); + } + + m_imgui->text(_L("Featue 2") + ":"); + ImGui::SameLine(feature_text_size + space_size); + m_imgui->disabled_begin(!action.can_set_feature_2_reverse_rotation); + { + ImGui::PushItemWidth(set_to_reverse_rotation_size); + ImGui::PushID("Featue2"); + if (m_imgui->button(_L("Reverse rotation"))) { + set_to_reverse_rotation(same_model_object, 1); + } + ImGui::PopID(); + } + m_imgui->disabled_end(); + + m_imgui->disabled_begin(!action.can_set_to_parallel); + { + if (m_imgui->button(_L("Parallel"))) { + set_to_parallel(same_model_object); + } + ImGui::SameLine(set_to_parallel_size + space_size *2); + } + m_imgui->disabled_end(); + + m_imgui->disabled_begin(!(action.can_set_to_center_coincidence)); + { + ImGui::PushItemWidth(set_to_center_coincidence_size); + if (m_imgui->button(_L("Center coincidence"))) { + set_to_center_coincidence(same_model_object); + } + } + m_imgui->disabled_end(); + + if (action.has_parallel_distance) { + m_imgui->text(_u8L("Parallel_distance:")); + ImGui::SameLine(parallel_distance_size + space_size); + ImGui::PushItemWidth(input_size_max); + ImGui::BBLInputDouble("##parallel_distance_z", &m_buffered_parallel_distance, 0.0f, 0.0f, "%.2f"); + if (m_last_active_item_imgui != current_active_id && std::abs(m_buffered_parallel_distance - action.parallel_distance) > EPSILON) { + set_parallel_distance(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 + space_size); + ImGui::PushItemWidth(input_size_max); + ImGui::BBLInputDouble("##rotate_around_center", &m_buffered_around_center, 0.0f, 0.0f, "%.2f"); + if (m_last_active_item_imgui != current_active_id && std::abs(m_buffered_around_center) > EPSILON) { + set_to_around_center_of_faces(same_model_object, m_buffered_around_center); + m_buffered_around_center = 0; + } + ImGui::SameLine(rotate_around_center_size + space_size + input_size_max+ space_size / 2.0f); + m_imgui->text(_L("°")); + } + } } + render_input_window_warning(same_model_object); ImGui::Separator(); @@ -2126,6 +2176,17 @@ 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) +{ + if (wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasView3D) { + if (m_hit_different_volumes.size() == 2) { + if (same_model_object == false) { + m_imgui->text(_L("Warning") + ": " + _L("Due to ensuer_on_bed, assembly between \ndifferent objects may not be correct in 3D view.\n It is recommended to assemble them together.")); + } + } + } +} + void GLGizmoMeasure::remove_selected_sphere_raycaster(int id) { reset_gripper_pick(id == SEL_SPHERE_1_ID ? GripperType::SPHERE_1 : GripperType::SPHERE_2); @@ -2133,10 +2194,29 @@ void GLGizmoMeasure::remove_selected_sphere_raycaster(int id) void GLGizmoMeasure::update_measurement_result() { - if (!m_selected_features.first.feature.has_value()) + if (!m_selected_features.first.feature.has_value()) { m_measurement_result = Measure::MeasurementResult(); - 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::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); + //update buffer + const Measure::MeasurementResult &measure = m_measurement_result; + m_distance = Vec3d::Zero(); + if (measure.distance_xyz.has_value() && measure.distance_xyz->norm() > EPSILON) { + m_distance = *measure.distance_xyz; + } else if (measure.distance_infinite.has_value()) { + m_distance = measure.distance_infinite->to - measure.distance_infinite->from; + } else if (measure.distance_strict.has_value()) { + m_distance = measure.distance_strict->to - measure.distance_strict->from; + } + if (wxGetApp().app_config->get("use_inches") == "1") 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()))); } @@ -2366,5 +2446,206 @@ void GLGizmoMeasure::update_feature_by_tran(Measure::SurfaceFeature &feature) } } +void GLGizmoMeasure::set_distance(bool same_model_object, const Vec3d &displacement, bool take_shot) +{ + if (m_hit_different_volumes.size() == 2 && displacement.norm() > 0.0f) { + 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("MoveInMeasure", UndoRedo::SnapshotType::GizmoAction); // avoid storing another snapshot + } + selection->set_mode(same_model_object ? Selection::Volume : Selection::Instance); + m_pending_scale ++; + if (same_model_object == false) { + selection->translate(v->object_idx(), v->instance_idx(), displacement); + wxGetApp().plater()->canvas3D()->do_move(""); + register_single_mesh_pick(); + } else { + selection->translate(v->object_idx(), v->instance_idx(), v->volume_idx(), displacement); + wxGetApp().plater()->canvas3D()->do_move(""); + update_single_mesh_pick(v); + } + update_feature_by_tran(*m_selected_features.second.feature); + } +} + +void GLGizmoMeasure::set_to_parallel(bool same_model_object, bool take_shot) +{ + 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 (abs(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()); + wxGetApp().plater()->canvas3D()->do_rotate(""); + register_single_mesh_pick(); + } 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(""); + update_single_mesh_pick(v); + } + 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; + } + + Vec3d dir(1,0,0); + float eps = 1e-2; + if ((plane_normal - dir).norm() < 1e-2) { + dir = Vec3d(0, 1, 0); + } + Vec3d new_pt = plane_center + dir; + Vec3d intersection_pt; + Measure::get_point_projection_to_plane(new_pt, plane_center, plane_normal, intersection_pt); + Vec3d axis = (intersection_pt - plane_center).normalized(); + 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()); + wxGetApp().plater()->canvas3D()->do_rotate(""); + register_single_mesh_pick(); + } 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(""); + update_single_mesh_pick(v); + } + 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); + } + } +} + +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()); + wxGetApp().plater()->canvas3D()->do_rotate(""); + register_single_mesh_pick(); + } 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(""); + update_single_mesh_pick(v); + } + 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); + + 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); + wxGetApp().plater()->canvas3D()->do_move(""); + register_single_mesh_pick(); + } else { + selection->translate(v->object_idx(), v->instance_idx(), v->volume_idx(), displacement); + wxGetApp().plater()->canvas3D()->do_move(""); + update_single_mesh_pick(v); + } + update_feature_by_tran(*m_selected_features.second.feature); + } +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 2a991f00e..123484f06 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -82,7 +82,7 @@ class GLGizmoMeasure : public GLGizmoBase 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}; @@ -127,6 +127,8 @@ class GLGizmoMeasure : public GLGizmoBase 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; @@ -143,7 +145,8 @@ class GLGizmoMeasure : public GLGizmoBase KeyAutoRepeatFilter m_shift_kar_filter; SelectedFeatures m_selected_features; - bool m_pending_scale{ false }; + int m_pending_scale{ 0 }; + bool m_set_center_coincidence{false}; bool m_editing_distance{ false }; bool m_is_editing_distance_first_frame{ true }; @@ -186,6 +189,7 @@ protected: virtual void on_render_for_picking() override; virtual void on_render_input_window(float x, float y, float bottom_limit) override; + void render_input_window_warning(bool same_model_object); void remove_selected_sphere_raycaster(int id); void update_measurement_result(); @@ -202,6 +206,12 @@ protected: Measure::Measuring* get_measuring_of_mesh(GLVolume *v, Transform3d &tran); 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); + 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); private: // 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/Selection.cpp b/src/slic3r/GUI/Selection.cpp index d26a842e7..9e12147b9 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1635,6 +1635,54 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, un 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 90cb94ae6..09189e33a 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -399,6 +399,9 @@ 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