diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 9e1183c07..2adebbbec 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -254,10 +254,8 @@ 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()) { - std::fill(m_selected.begin(), m_selected.end(), false); - m_selected_count = 0; - } + 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()); } @@ -1012,99 +1010,103 @@ Vec3d GLGizmoCut3D::mouse_position_in_local_plane(Axis axis, const Linef3& mouse return transform(mouse_ray, m).intersect_plane(0.0); } +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&starting_drag_position = m_plane_center; + double projection = 0.0; + + Vec3d starting_vec = starting_drag_position - starting_box_center; + 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 + // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form + // in our case plane normal and ray direction are the same (orthogonal view) + // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal + Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + // vector from the starting position to the found intersection + Vec3d inters_vec = inters - starting_drag_position; + + starting_vec.normalize(); + // finds projection of the vector along the staring direction + projection = inters_vec.dot(starting_vec); + } + if (wxGetKeyState(WXK_SHIFT)) + projection = m_snap_step * (double)std::round(projection / m_snap_step); + + const Vec3d shift = starting_vec * projection; + + // move cut plane center + set_center(m_plane_center + shift); +} + +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 orig_dir = Vec2d::UnitX(); + const Vec2d new_dir = mouse_pos.normalized(); + + const double two_pi = 2.0 * PI; + + double theta = ::acos(std::clamp(new_dir.dot(orig_dir), -1.0, 1.0)); + if (cross2(orig_dir, new_dir) < 0.0) + theta = two_pi - theta; + + const double len = mouse_pos.norm(); + // snap to coarse snap region + if (m_snap_coarse_in_radius <= len && len <= m_snap_coarse_out_radius) { + const double step = two_pi / double(SnapRegionsCount); + theta = step * std::round(theta / step); + } + // snap to fine snap region (scale) + else if (m_snap_fine_in_radius <= len && len <= m_snap_fine_out_radius) { + const double step = two_pi / double(ScaleStepsCount); + theta = step * std::round(theta / step); + } + + if (is_approx(theta, two_pi)) + theta = 0.0; + if (m_hover_id == X) + theta += 0.5 * PI; + + Vec3d rotation = Vec3d::Zero(); + rotation[m_hover_id] = theta; + m_rotation_m = m_start_dragging_m * rotation_transform(rotation); + + m_angle = theta; + while (m_angle > two_pi) + m_angle -= two_pi; + if (m_angle < 0.0) + m_angle += two_pi; + + update_clipper(); +} + +void GLGizmoCut3D::dragging_connector(const GLGizmoBase::UpdateData &data) +{ + CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; + std::pair pos_and_normal; + Vec3d pos_world; + + if (unproject_on_cut_plane(data.mouse_pos.cast(), pos_and_normal, pos_world)) { + connectors[m_hover_id - m_connectors_group_id].pos = pos_and_normal.first; + update_raycasters_for_picking_transform(); + } +} + void GLGizmoCut3D::on_dragging(const UpdateData& data) { if (m_hover_id < 0) return; - - CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; - - if (m_hover_id == Z) { - Vec3d starting_box_center = m_plane_center - Vec3d::UnitZ();// some Margin - rotate_vec3d_around_plane_center(starting_box_center); - - const Vec3d& starting_drag_position = m_plane_center; - double projection = 0.0; - - Vec3d starting_vec = starting_drag_position - starting_box_center; - 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 - // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form - // in our case plane normal and ray direction are the same (orthogonal view) - // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal - Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; - // vector from the starting position to the found intersection - Vec3d inters_vec = inters - starting_drag_position; - - starting_vec.normalize(); - // finds projection of the vector along the staring direction - projection = inters_vec.dot(starting_vec); - } - if (wxGetKeyState(WXK_SHIFT)) - projection = m_snap_step * (double)std::round(projection / m_snap_step); - - Vec3d shift = starting_vec * projection; - - // move cut plane center - set_center(m_plane_center + shift); - } - - else if (m_hover_id == X || m_hover_id == Y) { - - Vec3d rotation = Vec3d::Zero(); - - const Vec2d mouse_pos = to_2d(mouse_position_in_local_plane((Axis)m_hover_id, data.mouse_ray)); - - const Vec2d orig_dir = Vec2d::UnitX(); - const Vec2d new_dir = mouse_pos.normalized(); - - const double two_pi = 2.0 * PI; - - double theta = ::acos(std::clamp(new_dir.dot(orig_dir), -1.0, 1.0)); - if (cross2(orig_dir, new_dir) < 0.0) - theta = two_pi - theta; - - const double len = mouse_pos.norm(); - // snap to coarse snap region - if (m_snap_coarse_in_radius <= len && len <= m_snap_coarse_out_radius) { - const double step = two_pi / double(SnapRegionsCount); - theta = step * std::round(theta / step); - } - else { - // snap to fine snap region (scale) - if (m_snap_fine_in_radius <= len && len <= m_snap_fine_out_radius) { - const double step = two_pi / double(ScaleStepsCount); - theta = step * std::round(theta / step); - } - } - - if (theta == two_pi) - theta = 0.0; - if (m_hover_id == X) - theta += 0.5 * PI; - - rotation[m_hover_id] = theta; - m_rotation_m = m_start_dragging_m * rotation_transform(rotation); - - m_angle = theta; - while (m_angle > two_pi) - m_angle -= two_pi; - if (m_angle < 0.0) - m_angle += two_pi; - - update_clipper(); - } - + if (m_hover_id == Z) + dragging_grabber_z(data); + else if (m_hover_id == X || m_hover_id == Y) + dragging_grabber_xy(data); else if (m_hover_id >= m_connectors_group_id && m_connector_mode == CutConnectorMode::Manual) - { - std::pair pos_and_normal; - Vec3d pos_world; - if (!unproject_on_cut_plane(data.mouse_pos.cast(), pos_and_normal, pos_world)) - return; - connectors[m_hover_id - m_connectors_group_id].pos = pos_and_normal.first; - update_raycasters_for_picking_transform(); - } + dragging_connector(data); } void GLGizmoCut3D::on_start_dragging() @@ -1215,10 +1217,10 @@ bool GLGizmoCut3D::update_bb() m_grabber_connection_len = 0.75 * 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.0f; - m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; + m_snap_coarse_in_radius = m_grabber_radius / 3.0; + m_snap_coarse_out_radius = m_snap_coarse_in_radius * 2.; m_snap_fine_in_radius = m_grabber_connection_len * 0.85; - m_snap_fine_out_radius = m_grabber_connection_len * 1.15f; + m_snap_fine_out_radius = m_grabber_connection_len * 1.15; m_plane.reset(); m_cone.reset(); @@ -1276,11 +1278,20 @@ void GLGizmoCut3D::init_rendering_items() m_plane.init_from(its_make_square_plane(float(m_radius))); } +void GLGizmoCut3D::render_clipper_cut() +{ + if (! m_connectors_editing) + ::glDisable(GL_DEPTH_TEST); + m_c->object_clipper()->render_cut(); + if (! m_connectors_editing) + ::glEnable(GL_DEPTH_TEST); +} + void GLGizmoCut3D::on_render() { if (update_bb() || force_update_clipper_on_render) { update_clipper_on_render(); - m_c->object_clipper()->set_behavior(m_connectors_editing, m_connectors_editing, 0.4f); + m_c->object_clipper()->set_behavior(m_connectors_editing, m_connectors_editing, 0.4); } init_picking_models(); @@ -1289,11 +1300,7 @@ void GLGizmoCut3D::on_render() render_connectors(); - if (! m_connectors_editing) - ::glDisable(GL_DEPTH_TEST); - m_c->object_clipper()->render_cut(); - if (! m_connectors_editing) - ::glEnable(GL_DEPTH_TEST); + render_clipper_cut(); if (!m_hide_cut_plane && !m_connectors_editing) { render_cut_plane(); @@ -1303,7 +1310,7 @@ void GLGizmoCut3D::on_render() render_cut_line(); } -void GLGizmoCut3D::render_debug_block() +void GLGizmoCut3D::render_debug_input_window() { m_imgui->begin(wxString("DEBUG")); @@ -1342,9 +1349,20 @@ void GLGizmoCut3D::adjust_window_position(float x, float y, float bottom_limit) } } -void GLGizmoCut3D::render_connectors_editing(CutConnectors &connectors) +void GLGizmoCut3D::unselect_all_connectors() +{ + std::fill(m_selected.begin(), m_selected.end(), false); + m_selected_count = 0; +} + +void GLGizmoCut3D::select_all_connectors() +{ + std::fill(m_selected.begin(), m_selected.end(), true); + m_selected_count = int(m_selected.size()); +} + +void GLGizmoCut3D::render_shortcuts() { - // add shortcuts panel if (m_imgui->button("? " + (m_show_shortcuts ? wxString(ImGui::CollapseBtn) : wxString(ImGui::ExpandBtn)))) m_show_shortcuts = !m_show_shortcuts; @@ -1354,6 +1372,19 @@ void GLGizmoCut3D::render_connectors_editing(CutConnectors &connectors) ImGui::SameLine(m_label_width); m_imgui->text(shortcut.second); } +} + +void GLGizmoCut3D::apply_selected_connectors(std::function apply_fn) +{ + for (size_t idx = 0; idx < m_selected.size(); idx++) + if (m_selected[idx]) + apply_fn(idx); +} + +void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) +{ + // add shortcuts panel + render_shortcuts(); // Connectors section @@ -1377,48 +1408,67 @@ void GLGizmoCut3D::render_connectors_editing(CutConnectors &connectors) bool type_changed = render_connect_type_radio_button(CutConnectorType::Plug); type_changed |= render_connect_type_radio_button(CutConnectorType::Dowel); if (type_changed) - for (size_t idx = 0; idx < m_selected.size() ; idx++) - if (m_selected[idx]) - connectors[idx].attribs.type = CutConnectorType(m_connector_type); + apply_selected_connectors([this, &connectors] (size_t idx) { connectors[idx].attribs.type = CutConnectorType(m_connector_type); }); if (render_combo(_u8L("Style"), m_connector_styles, m_connector_style)) - for (size_t idx = 0; idx < m_selected.size() ; idx++) - if (m_selected[idx]) - connectors[idx].attribs.style = CutConnectorStyle(m_connector_style) ; + apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); }); if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id)) - for (size_t idx = 0; idx < m_selected.size() ; idx++) - if (m_selected[idx]) - connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); + apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); }); if (render_slider_double_input(_u8L("Depth ratio"), m_connector_depth_ratio, m_connector_depth_ratio_tolerance)) - for (size_t idx = 0; idx < m_selected.size() ; idx++) - if (m_selected[idx]) { - auto&connector = connectors[idx]; - connector.height = float(m_connector_depth_ratio); - connector.height_tolerance = 0.01f * float(m_connector_depth_ratio_tolerance); - } + apply_selected_connectors([this, &connectors](size_t idx) { + connectors[idx].height = float(m_connector_depth_ratio); + connectors[idx].height_tolerance = 0.01f * float(m_connector_depth_ratio_tolerance); + }); if (render_slider_double_input(_u8L("Size"), m_connector_size, m_connector_size_tolerance)) - for (size_t idx = 0; idx < m_selected.size(); idx++) - if (m_selected[idx]) { - auto&connector = connectors[idx]; - connector.radius = float(m_connector_size * 0.5); - connector.radius_tolerance = 0.01f * float(m_connector_size_tolerance); - } + apply_selected_connectors([this, &connectors](size_t idx) { + connectors[idx].radius = float(m_connector_size * 0.5); + connectors[idx].radius_tolerance = 0.01f * float(m_connector_size_tolerance); + }); if (m_imgui->button(_L("Confirm connectors"))) { m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); - m_connectors_editing = false; - update_raycasters_for_picking(); - std::fill(m_selected.begin(), m_selected.end(), false); - m_selected_count = 0; + unselect_all_connectors(); + set_connectors_editing(false); } m_parent.request_extra_frame(); } -void GLGizmoCut3D::render_cut_plane_editing(CutConnectors &connectors) +void GLGizmoCut3D::render_build_size() +{ + 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(); + + Vec3d tbb_sz = tbb.size(); + wxString size = "X: " + double_to_string(tbb_sz.x() * koef, 2) + unit_str + + ", Y: " + double_to_string(tbb_sz.y() * koef, 2) + unit_str + + ", Z: " + double_to_string(tbb_sz.z() * koef, 2) + unit_str; + + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("Build size")); + ImGui::SameLine(m_label_width); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, size); +} + +void GLGizmoCut3D::reset_cut_plane() +{ + set_center(bounding_box().center()); + m_rotation_m = Transform3d::Identity(); + m_angle_arc.reset(); + update_clipper(); +} + +void GLGizmoCut3D::set_connectors_editing(bool connectors_editing) +{ + m_connectors_editing = connectors_editing; + update_raycasters_for_picking(); +} + +void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) { // WIP : cut plane mode // render_combo(_u8L("Mode"), m_modes, m_mode); @@ -1426,95 +1476,60 @@ void GLGizmoCut3D::render_cut_plane_editing(CutConnectors &connectors) if (m_mode == size_t(CutMode::cutPlanar)) { ImGui::AlignTextToFramePadding(); m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Hold SHIFT key and connect some two points of an object to cut by line")); + ImGui::Separator(); - 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(); - - Vec3d tbb_sz = tbb.size(); - wxString size = "X: " + double_to_string(tbb_sz.x() * koef, 2) + unit_str + - ", Y: " + double_to_string(tbb_sz.y() * koef, 2) + unit_str + - ", Z: " + double_to_string(tbb_sz.z() * koef, 2) + unit_str; + render_build_size(); - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Build size")); - ImGui::SameLine(m_label_width); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, size); - m_imgui->text(_L("Cut position: ")); render_move_center_input(Z); - if (m_imgui->button("Reset cutting plane")) { - set_center(bounding_box().center()); - m_rotation_m = Transform3d::Identity(); - m_angle_arc.reset(); - update_clipper(); - } + if (m_imgui->button("Reset cutting plane")) + reset_cut_plane(); ImGui::Separator(); + auto render_part_action_line = [this, connectors](const wxString& info_label, const wxString& label, const wxString& suffix, bool& keep_part, bool& place_on_cut_part, bool& rotate_part) { + bool keep = true; + + m_imgui->text(info_label); + ImGui::SameLine(m_label_width); + m_imgui->text(label); + + ImGui::SameLine(2 * m_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(3 * m_label_width); + + m_imgui->disabled_begin(!keep_part); + if (m_imgui->checkbox(_L("Place on cut") + suffix, place_on_cut_part)) + rotate_part = false; + ImGui::SameLine(); + if (m_imgui->checkbox(_L("Flip") + suffix, rotate_part)) + place_on_cut_part = false; + m_imgui->disabled_end(); + }; + ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("After cut") + ": "); - bool keep = true; - - ImGui::SameLine(m_label_width); - m_imgui->text(_L("Upper part")); - ImGui::SameLine(2 * m_label_width); - - m_imgui->disabled_begin(!connectors.empty()); - m_imgui->checkbox(_L("Keep") + "##upper", connectors.empty() ? m_keep_upper : keep); - m_imgui->disabled_end(); - ImGui::SameLine(3 * m_label_width); - - m_imgui->disabled_begin(!m_keep_upper); - - m_imgui->disabled_begin(is_approx(Transformation(m_rotation_m).get_rotation().x(), 0.) && is_approx(Transformation(m_rotation_m).get_rotation().y(), 0.)); - if (m_imgui->checkbox(_L("Place on cut") + "##upper", m_place_on_cut_upper)) - m_rotate_upper = false; - m_imgui->disabled_end(); - - ImGui::SameLine(); - if (m_imgui->checkbox(_L("Flip") + "##upper", m_rotate_upper)) - m_place_on_cut_upper = false; - - m_imgui->disabled_end(); - - m_imgui->text(""); - ImGui::SameLine(m_label_width); - m_imgui->text(_L("Lower part")); - ImGui::SameLine(2 * m_label_width); - - m_imgui->disabled_begin(!connectors.empty()); - - m_imgui->checkbox(_L("Keep") + "##lower", connectors.empty() ? m_keep_lower : keep); - m_imgui->disabled_end(); - ImGui::SameLine(3 * m_label_width); - m_imgui->disabled_begin(!m_keep_lower); - - if (m_imgui->checkbox(_L("Place on cut") + "##lower", m_place_on_cut_lower)) - m_rotate_lower = false; - ImGui::SameLine(); - if (m_imgui->checkbox(_L("Flip") + "##lower", m_rotate_lower)) - m_place_on_cut_lower = false; - - m_imgui->disabled_end(); + render_part_action_line(_L("After cut") + ": ", _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); } if (wxGetApp().plater()->printer_technology() == ptFFF) { m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower); - if (m_imgui->button(_L("Add/Edit connectors"))) { - m_connectors_editing = true; - update_raycasters_for_picking(); - } + if (m_imgui->button(_L("Add/Edit connectors"))) + set_connectors_editing(true); m_imgui->disabled_end(); } ImGui::Separator(); m_imgui->disabled_begin(!can_perform_cut()); - if((m_keep_upper || m_keep_lower) && m_imgui->button(_L("Perform cut"))) - perform_cut(m_parent.get_selection());; + if(m_imgui->button(_L("Perform cut"))) + perform_cut(m_parent.get_selection());; m_imgui->disabled_end(); } @@ -1550,7 +1565,7 @@ void GLGizmoCut3D::render_input_window_warning() const void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) { - m_imgui->begin(_L("Cut"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + m_imgui->begin(get_name(), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); // adjust window position to avoid overlap the view toolbar adjust_window_position(x, y, bottom_limit); @@ -1560,15 +1575,15 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) init_input_window_data(connectors); if (m_connectors_editing) // connectors mode - render_connectors_editing(connectors); + render_connectors_input_window(connectors); else - render_cut_plane_editing(connectors); + render_cut_plane_input_window(connectors); render_input_window_warning(); m_imgui->end(); - render_debug_block(); + render_debug_input_window(); } // get volume transformation regarding to the "border". Border is related from the size of connectors @@ -1598,9 +1613,6 @@ Transform3d GLGizmoCut3D::get_volume_transformation(const ModelVolume* volume) c void GLGizmoCut3D::render_connectors() { - if (!m_connectors_editing) - return; - ::glEnable(GL_DEPTH_TEST); if (cut_line_processing() || m_connector_mode == CutConnectorMode::Auto || !m_c->selection_info()) @@ -1631,7 +1643,7 @@ void GLGizmoCut3D::render_connectors() const ModelInstance* mi = mo->instances[inst_id]; const Vec3d& instance_offset = mi->get_offset(); - const float sla_shift = m_c->selection_info()->get_sla_shift(); + const double sla_shift = double(m_c->selection_info()->get_sla_shift()); const ClippingPlane* cp = m_c->object_clipper()->get_clipping_plane(); const Vec3d& normal = cp && cp->is_active() ? cp->get_normal() : m_clp_normal; @@ -1643,7 +1655,7 @@ void GLGizmoCut3D::render_connectors() for (size_t i = 0; i < connectors.size(); ++i) { const CutConnector& connector = connectors[i]; - double height = connector.height; + double height = double(connector.height); // recalculate connector position to world position Vec3d pos = connector.pos + instance_offset; if (connector.attribs.type == CutConnectorType::Dowel && @@ -1757,13 +1769,13 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) } plater->cut(object_idx, instance_idx, cut_center_offset, rotation, - only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | - only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | - 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) | - only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) | - only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels)); + only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | + only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | + 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) | + only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) | + only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels)); } else { // the object is SLA-elevated and the plane is under it. @@ -1907,6 +1919,85 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse return false; } +bool GLGizmoCut3D::add_connector(CutConnectors& connectors, const Vec2d& mouse_position) +{ + std::pair pos_and_normal; + Vec3d pos_world; + if (unproject_on_cut_plane(mouse_position.cast(), pos_and_normal, pos_world)) { + const Vec3d& hit = pos_and_normal.first; + + if (m_connectors_editing) { + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); + + connectors.emplace_back(hit, Transformation(m_rotation_m).get_rotation(), + float(m_connector_size) * 0.5f, float(m_connector_depth_ratio), + float(m_connector_size_tolerance) * 0.01f, float(m_connector_depth_ratio_tolerance) * 0.01f, + CutConnectorAttributes( CutConnectorType(m_connector_type), + CutConnectorStyle(m_connector_style), + CutConnectorShape(m_connector_shape_id))); + unselect_all_connectors(); + m_selected.push_back(true); + assert(m_selected.size() == connectors.size()); + update_model_object(); + m_parent.set_as_dirty(); + } + else { + // Following would inform the clipper about the mouse click, so it can + // toggle the respective contour as disabled. + //m_c->object_clipper()->pass_mouse_click(pos_world); + } + + return true; + } + return false; +} + +bool GLGizmoCut3D::delete_selected_connectors(CutConnectors& connectors) +{ + if (connectors.empty()) + return false; + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Delete connector"), UndoRedo::SnapshotType::GizmoAction); + + // remove connectors + for (int i = int(connectors.size()) - 1; i >= 0; i--) + if (m_selected[i]) + connectors.erase(connectors.begin() + i); + // remove selections + m_selected.erase(std::remove_if(m_selected.begin(), m_selected.end(), [](const auto& selected) { + return selected; }), m_selected.end()); + + assert(m_selected.size() == connectors.size()); + update_model_object(); + m_parent.set_as_dirty(); + return true; +} + +void GLGizmoCut3D::select_connector(int idx, bool select) +{ + m_selected[idx] = select; + if (select) + ++m_selected_count; + else + --m_selected_count; +} + +bool GLGizmoCut3D::is_selection_changed(bool alt_down, bool control_down) +{ + if (m_hover_id >= m_connectors_group_id) { + if (alt_down) + select_connector(m_hover_id - m_connectors_group_id, false); + else { + if (!control_down) + unselect_all_connectors(); + select_connector(m_hover_id - m_connectors_group_id, true); + } + return true; + } + return false; +} + bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) { if (is_dragging() || m_connector_mode == CutConnectorMode::Auto || (!m_keep_upper || !m_keep_lower)) @@ -1920,76 +2011,33 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi if (action == SLAGizmoEventType::LeftDown && !shift_down) { // If there is no selection and no hovering, add new point - if (m_hover_id == -1 && !control_down && !alt_down) { - std::pair pos_and_normal; - Vec3d pos_world; - if (unproject_on_cut_plane(mouse_position.cast(), pos_and_normal, pos_world)) { - const Vec3d& hit = pos_and_normal.first; - - if (m_connectors_editing) { - - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); - - connectors.emplace_back(hit, Transformation(m_rotation_m).get_rotation(), - float(m_connector_size * 0.5), float(m_connector_depth_ratio), - float(0.01f * m_connector_size_tolerance), float(0.01f * m_connector_depth_ratio_tolerance), - CutConnectorAttributes( CutConnectorType(m_connector_type), - CutConnectorStyle(m_connector_style), - CutConnectorShape(m_connector_shape_id))); - std::fill(m_selected.begin(), m_selected.end(), false); - m_selected.push_back(true); - assert(m_selected.size() == connectors.size()); - update_model_object(); - m_parent.set_as_dirty(); - } else { - // Following would inform the clipper about the mouse click, so it can - // toggle the respective contour as disabled. - //m_c->object_clipper()->pass_mouse_click(pos_world); - } - - return true; - } - return false; - } + if (m_hover_id == -1 && !control_down && !alt_down) + return add_connector(connectors, mouse_position); return true; } - else if (action == SLAGizmoEventType::LeftUp && !shift_down && m_connectors_editing) { - if (m_hover_id >= m_connectors_group_id) { - if (alt_down) { - m_selected[m_hover_id - m_connectors_group_id] = false; - --m_selected_count; - } - else { - if (!control_down) { - std::fill(m_selected.begin(), m_selected.end(), false); - m_selected_count = 0; - } - m_selected[m_hover_id - m_connectors_group_id] = true; - ++m_selected_count; - } - return true; - } - } - else if (action == SLAGizmoEventType::RightDown && !shift_down && m_connectors_editing) { + if (!m_connectors_editing) + return false; + + if (action == SLAGizmoEventType::LeftUp && !shift_down) + return is_selection_changed(alt_down, control_down); + + if (action == SLAGizmoEventType::RightDown && !shift_down) { // If any point is in hover state, this should initiate its move - return control back to GLCanvas: if (m_hover_id < m_connectors_group_id) return false; + unselect_all_connectors(); + select_connector(m_hover_id - m_connectors_group_id, true); + return delete_selected_connectors(connectors); + } + + if (action == SLAGizmoEventType::Delete) + return delete_selected_connectors(connectors); - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Delete connector"), UndoRedo::SnapshotType::GizmoAction); - - size_t connector_id = m_hover_id - m_connectors_group_id; - connectors.erase(connectors.begin() + connector_id); - m_selected.erase(m_selected.begin() + connector_id); - assert(m_selected.size() == connectors.size()); - update_model_object(); - m_parent.set_as_dirty(); - - return true; - } - else if (action == SLAGizmoEventType::SelectAll) { - std::fill(m_selected.begin(), m_selected.end(), true); + if (action == SLAGizmoEventType::SelectAll) { + select_all_connectors(); return true; } + return false; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 11c7b1abd..ac3113a0a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -155,26 +155,40 @@ public: BoundingBoxf3 transformed_bounding_box(bool revert_move = false) const; protected: - bool on_init() override; - void on_load(cereal::BinaryInputArchive& ar) override; - void on_save(cereal::BinaryOutputArchive& ar) const override; - std::string on_get_name() const override; - void on_set_state() override; + bool on_init() override; + void on_load(cereal::BinaryInputArchive&ar) override; + void on_save(cereal::BinaryOutputArchive&ar) const override; + std::string on_get_name() const override; + void on_set_state() override; CommonGizmosDataID on_get_requirements() const override; - void on_set_hover_id() override; - bool on_is_activable() const override; - Vec3d mouse_position_in_local_plane(Axis axis, const Linef3& mouse_ray) const; - void on_dragging(const UpdateData& data) override; - void on_start_dragging() override; - void on_stop_dragging() override; - void on_render() override; + void on_set_hover_id() override; + bool on_is_activable() const override; + Vec3d mouse_position_in_local_plane(Axis 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); + void on_dragging(const UpdateData&data) override; + void on_start_dragging() override; + void on_stop_dragging() override; + void on_render() override; - void render_debug_block(); + void render_debug_input_window(); void adjust_window_position(float x, float y, float bottom_limit); - void render_connectors_editing(CutConnectors &connectors); - void render_cut_plane_editing(CutConnectors &connectors); + void unselect_all_connectors(); + void select_all_connectors(); + void render_shortcuts(); + void apply_selected_connectors(std::function apply_fn); + void render_connectors_input_window(CutConnectors &connectors); + void render_build_size(); + void reset_cut_plane(); + void set_connectors_editing(bool connectors_editing); + void render_cut_plane_input_window(CutConnectors &connectors); void init_input_window_data(CutConnectors &connectors); void render_input_window_warning() const; + bool add_connector(CutConnectors&connectors, const Vec2d&mouse_position); + bool delete_selected_connectors(CutConnectors&connectors); + void select_connector(int idx, bool select); + bool is_selection_changed(bool alt_down, bool control_down); virtual void on_register_raycasters_for_picking() override; virtual void on_unregister_raycasters_for_picking() override; @@ -213,6 +227,7 @@ private: bool update_bb(); void init_picking_models(); void init_rendering_items(); + void render_clipper_cut(); void reset_connectors(); void update_connector_shape(); void update_model_object(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 024b23819..3946e54e1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -551,7 +551,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) case WXK_BACK: case WXK_DELETE: { - if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::Delete)) + if ((m_current == SlaSupports || m_current == Hollow || m_current == Cut) && gizmo_event(SLAGizmoEventType::Delete)) processed = true; break;