From 0fba32fa5327e6ef7a3708e87e0ebb82cebbf56a Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 8 Mar 2022 14:10:25 +0100 Subject: [PATCH] Cut: Add connectors. WIP --- src/libslic3r/Model.hpp | 43 +++ src/slic3r/GUI/Gizmos/GLGizmoBase.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 368 ++++++++++++++++++++++---- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 21 +- 4 files changed, 375 insertions(+), 59 deletions(-) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 3d933c4702..2e96910f48 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -219,6 +219,46 @@ private: friend class ModelObject; }; +struct CutConnector +{ + Vec3f pos; + Vec3f normal; + float radius; + float height; + bool failed = false; + + CutConnector() + : pos(Vec3f::Zero()), normal(Vec3f::UnitZ()), radius(5.f), height(10.f) + {} + + CutConnector(Vec3f p, Vec3f n, float r, float h, bool fl = false) + : pos(p), normal(n), radius(r), height(h), failed(fl) + {} + + CutConnector(const CutConnector& rhs) : + CutConnector(rhs.pos, rhs.normal, rhs.radius, rhs.height, rhs.failed) {} + + bool operator==(const CutConnector& sp) const; + + bool operator!=(const CutConnector& sp) const { return !(sp == (*this)); } +/* + bool is_inside(const Vec3f& pt) const; + + bool get_intersections(const Vec3f& s, const Vec3f& dir, + std::array, 2>& out) const; + + indexed_triangle_set to_mesh() const; +*/ + template inline void serialize(Archive& ar) + { + ar(pos, normal, radius, height, failed); + } + + static constexpr size_t steps = 32; +}; + +using CutConnectors = std::vector; + // Declared outside of ModelVolume, so it could be forward declared. enum class ModelVolumeType : int { INVALID = -1, @@ -269,6 +309,9 @@ public: // Holes to be drilled into the object so resin can flow out sla::DrainHoles sla_drain_holes; + // Connectors to be added into the object after cut + CutConnectors cut_connectors; + /* This vector accumulates the total translation applied to the object by the center_around_origin() method. Callers might want to apply the same translation to new volumes before adding them to this object in order to preserve alignment diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index d503c024ea..b188d60af2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -171,7 +171,7 @@ bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) { if (!m_grabbers.empty()) { if (m_hover_id < int(m_grabbers.size())) m_grabbers[m_hover_id].dragging = true; - else if (m_group_id >= 0 && m_hover_id >= m_group_id) + else if (m_group_id >= 0 && m_hover_id < int(m_grabbers.size() + m_group_id)) m_grabbers[m_hover_id - m_group_id].dragging = true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 3dd37ad39a..90084b0668 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -33,6 +33,7 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, { m_rotation_gizmo.use_only_grabbers(); m_group_id = 3; + m_connectors_group_id = 4; m_modes = { _u8L("Planar"), _u8L("By Line"),_u8L("Grid") // , _u8L("Radial"), _u8L("Modular") @@ -50,6 +51,8 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, }; m_axis_names = { "X", "Y", "Z" }; + + update_connector_shape(); } std::string GLGizmoCut3D::get_tooltip() const @@ -68,11 +71,71 @@ std::string GLGizmoCut3D::get_tooltip() const bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) { + if (mouse_event.Moving()) + return false; + if (m_rotation_gizmo.on_mouse(mouse_event)) { update_clipper(); return true; } - return use_grabbers(mouse_event); + + if (use_grabbers(mouse_event)) + return true; + + Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); + Vec2d mouse_pos = mouse_coord.cast(); + + static bool pending_right_up = false; + if (mouse_event.LeftDown()) { + bool grabber_contains_mouse = (get_hover_id() != -1); + bool control_down = mouse_event.CmdDown(); + if ((!control_down || grabber_contains_mouse) && + gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false)) + return true; + } + else if (mouse_event.Dragging()) { + bool control_down = mouse_event.CmdDown(); + if (m_parent.get_move_volume_id() != -1) { + // don't allow dragging objects with the Sla gizmo on + return true; + } + else if (!control_down && + gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false)) { + // the gizmo got the event and took some action, no need to do + // anything more here + m_parent.set_as_dirty(); + return true; + } + else if (control_down && (mouse_event.LeftIsDown() || mouse_event.RightIsDown())) { + // CTRL has been pressed while already dragging -> stop current action + if (mouse_event.LeftIsDown()) + gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), true); + else if (mouse_event.RightIsDown()) + pending_right_up = false; + } + } + else if (mouse_event.LeftUp() && !m_parent.is_mouse_dragging()) { + // in case SLA/FDM gizmo is selected, we just pass the LeftUp event + // and stop processing - neither object moving or selecting is + // suppressed in that case + gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); + return true; + } + else if (mouse_event.RightDown()) { + if (m_parent.get_selection().get_object_idx() != -1 && + gizmo_event(SLAGizmoEventType::RightDown, mouse_pos, false, false, false)) { + // we need to set the following right up as processed to avoid showing + // the context menu if the user release the mouse over the object + pending_right_up = true; + // event was taken care of by the SlaSupports gizmo + return true; + } + } + else if (pending_right_up && mouse_event.RightUp()) { + pending_right_up = false; + return true; + } + return false; } void GLGizmoCut3D::shift_cut_z(double delta) @@ -128,7 +191,7 @@ void GLGizmoCut3D::set_center(const Vec3d& center) update_clipper(); } -void GLGizmoCut3D::render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx) +bool GLGizmoCut3D::render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx) { ImGui::AlignTextToFramePadding(); m_imgui->text(label); @@ -163,10 +226,13 @@ void GLGizmoCut3D::render_combo(const std::string& label, const std::vectortext(label); @@ -176,12 +242,14 @@ void GLGizmoCut3D::render_double_input(const std::string& label, double& value_i double value = value_in; if (m_imperial_units) value *= ObjectManipulation::mm_to_in; + double old_val = value; ImGui::InputDouble(("##" + label).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); ImGui::SameLine(); m_imgui->text(m_imperial_units ? _L("in") : _L("mm")); value_in = value * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0); + return old_val != value; } void GLGizmoCut3D::render_move_center_input(int axis) @@ -271,11 +339,6 @@ bool GLGizmoCut3D::render_revert_button(const std::string& label_id) void GLGizmoCut3D::render_cut_plane() { - m_c->object_clipper()->render_cut(); - - if (m_hide_cut_plane) - return; - const BoundingBoxf3 box = bounding_box(); const float min_x = box.min.x() - Margin - m_plane_center.x(); @@ -349,7 +412,7 @@ void GLGizmoCut3D::render_cut_center_graber() const BoundingBoxf3 box = bounding_box(); Vec3d grabber_center = m_plane_center; - grabber_center[Z] += 10; // Margin + grabber_center[Z] += float(box.radius()/2.0); // Margin rotate_vec3d_around_center(grabber_center, angles, m_plane_center); @@ -439,31 +502,50 @@ bool GLGizmoCut3D::on_is_activable() const void GLGizmoCut3D::on_dragging(const UpdateData& data) { - assert(m_hover_id == m_group_id); + if (m_hover_id < m_group_id) + return; - const Vec3d & starting_box_center = m_plane_center; - const Vec3d & starting_drag_position = m_grabbers[0].center; - double projection = 0.0; + CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; - 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; + if (m_hover_id == m_group_id) { + const Vec3d& starting_box_center = m_plane_center; + const Vec3d& starting_drag_position = m_grabbers[0].center; + double projection = 0.0; - starting_vec.normalize(); - // finds projection of the vector along the staring direction - projection = inters_vec.dot(starting_vec); + 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); + + // move cut plane center + set_center(starting_box_center + starting_vec * projection); + + // move connectors + Vec3f shift = Vec3f(starting_vec.cast() * projection); + for (auto& connector : connectors) + connector.pos += shift; + } + else if (m_hover_id > m_group_id) + { + std::pair pos_and_normal; + if (!unproject_on_cut_plane(data.mouse_pos.cast(), pos_and_normal)) + return; + connectors[m_hover_id - m_connectors_group_id].pos = pos_and_normal.first; + connectors[m_hover_id - m_connectors_group_id].normal = -pos_and_normal.second; } - if (wxGetKeyState(WXK_SHIFT)) - projection = m_snap_step * (double)std::round(projection / m_snap_step); - - set_center(starting_box_center + starting_vec * projection); } void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos) @@ -474,7 +556,7 @@ void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos) // Clamp the center position of the cut plane to the object's bounding box //m_plane_center = Vec3d(std::clamp(center_pos.x(), m_min_pos.x(), m_max_pos.x()), // std::clamp(center_pos.y(), m_min_pos.y(), m_max_pos.y()), - // std::clamp(center_pos.z(), m_min_pos.z(), m_max_pos.z()))); + // std::clamp(center_pos.z(), m_min_pos.z(), m_max_pos.z())); m_center_offset = m_plane_center - m_bb_center; } @@ -513,11 +595,17 @@ void GLGizmoCut3D::on_render() update_clipper_on_render(); } - render_cut_plane(); - render_cut_center_graber(); - if (m_mode == CutMode::cutPlanar) { - if (m_hover_id < m_group_id) - m_rotation_gizmo.render(); + render_connectors(false); + + m_c->object_clipper()->render_cut(); + + if (!m_hide_cut_plane) { + render_cut_plane(); + render_cut_center_graber(); + if (m_mode == CutMode::cutPlanar) { + if (m_hover_id < m_group_id) + m_rotation_gizmo.render(); + } } if (!suppress_update_clipper_on_render) @@ -528,6 +616,8 @@ void GLGizmoCut3D::on_render_for_picking() { m_rotation_gizmo.render_for_picking(); render_grabbers_for_picking(m_parent.get_selection().get_bounding_box()); + + render_connectors(true); } void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) @@ -617,11 +707,23 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) render_connect_type_radio_button(ConnectorType::Plug); render_connect_type_radio_button(ConnectorType::Dowel); - render_combo(_u8L("Style"), m_connector_styles, m_connector_style); - render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape); + if (render_combo(_u8L("Style"), m_connector_styles, m_connector_style)) + update_connector_shape(); + if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id)) + update_connector_shape(); - render_double_input(_u8L("Depth ratio"), m_connector_depth_ratio); - render_double_input(_u8L("Size"), m_connector_size); + CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; + if (render_double_input(_u8L("Depth ratio"), m_connector_depth_ratio)) + for (auto& connector : connectors) + connector.height = float(m_connector_depth_ratio); + if (render_double_input(_u8L("Size"), m_connector_size)) + for (auto& connector : connectors) + connector.radius = float(m_connector_size * 0.5); + + m_imgui->disabled_begin((!m_keep_upper && !m_keep_lower) || !can_perform_cut()); + if (m_imgui->button(_L("Reset connectors"))) + reset_connectors(); + m_imgui->disabled_end(); m_imgui->disabled_end(); @@ -633,7 +735,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) ImGui::Separator(); - m_imgui->checkbox("hide_cut_plane", m_hide_cut_plane); + m_imgui->checkbox(_L("Hide cut plane and grabbers"), m_hide_cut_plane); //////// static bool hide_clipped = true; @@ -658,6 +760,87 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) } } +void GLGizmoCut3D::render_connectors(bool picking) +{ + const Selection& selection = m_parent.get_selection(); + +#if ENABLE_GLBEGIN_GLEND_REMOVAL + GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat") : wxGetApp().get_shader("gouraud_light"); + if (shader == nullptr) + return; + + shader->start_using(); + ScopeGuard guard([shader]() { shader->stop_using(); }); +#else + GLShaderProgram* shader = picking ? nullptr : wxGetApp().get_shader("gouraud_light"); + if (shader) + shader->start_using(); + ScopeGuard guard([shader]() { if (shader) shader->stop_using(); }); +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL + + const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); + //const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); + //const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix(); + + glsafe(::glPushMatrix()); + glsafe(::glTranslated(0.0, 0.0, m_c->selection_info()->get_sla_shift())); +// glsafe(::glMultMatrixd(instance_matrix.data())); + + ColorRGBA render_color; + const CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; + size_t cache_size = connectors.size(); + + for (size_t i = 0; i < cache_size; ++i) { + const CutConnector& connector = connectors[i]; + const bool& point_selected = m_selected[i]; + + // First decide about the color of the point. + if (picking) + render_color = picking_decode(BASE_ID - i - m_connectors_group_id); + else { + if (size_t(m_hover_id- m_connectors_group_id) == i) + render_color = ColorRGBA::CYAN(); + else // neither hover nor picking + render_color = point_selected ? ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f) : ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f); + } + +#if ENABLE_GLBEGIN_GLEND_REMOVAL + m_connector_shape.set_color(render_color); +#else + const_cast(&m_connector_shape)->set_color(-1, render_color); +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL + + // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. + glsafe(::glPushMatrix()); +// glsafe(::glTranslatef(connector.pos.x() - m_plane_center.x(), connector.pos.y() - m_plane_center.y(), connector.pos.z() - m_plane_center.z())); + glsafe(::glTranslatef(connector.pos.x(), connector.pos.y(), connector.pos.z())); +// glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); + + if (vol->is_left_handed()) + glFrontFace(GL_CW); + + const Vec3d& angles = m_rotation_gizmo.get_rotation(); + glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0)); + glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); + glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); + + // Matrices set, we can render the point mark now. + /* Eigen::Quaterniond q; + q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * (-connector.normal).cast()); + Eigen::AngleAxisd aa(q); + glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis().x(), aa.axis().y(), aa.axis().z())); +*/ glsafe(::glTranslated(0., 0., -0.5*connector.height)); + glsafe(::glScaled(connector.radius, connector.radius, connector.height)); + m_connector_shape.render(); + + if (vol->is_left_handed()) + glFrontFace(GL_CCW); + glsafe(::glPopMatrix()); + } + + glsafe(::glPopMatrix()); +} + bool GLGizmoCut3D::can_perform_cut() const { BoundingBoxf3 box = bounding_box(); @@ -684,46 +867,125 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) Vec3d cut_center_offset = m_plane_center - instance_offset; cut_center_offset[Z] -= first_glvolume->get_sla_shift_z(); - if (0.0 < object_cut_z && can_perform_cut()) + if (0.0 < object_cut_z && can_perform_cut()) { wxGetApp().plater()->cut(object_idx, instance_idx, cut_center_offset, m_rotation_gizmo.get_rotation(), only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(m_keep_lower, ModelObjectCutAttribute::KeepLower) | only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower)); + m_selected.clear(); + } else { // the object is SLA-elevated and the plane is under it. } } -bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) + + +// Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal +// Return false if no intersection was found, true otherwise. +bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair& pos_and_normal) { - if (is_dragging()) + if (!m_c->raycaster()->raycaster()) return false; - const ModelObject *mo = m_c->selection_info()->model_object(); - const ModelInstance *mi = mo->instances[m_c->selection_info()->get_active_instance()]; - const Transform3d instance_trafo = mi->get_transformation().get_matrix(); + + const ModelObject* mo = m_c->selection_info()->model_object(); + const ModelInstance* mi = mo->instances[m_c->selection_info()->get_active_instance()]; + const Transform3d instance_trafo = mi->get_transformation().get_matrix(); const Transform3d instance_trafo_not_translate = mi->get_transformation().get_matrix(true); const Camera& camera = wxGetApp().plater()->get_camera(); int mesh_id = -1; - for (const ModelVolume *mv : mo->volumes) { + for (const ModelVolume* mv : mo->volumes) { ++mesh_id; - if (! mv->is_model_part()) + if (!mv->is_model_part()) continue; Vec3f hit; Vec3f normal; bool clipping_plane_was_hit = false; m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(mouse_position, instance_trafo * mv->get_matrix(), - camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), - nullptr, &clipping_plane_was_hit); + camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), + nullptr, &clipping_plane_was_hit); if (clipping_plane_was_hit) { - // The clipping plane was clicked, hit containts coordinates of the hit in world coords. - std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl; + // Return both the point and the facet normal. + pos_and_normal = std::make_pair(hit, normal); return true; } } return false; } +void GLGizmoCut3D::reset_connectors() +{ + m_c->selection_info()->model_object()->cut_connectors.clear(); + m_selected.clear(); +} + +void GLGizmoCut3D::update_connector_shape() +{ + if (m_connector_shape.is_initialized()) + m_connector_shape.reset(); + + bool is_prizm = m_connector_style == size_t(Prizm); + const std::function& its_make_shape = is_prizm ? its_make_cylinder : its_make_cone; + + + switch (ConnectorShape(m_connector_shape_id)) { + case Triangle: + m_connector_shape.init_from(its_make_shape(1.0, 1.0, (2 * PI / 3))); + break; + case Square: + m_connector_shape.init_from(its_make_shape(1.0, 1.0, (2 * PI / 4))); + break; + case Circle: + m_connector_shape.init_from(its_make_shape(1.0, 1.0, 2 * PI / 360)); + break; + case Hexagon: + m_connector_shape.init_from(its_make_shape(1.0, 1.0, (2 * PI / 6))); + break; + } +} + +bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) +{ + if (is_dragging() || action != SLAGizmoEventType::LeftDown) + return false; + + ModelObject *mo = m_c->selection_info()->model_object(); + const Camera& camera = wxGetApp().plater()->get_camera(); + + int mesh_id = -1; + + // left down without selection rectangle - place point on the mesh: + if (action == SLAGizmoEventType::LeftDown && /*!m_selection_rectangle.is_dragging() && */!shift_down) { + // If any point is in hover state, this should initiate its move - return control back to GLCanvas: + if (m_hover_id != -1) + return false; + + // If there is some selection, don't add new point and deselect everything instead. + if (m_selection_empty) { + std::pair pos_and_normal; + if (unproject_on_cut_plane(mouse_position.cast(), pos_and_normal)) { + const Vec3f& hit = pos_and_normal.first; + const Vec3f& normal = pos_and_normal.second; + // The clipping plane was clicked, hit containts coordinates of the hit in world coords. + std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl; + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add pin")); + + mo->cut_connectors.emplace_back(hit, -normal, float(m_connector_size * 0.5), float(m_connector_depth_ratio)); + m_selected.push_back(false); + assert(m_selected.size() == mo->cut_connectors.size()); + m_parent.set_as_dirty(); + m_wait_for_up_event = true; + + return true; + } + return false; + } + return true; + } + return false; +} + CommonGizmosDataID GLGizmoCut3D::on_get_requirements() const { return CommonGizmosDataID( int(CommonGizmosDataID::SelectionInfo) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 995d1f82d1..e116a6df14 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -18,6 +18,7 @@ class GLGizmoCut3D : public GLGizmoBase { GLGizmoRotate3D m_rotation_gizmo; double m_snap_step{ 1.0 }; + int m_connectors_group_id; Vec3d m_plane_center{ Vec3d::Zero() }; // data to check position of the cut palne center on gizmo activation @@ -26,6 +27,7 @@ class GLGizmoCut3D : public GLGizmoBase Vec3d m_bb_center{ Vec3d::Zero() }; Vec3d m_center_offset{ Vec3d::Zero() }; + GLModel m_connector_shape; #if ENABLE_GLBEGIN_GLEND_REMOVAL GLModel m_plane; @@ -39,14 +41,18 @@ class GLGizmoCut3D : public GLGizmoBase bool m_hide_cut_plane{ false }; - double m_connector_depth_ratio{ 1.5 }; - double m_connector_size{ 5.0 }; + double m_connector_depth_ratio{ 5.0 }; + double m_connector_size{ 2.0 }; float m_label_width{ 150.0 }; float m_control_width{ 200.0 }; bool m_imperial_units{ false }; bool suppress_update_clipper_on_render{false}; + mutable std::vector m_selected; // which pins are currently selected + bool m_selection_empty = true; + bool m_wait_for_up_event = false; + Matrix3d m_rotation_matrix; Vec3d m_rotations{ Vec3d::Zero() }; @@ -95,7 +101,7 @@ class GLGizmoCut3D : public GLGizmoBase size_t m_connector_style{ size_t(Prizm) }; std::vector m_connector_shapes; - size_t m_connector_shape{ size_t(Hexagon) }; + size_t m_connector_shape_id{ size_t(Hexagon) }; std::vector m_axis_names; @@ -103,6 +109,7 @@ public: GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); std::string get_tooltip() const override; + bool unproject_on_cut_plane(const Vec2d& mouse_pos, std::pair& pos_and_normal); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); /// @@ -136,13 +143,15 @@ protected: private: void set_center(const Vec3d& center); - void render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx); - void render_double_input(const std::string& label, double& value_in); + bool render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx); + bool render_double_input(const std::string& label, double& value_in); void render_move_center_input(int axis); void render_rotation_input(int axis); void render_connect_mode_radio_button(ConnectorMode mode); bool render_revert_button(const std::string& label); void render_connect_type_radio_button(ConnectorType type); + void render_connectors(bool picking); + bool can_perform_cut() const; void render_cut_plane(); @@ -150,6 +159,8 @@ private: void perform_cut(const Selection& selection); void set_center_pos(const Vec3d& center_pos); bool update_bb(); + void reset_connectors(); + void update_connector_shape(); }; } // namespace GUI