From b40473be51fd1050fa4d7b2c3297230758085d46 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 9 Feb 2023 08:52:07 +0100 Subject: [PATCH] CutGizmo: Improvements to identify Upper/Lower parts_count + Rework CutDialog + Added "Cut to" section (Idea from #9564[Cut: Keep results as parts of current idea] was used, but there is other implementation) + Fix for #9657 - Inconvenient gizma, blocks the view of the cut of small parts + Add functionality "Flip cut plane" = Fix for #9632 - Adding connectors to either split cut part + ImGuiWrapper: added tooltip for button --- src/libslic3r/Model.cpp | 57 +++-- src/libslic3r/Model.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 334 ++++++++++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 25 +- src/slic3r/GUI/ImGuiWrapper.cpp | 11 +- src/slic3r/GUI/ImGuiWrapper.hpp | 2 +- 6 files changed, 295 insertions(+), 136 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index bab2e5bc86..9c074ef1e9 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1477,6 +1477,12 @@ void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d& // Add required cut parts to the objects + if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) { + add_cut_volume(lower_mesh, lower, volume, cut_matrix); + add_cut_volume(upper_mesh, lower, volume, cut_matrix); + return; + } + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) add_cut_volume(upper_mesh, upper, volume, cut_matrix); @@ -1608,34 +1614,39 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix, ModelObjectPtrs res; - if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) { - invalidate_translations(upper, instances[instance]); - - reset_instance_transformation(upper, instance, cut_matrix, - attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), - attributes.has(ModelObjectCutAttribute::FlipUpper), - local_displace); - res.push_back(upper); - } - - if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) { - invalidate_translations(lower, instances[instance]); - - reset_instance_transformation(lower, instance, cut_matrix, - attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), - attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower)); + if (attributes.has(ModelObjectCutAttribute::KeepAsParts) && !lower->volumes.empty()) { res.push_back(lower); } + else { + if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) { + invalidate_translations(upper, instances[instance]); - if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) { - for (auto dowel : dowels) { - invalidate_translations(dowel, instances[instance]); + reset_instance_transformation(upper, instance, cut_matrix, + attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), + attributes.has(ModelObjectCutAttribute::FlipUpper), + local_displace); + res.push_back(upper); + } - reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace); + if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) { + invalidate_translations(lower, instances[instance]); - local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0)); - dowel->name += "-Dowel-" + dowel->volumes[0]->name; - res.push_back(dowel); + reset_instance_transformation(lower, instance, cut_matrix, + attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), + attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower)); + res.push_back(lower); + } + + if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) { + for (auto dowel : dowels) { + invalidate_translations(dowel, instances[instance]); + + reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace); + + local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0)); + dowel->name += "-Dowel-" + dowel->volumes[0]->name; + res.push_back(dowel); + } } } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 6e444fe491..a1ae950d18 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -316,7 +316,7 @@ enum class ModelVolumeType : int { SUPPORT_ENFORCER, }; -enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels }; +enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, KeepAsParts, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels }; using ModelObjectCutAttributes = enum_bitmask; ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 0798b61f49..6fd74c2400 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -21,6 +21,8 @@ namespace Slic3r { namespace GUI { static const ColorRGBA GRABBER_COLOR = ColorRGBA::YELLOW(); +static const ColorRGBA ABOVE_GRABBER_COLOR = ColorRGBA::CYAN(); +static const ColorRGBA BELOW_GRABBER_COLOR = ColorRGBA::MAGENTA(); // connector colors static const ColorRGBA PLAG_COLOR = ColorRGBA::YELLOW(); @@ -196,7 +198,7 @@ static void init_from_angle_arc(GLModel& model, double angle, double radius) GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) - , m_connectors_group_id (3) + , m_connectors_group_id (GrabberID::Count) , m_connector_type (CutConnectorType::Plug) , m_connector_style (size_t(CutConnectorStyle::Prizm)) , m_connector_shape_id (size_t(CutConnectorShape::Circle)) @@ -227,13 +229,19 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, m_axis_names = { "X", "Y", "Z" }; + m_part_orientation_names = { + {"none", _L("Keep orientation")}, + {"on_cut", _L("Place on cut")}, + {"flip", _L("Flip upside down")}, + }; + update_connector_shape(); } std::string GLGizmoCut3D::get_tooltip() const { std::string tooltip; - if (m_hover_id == Z) { + if (m_hover_id == Z || (m_dragging && m_hover_id == CutPlane)) { double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; std::string unit_str = " " + (m_imperial_units ? _u8L("inch") : _u8L("mm")); const BoundingBoxf3 tbb = transformed_bounding_box(m_plane_center); @@ -249,6 +257,11 @@ std::string GLGizmoCut3D::get_tooltip() const } return tooltip; } + + if (!m_dragging && m_hover_id == CutPlane) + return _u8L("Click to flip the cut plane\n" + "Drag to move the cut plane"); + if (tooltip.empty() && (m_hover_id == X || m_hover_id == Y)) { std::string axis = m_hover_id == X ? "X" : "Y"; return axis + ": " + format(float(rad2deg(m_angle)), 1) + _u8L("°"); @@ -278,11 +291,17 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) if (use_grabbers(mouse_event)) { if (m_hover_id >= m_connectors_group_id) { - if (mouse_event.LeftDown() && !mouse_event.CmdDown()&& !mouse_event.AltDown()) + if (mouse_event.LeftDown() && !mouse_event.CmdDown() && !mouse_event.AltDown()) unselect_all_connectors(); if (mouse_event.LeftUp() && !mouse_event.ShiftDown()) gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); } + else if (m_hover_id == CutPlane) { + if (mouse_event.LeftDown()) + m_was_cut_plane_dragged = false; + else if (mouse_event.LeftUp() && !m_was_cut_plane_dragged) + flip_cut_plane(); + } return true; } @@ -656,12 +675,11 @@ void GLGizmoCut3D::render_cut_plane() shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - if (can_perform_cut() && has_valid_contour()) -// m_plane.set_color({ 0.8f, 0.8f, 0.8f, 0.5f }); - m_plane.set_color({ 0.9f, 0.9f, 0.9f, 0.5f }); + if (can_perform_cut() && has_valid_contour()) + m_plane.model.set_color({ 0.8f, 0.8f, 0.8f, 0.5f }); else - m_plane.set_color({ 1.0f, 0.8f, 0.8f, 0.5f }); - m_plane.render(); + m_plane.model.set_color({ 1.0f, 0.8f, 0.8f, 0.5f }); + m_plane.model.render(); glsafe(::glEnable(GL_CULL_FACE)); glsafe(::glDisable(GL_BLEND)); @@ -717,7 +735,7 @@ void GLGizmoCut3D::render_line(GLModel& line_model, const ColorRGBA& color, Tran } } -void GLGizmoCut3D::render_rotation_snapping(Axis axis, const ColorRGBA& color) +void GLGizmoCut3D::render_rotation_snapping(GrabberID axis, const ColorRGBA& color) { GLShaderProgram* line_shader = OpenGLManager::get_gl_info().is_core_profile() ? wxGetApp().get_shader("dashed_thick_lines") : wxGetApp().get_shader("flat"); if (!line_shader) @@ -760,49 +778,34 @@ void GLGizmoCut3D::render_cut_plane_grabbers() { glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - ColorRGBA color = m_hover_id == Z ? complementary(GRABBER_COLOR) : GRABBER_COLOR; + ColorRGBA color = ColorRGBA::GRAY(); const Transform3d view_matrix = wxGetApp().plater()->get_camera().get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; const double mean_size = get_grabber_mean_size(bounding_box()); + double size; - double size = m_dragging && m_hover_id == Z ? get_dragging_half_size(mean_size) : get_half_size(mean_size); + const bool dragging_by_cut_plane = m_dragging && m_hover_id == CutPlane; - Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); - Vec3d offset = 1.25 * size * Vec3d::UnitZ(); + if (!dragging_by_cut_plane) { + render_grabber_connection(GRABBER_COLOR, view_matrix); - // render Z grabber - - if (!m_dragging && m_hover_id < 0) - render_grabber_connection(color, view_matrix); - render_model(m_sphere.model, color, view_matrix * scale_transform(size)); - - if ((!m_dragging && m_hover_id < 0) || m_hover_id == Z) - { - const BoundingBoxf3 tbb = transformed_bounding_box(m_plane_center); - if (tbb.min.z() <= 0.0) - render_model(m_cone.model, color, view_matrix * translation_transform(-offset) * rotation_transform(PI * Vec3d::UnitX()) * scale_transform(cone_scale)); - - if (tbb.max.z() >= 0.0) - render_model(m_cone.model, color, view_matrix * translation_transform(offset) * scale_transform(cone_scale)); - } - - // render top sphere for X/Y grabbers - - if ((!m_dragging && m_hover_id < 0) || m_hover_id == X || m_hover_id == Y) - { + // render sphere grabber size = m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size); - color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : - m_hover_id == X ? complementary(ColorRGBA::RED()) : ColorRGBA::GRAY(); + color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : + m_hover_id == X ? complementary(ColorRGBA::RED()) : + m_hover_id == Z ? GRABBER_COLOR : ColorRGBA::GRAY(); render_model(m_sphere.model, color, view_matrix * translation_transform(m_grabber_connection_len * Vec3d::UnitZ()) * scale_transform(size)); } + const bool no_one_grabber_hovered = !m_dragging && (m_hover_id < 0 || m_hover_id == CutPlane); + // render X grabber - if ((!m_dragging && m_hover_id < 0) || m_hover_id == X) + if (no_one_grabber_hovered || m_hover_id == X) { size = m_dragging && m_hover_id == X ? get_dragging_half_size(mean_size) : get_half_size(mean_size); - cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); + const Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); color = m_hover_id == X ? complementary(ColorRGBA::RED()) : ColorRGBA::RED(); if (m_hover_id == X) { @@ -810,7 +813,7 @@ void GLGizmoCut3D::render_cut_plane_grabbers() render_rotation_snapping(X, color); } - offset = Vec3d(0.0, 1.25 * size, m_grabber_connection_len); + Vec3d offset = Vec3d(0.0, 1.25 * size, m_grabber_connection_len); render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitX()) * scale_transform(cone_scale)); offset = Vec3d(0.0, -1.25 * size, m_grabber_connection_len); render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitX()) * scale_transform(cone_scale)); @@ -818,10 +821,10 @@ void GLGizmoCut3D::render_cut_plane_grabbers() // render Y grabber - if ((!m_dragging && m_hover_id < 0) || m_hover_id == Y) + if (no_one_grabber_hovered || m_hover_id == Y) { size = m_dragging && m_hover_id == Y ? get_dragging_half_size(mean_size) : get_half_size(mean_size); - cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); + const Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : ColorRGBA::GREEN(); if (m_hover_id == Y) { @@ -829,7 +832,7 @@ void GLGizmoCut3D::render_cut_plane_grabbers() render_rotation_snapping(Y, color); } - offset = Vec3d(1.25 * size, 0.0, m_grabber_connection_len); + Vec3d offset = Vec3d(1.25 * size, 0.0, m_grabber_connection_len); render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); offset = Vec3d(-1.25 * size, 0.0, m_grabber_connection_len); render_model(m_cone.model, color, view_matrix * translation_transform(offset)* rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); @@ -940,8 +943,8 @@ void GLGizmoCut3D::on_register_raycasters_for_picking() m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Y, *m_cone.mesh_raycaster, Transform3d::Identity())); m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Z, *m_sphere.mesh_raycaster, Transform3d::Identity())); - m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Z, *m_cone.mesh_raycaster, Transform3d::Identity())); - m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Z, *m_cone.mesh_raycaster, Transform3d::Identity())); + + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlane, *m_plane.mesh_raycaster, Transform3d::Identity())); } update_raycasters_for_picking_transform(); @@ -1018,20 +1021,22 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform() const double size = get_half_size(get_grabber_mean_size(box)); Vec3d scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); + int id = 0; + Vec3d offset = Vec3d(0.0, 1.25 * size, m_grabber_connection_len); - m_raycasters[0]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitX()) * scale_transform(scale)); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitX()) * scale_transform(scale)); offset = Vec3d(0.0, -1.25 * size, m_grabber_connection_len); - m_raycasters[1]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitX()) * scale_transform(scale)); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitX()) * scale_transform(scale)); offset = Vec3d(1.25 * size, 0.0, m_grabber_connection_len); - m_raycasters[2]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); offset = Vec3d(-1.25 * size, 0.0, m_grabber_connection_len); - m_raycasters[3]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); offset = 1.25 * size * Vec3d::UnitZ(); - m_raycasters[4]->set_transform(trafo * scale_transform(size)); - m_raycasters[5]->set_transform(trafo * translation_transform(-offset) * rotation_transform(PI * Vec3d::UnitX()) * scale_transform(scale)); - m_raycasters[6]->set_transform(trafo * translation_transform(offset) * scale_transform(scale)); + m_raycasters[id++]->set_transform(trafo * translation_transform(m_grabber_connection_len * Vec3d::UnitZ()) * scale_transform(size)); + + m_raycasters[id++]->set_transform(trafo); } } @@ -1072,7 +1077,7 @@ bool GLGizmoCut3D::on_is_selectable() const return wxGetApp().get_mode() != comSimple; } -Vec3d GLGizmoCut3D::mouse_position_in_local_plane(Axis axis, const Linef3& mouse_ray) const +Vec3d GLGizmoCut3D::mouse_position_in_local_plane(GrabberID axis, const Linef3& mouse_ray) const { double half_pi = 0.5 * PI; @@ -1108,13 +1113,11 @@ Vec3d GLGizmoCut3D::mouse_position_in_local_plane(Axis axis, const Linef3& mouse void GLGizmoCut3D::dragging_grabber_z(const GLGizmoBase::UpdateData &data) { - Vec3d starting_box_center = m_plane_center - Vec3d::UnitZ(); // some Margin - rotate_vec3d_around_plane_center(starting_box_center); + const Vec3d grabber_init_pos = (m_hover_id == CutPlane ? 0. : m_grabber_connection_len) * Vec3d::UnitZ(); + const Vec3d starting_drag_position = translation_transform(m_plane_center) * m_rotation_m * grabber_init_pos; + double projection = 0.0; - const Vec3d&starting_drag_position = m_plane_center; - double projection = 0.0; - - Vec3d starting_vec = starting_drag_position - starting_box_center; + Vec3d starting_vec = m_rotation_m * Vec3d::UnitZ(); if (starting_vec.norm() != 0.0) { Vec3d mouse_dir = data.mouse_ray.unit_vector(); // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position @@ -1130,17 +1133,19 @@ void GLGizmoCut3D::dragging_grabber_z(const GLGizmoBase::UpdateData &data) projection = inters_vec.dot(starting_vec); } if (wxGetKeyState(WXK_SHIFT)) - projection = m_snap_step * (double)std::round(projection / m_snap_step); + projection = m_snap_step * std::round(projection / m_snap_step); const Vec3d shift = starting_vec * projection; // move cut plane center set_center(m_plane_center + shift); + + m_was_cut_plane_dragged = true; } void GLGizmoCut3D::dragging_grabber_xy(const GLGizmoBase::UpdateData &data) { - const Vec2d mouse_pos = to_2d(mouse_position_in_local_plane((Axis)m_hover_id, data.mouse_ray)); + const Vec2d mouse_pos = to_2d(mouse_position_in_local_plane((GrabberID)m_hover_id, data.mouse_ray)); const Vec2d orig_dir = Vec2d::UnitX(); const Vec2d new_dir = mouse_pos.normalized(); @@ -1197,7 +1202,7 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) { if (m_hover_id < 0) return; - if (m_hover_id == Z) + if (m_hover_id == Z || m_hover_id == CutPlane) dragging_grabber_z(data); else if (m_hover_id == X || m_hover_id == Y) dragging_grabber_xy(data); @@ -1223,8 +1228,9 @@ void GLGizmoCut3D::on_stop_dragging() Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Rotate cut plane"), UndoRedo::SnapshotType::GizmoAction); m_start_dragging_m = m_rotation_m; } - else if (m_hover_id == Z) { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move cut plane"), UndoRedo::SnapshotType::GizmoAction); + else if (m_hover_id == Z || m_hover_id == CutPlane) { + if (m_was_cut_plane_dragged) + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move cut plane"), UndoRedo::SnapshotType::GizmoAction); m_ar_plane_center = m_plane_center; } } @@ -1332,7 +1338,7 @@ bool GLGizmoCut3D::update_bb() set_center_pos(m_bb_center, true); m_radius = box.radius(); - m_grabber_connection_len = 0.75 * m_radius;// std::min(0.75 * m_radius, 35.0); + m_grabber_connection_len = 0.5 * m_radius;// std::min(0.75 * m_radius, 35.0); m_grabber_radius = m_grabber_connection_len * 0.85; m_snap_coarse_in_radius = m_grabber_radius / 3.0; @@ -1372,6 +1378,14 @@ void GLGizmoCut3D::init_picking_models() m_sphere.model.init_from(its); m_sphere.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); } + + if (!m_plane.model.is_initialized() && !m_hide_cut_plane && !m_connectors_editing) { + const double cp_width = 0.02 * get_grabber_mean_size(bounding_box()); + indexed_triangle_set its = its_make_frustum_dowel((double)m_cut_plane_radius_koef * m_radius, cp_width, m_cut_plane_as_circle ? 180 : 4); + m_plane.model.init_from(its); + m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); + } + if (m_shapes.empty()) init_connector_shapes(); } @@ -1392,18 +1406,6 @@ void GLGizmoCut3D::init_rendering_items() } if (!m_angle_arc.is_initialized() || m_angle != 0.0) init_from_angle_arc(m_angle_arc, m_angle, m_grabber_connection_len); - - if (!m_plane.is_initialized() && !m_hide_cut_plane && !m_connectors_editing) { -#if 1 - const double cp_width = 0.02 * get_grabber_mean_size(bounding_box()); - m_plane.init_from(its_make_frustum_dowel((double)m_cut_plane_radius_koef * m_radius, cp_width, m_cut_plane_as_circle ? 180 : 4)); -#else - if (m_cut_plane_as_circle) - m_plane.init_from(its_make_frustum_dowel(2. * m_radius, 0.3, 180)); - else - m_plane.init_from(its_make_square_plane(float(m_radius))); -#endif - } } void GLGizmoCut3D::render_clipper_cut() @@ -1566,6 +1568,8 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) reset_connectors(); m_imgui->disabled_end(); + render_flip_plane_button(m_connectors_editing && connectors.empty()); + m_imgui->text(_L("Type")); bool type_changed = render_connect_type_radio_button(CutConnectorType::Plug); type_changed |= render_connect_type_radio_button(CutConnectorType::Dowel); @@ -1660,6 +1664,58 @@ void GLGizmoCut3D::set_connectors_editing(bool connectors_editing) m_parent.request_extra_frame(); } +void GLGizmoCut3D::flip_cut_plane() +{ + m_rotation_m = m_start_dragging_m * rotation_transform(PI * Vec3d::UnitX()); + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Flip cut plane"), UndoRedo::SnapshotType::GizmoAction); + m_start_dragging_m = m_rotation_m; + + update_clipper(); +} + +void GLGizmoCut3D::render_flip_plane_button(bool disable_pred /*=false*/) +{ + ImGui::SameLine(2.5 * m_label_width); + + if (m_hover_id == CutPlane) + ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_ButtonHovered)); + + m_imgui->disabled_begin(disable_pred); + if (m_imgui->button(_L("Flip cut plane"))) + flip_cut_plane(); + m_imgui->disabled_end(); + + if (m_hover_id == CutPlane) + ImGui::PopStyleColor(); +} + +void GLGizmoCut3D::add_vertical_scaled_interval(float interval) +{ + ImGui::GetCurrentWindow()->DC.CursorPos.y += m_imgui->scaled(interval); +} + +void GLGizmoCut3D::add_horizontal_scaled_interval(float interval) +{ + ImGui::GetCurrentWindow()->DC.CursorPos.x += m_imgui->scaled(interval); +} + +void GLGizmoCut3D::add_horizontal_shift(float shift) +{ + ImGui::GetCurrentWindow()->DC.CursorPos.x += shift; +} + +void GLGizmoCut3D::render_color_marker(float size, const ImU32& color) +{ + ImGui::SameLine(); + const float radius = 0.5f * size; + ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos; + pos.x += size; + pos.y += 1.25f * radius; + ImGui::GetCurrentWindow()->DrawList->AddNgonFilled(pos, radius, color, 6); + ImGuiWrapper::text(" "); +} + void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) { // WIP : cut plane mode @@ -1667,65 +1723,130 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) if (m_mode == size_t(CutMode::cutPlanar)) { ImGui::AlignTextToFramePadding(); - m_imgui->text(wxString(ImGui::InfoMarkerSmall)); + ImGuiWrapper::text(wxString(ImGui::InfoMarkerSmall)); ImGui::SameLine(); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, - get_wraped_wxString(_L("Hold SHIFT key and connect some two points of an object to cut by line"), 40)); + ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, + get_wraped_wxString(_L("Hold SHIFT key to draw a cut line"), 40)); ImGui::Separator(); render_build_size(); ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Cut position: ")); + ImGuiWrapper::text(_L("Cut position: ")); ImGui::SameLine(m_label_width); render_move_center_input(Z); ImGui::SameLine(); + const bool has_connectors = !connectors.empty(); + const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && bounding_box().center() == m_plane_center; m_imgui->disabled_begin(is_cut_plane_init); - if (render_reset_button("cut_plane", _u8L("Reset cutting plane"))) - reset_cut_plane(); + if (render_reset_button("cut_plane", _u8L("Reset cutting plane"))) + reset_cut_plane(); m_imgui->disabled_end(); +// render_flip_plane_button(); + + add_vertical_scaled_interval(0.75f); + m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower); - if (m_imgui->button(_L("Add/Edit connectors"))) + if (m_imgui->button(has_connectors ? _L("Edit connectors") : _L("Add connectors"))) set_connectors_editing(true); m_imgui->disabled_end(); + ImGui::SameLine(2.5f * m_label_width); + + m_imgui->disabled_begin(is_cut_plane_init && !has_connectors); + if (m_imgui->button(_L("Reset cut"), _L("Reset cutting plane and remove connectors"))) { + reset_cut_plane(); + reset_connectors(); + } + m_imgui->disabled_end(); + ImGui::Separator(); - float label_width = 0; - for (const wxString& label : {_L("Upper part"), _L("Lower part")}) { - const float width = m_imgui->calc_text_size(label).x + m_imgui->scaled(1.5f); - if (label_width < width) - label_width = width; + // render "After Cut" section + + ImVec2 label_size; + for (const auto& item : m_part_orientation_names) { + const ImVec2 text_size = ImGuiWrapper::calc_text_size(item.second); + if (label_size.x < text_size.x) + label_size.x = text_size.x; + if (label_size.y < text_size.y) + label_size.y = text_size.y; } - - auto render_part_action_line = [this, label_width, connectors](const wxString& label, const wxString& suffix, bool& keep_part, bool& place_on_cut_part, bool& rotate_part) { + const float h_shift = label_size.x + m_imgui->scaled(3.f); + const float marker_size = label_size.y; + + auto render_part_name = [this, marker_size, has_connectors](const wxString& name, bool& keep_part, const ImU32& color) { bool keep = true; - ImGui::AlignTextToFramePadding(); - m_imgui->text(label); + add_horizontal_shift(m_imgui->scaled(1.2f)); + m_imgui->checkbox((m_keep_as_parts ? _L("Part") : _L("Object")) + " " + name, has_connectors ? keep : keep_part); + render_color_marker(marker_size, color); + }; - ImGui::SameLine(label_width); - - m_imgui->disabled_begin(!connectors.empty()); - m_imgui->checkbox(_L("Keep") + suffix, connectors.empty() ? keep_part : keep); - m_imgui->disabled_end(); - - ImGui::SameLine(); - - m_imgui->disabled_begin(!keep_part); - if (m_imgui->checkbox(_L("Place on cut") + suffix, place_on_cut_part)) + auto render_part_actions = [this, h_shift] (const wxString& suffix, const bool& keep_part, bool& place_on_cut_part, bool& rotate_part) + { + float shift = m_imgui->scaled(1.2f); + if (suffix == "##lower") + shift += h_shift; + m_imgui->disabled_begin(!keep_part || m_keep_as_parts); + add_horizontal_shift(shift); + if (m_imgui->radio_button(m_part_orientation_names.at("none") + suffix, !place_on_cut_part && !rotate_part)) { rotate_part = false; - ImGui::SameLine(); - if (m_imgui->checkbox(_L("Flip") + suffix, rotate_part)) place_on_cut_part = false; + } + + add_horizontal_shift(shift); + if (m_imgui->radio_button(m_part_orientation_names.at("on_cut") + suffix, place_on_cut_part)) { + place_on_cut_part = !place_on_cut_part; + rotate_part = false; + } + + add_horizontal_shift(shift); + if (m_imgui->radio_button(m_part_orientation_names.at("flip") + suffix, rotate_part)) { + rotate_part = !rotate_part; + place_on_cut_part = false; + } m_imgui->disabled_end(); }; - m_imgui->text(_L("After cut") + ": "); - render_part_action_line( _L("Upper part"), "##upper", m_keep_upper, m_place_on_cut_upper, m_rotate_upper); - render_part_action_line( _L("Lower part"), "##lower", m_keep_lower, m_place_on_cut_lower, m_rotate_lower); + ImGuiWrapper::text(_L("After cut") + ": "); + add_vertical_scaled_interval(0.5f); + + m_imgui->disabled_begin(has_connectors || m_keep_as_parts); + render_part_name("A", m_keep_upper, m_imgui->to_ImU32(ABOVE_GRABBER_COLOR)); + ImGui::SameLine(h_shift + ImGui::GetCurrentWindow()->WindowPadding.x); + render_part_name("B", m_keep_lower, m_imgui->to_ImU32(BELOW_GRABBER_COLOR)); + m_imgui->disabled_end(); + + add_vertical_scaled_interval(0.5f); + + const ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos; + render_part_actions("##upper", m_keep_upper, m_place_on_cut_upper, m_rotate_upper); + + ImGui::GetCurrentWindow()->DC.CursorPos = pos; + render_part_actions("##lower", m_keep_lower, m_place_on_cut_lower, m_rotate_lower); + + add_vertical_scaled_interval(0.75f); + + m_imgui->disabled_begin(has_connectors); + add_horizontal_shift(m_imgui->scaled(/*1*/.2f)); + ImGuiWrapper::text(_L("Cut to") + ":"); + + ImGui::SameLine(); + if (m_imgui->radio_button(_L("Objects"), !m_keep_as_parts)) + m_keep_as_parts = false; + ImGui::SameLine(); + if (m_imgui->radio_button(_L("Parts"), m_keep_as_parts)) + m_keep_as_parts = true; + + if (m_keep_as_parts) { + m_keep_upper = m_keep_lower = true; + m_place_on_cut_upper = m_place_on_cut_lower = false; + m_rotate_upper = m_rotate_lower = false; + } + m_imgui->disabled_end(); } ImGui::Separator(); @@ -2110,6 +2231,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) plater->cut(object_idx, instance_idx, translation_transform(cut_center_offset) * m_rotation_m, only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | + only_if(has_connectors ? false: m_keep_as_parts, ModelObjectCutAttribute::KeepAsParts) | only_if(m_place_on_cut_upper, ModelObjectCutAttribute::PlaceOnCutUpper) | only_if(m_place_on_cut_lower, ModelObjectCutAttribute::PlaceOnCutLower) | only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) | diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 3483ae2015..3b7663171a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -6,6 +6,7 @@ #include "slic3r/GUI/GLModel.hpp" #include "libslic3r/TriangleMesh.hpp" #include "libslic3r/Model.hpp" +#include "imgui/imgui.h" namespace Slic3r { @@ -20,6 +21,14 @@ enum class SLAGizmoEventType : unsigned char; class GLGizmoCut3D : public GLGizmoBase { + enum GrabberID { + X = 0, + Y, + Z, + CutPlane, + Count, + }; + Transform3d m_rotation_m{ Transform3d::Identity() }; double m_snap_step{ 1.0 }; int m_connectors_group_id; @@ -57,10 +66,10 @@ class GLGizmoCut3D : public GLGizmoBase Vec2d m_ldown_mouse_position{ Vec2d::Zero() }; - GLModel m_plane; GLModel m_grabber_connection; GLModel m_cut_line; + PickingModel m_plane; PickingModel m_sphere; PickingModel m_cone; std::map m_shapes; @@ -90,6 +99,7 @@ class GLGizmoCut3D : public GLGizmoBase bool m_keep_upper{ true }; bool m_keep_lower{ true }; + bool m_keep_as_parts{ false }; bool m_place_on_cut_upper{ true }; bool m_place_on_cut_lower{ false }; bool m_rotate_upper{ false }; @@ -121,6 +131,7 @@ class GLGizmoCut3D : public GLGizmoBase GLSelectionRectangle m_selection_rectangle; bool m_has_invalid_connector{ false }; + bool m_was_cut_plane_dragged { false }; bool m_show_shortcuts{ false }; std::vector> m_shortcuts; @@ -154,6 +165,8 @@ class GLGizmoCut3D : public GLGizmoBase std::vector m_axis_names; + std::map m_part_orientation_names; + public: GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); @@ -192,7 +205,7 @@ protected: void on_set_hover_id() override; bool on_is_activable() const override; bool on_is_selectable() const override; - Vec3d mouse_position_in_local_plane(Axis axis, const Linef3&mouse_ray) const; + Vec3d mouse_position_in_local_plane(GrabberID axis, const Linef3&mouse_ray) const; void dragging_grabber_z(const GLGizmoBase::UpdateData &data); void dragging_grabber_xy(const GLGizmoBase::UpdateData &data); void dragging_connector(const GLGizmoBase::UpdateData &data); @@ -211,6 +224,12 @@ protected: void render_build_size(); void reset_cut_plane(); void set_connectors_editing(bool connectors_editing); + void flip_cut_plane(); + void render_flip_plane_button(bool disable_pred = false); + void add_vertical_scaled_interval(float interval); + void add_horizontal_scaled_interval(float interval); + void add_horizontal_shift(float shift); + void render_color_marker(float size, const ImU32& color); void render_cut_plane_input_window(CutConnectors &connectors); void init_input_window_data(CutConnectors &connectors); void render_input_window_warning() const; @@ -258,7 +277,7 @@ private: void render_cut_plane(); void render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix); void render_line(GLModel& line_model, const ColorRGBA& color, Transform3d view_model_matrix, float width); - void render_rotation_snapping(Axis axis, const ColorRGBA& color); + void render_rotation_snapping(GrabberID axis, const ColorRGBA& color); void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix); void render_cut_plane_grabbers(); void render_cut_line(); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index c92f65f6b9..8908a5ef9d 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -453,10 +453,17 @@ void ImGuiWrapper::end() ImGui::End(); } -bool ImGuiWrapper::button(const wxString &label) +bool ImGuiWrapper::button(const wxString &label, const wxString& tooltip) { auto label_utf8 = into_u8(label); - return ImGui::Button(label_utf8.c_str()); + const bool ret = ImGui::Button(label_utf8.c_str()); + + if (!tooltip.IsEmpty() && ImGui::IsItemHovered()) { + auto tooltip_utf8 = into_u8(tooltip); + ImGui::SetTooltip(tooltip_utf8.c_str()); + } + + return ret; } bool ImGuiWrapper::button(const wxString& label, float width, float height) diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 3365954bab..4d8b4944cd 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -91,7 +91,7 @@ public: bool begin(const wxString& name, bool* close, int flags = 0); void end(); - bool button(const wxString &label); + bool button(const wxString &label, const wxString& tooltip = {}); bool button(const wxString& label, float width, float height); bool button(const wxString& label, const ImVec2 &size, bool enable); // default size = ImVec2(0.f, 0.f) bool radio_button(const wxString &label, bool active);