diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 6c42a8cd19..c5c9f798b6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -107,6 +107,7 @@ void GLGizmoFdmSupports::on_render() const render_triangles(selection); render_clipping_plane(selection); + render_cursor_circle(); glsafe(::glDisable(GL_BLEND)); } @@ -122,7 +123,9 @@ void GLGizmoFdmSupports::render_triangles(const Selection& selection) const ::glColor3f(0.0f, 0.37f, 1.0f); - for (size_t facet_idx : m_selected_facets) { + for (size_t facet_idx=0; facet_idxits, facet_idx); ::glPushMatrix(); ::glTranslatef(normal(0), normal(1), normal(2)); @@ -135,9 +138,6 @@ void GLGizmoFdmSupports::render_triangles(const Selection& selection) const ::glEnd(); ::glPopMatrix(); } - - - } void GLGizmoFdmSupports::render_clipping_plane(const Selection& selection) const @@ -170,6 +170,53 @@ void GLGizmoFdmSupports::render_clipping_plane(const Selection& selection) const } } +void GLGizmoFdmSupports::render_cursor_circle() const +{ + const Camera& camera = m_parent.get_camera(); + float zoom = (float)camera.get_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + std::cout << zoom << " " << inv_zoom << std::endl; + + Size cnv_size = m_parent.get_canvas_size(); + float cnv_half_width = 0.5f * (float)cnv_size.get_width(); + float cnv_half_height = 0.5f * (float)cnv_size.get_height(); + if ((cnv_half_width == 0.0f) || (cnv_half_height == 0.0f)) + return; + Vec2d mouse_pos(m_parent.get_local_mouse_position()(0), m_parent.get_local_mouse_position()(1)); + Vec2d center(mouse_pos(0) - cnv_half_width, cnv_half_height - mouse_pos(1)); + center = center * inv_zoom; + + glsafe(::glLineWidth(1.5f)); + float color[3]; + color[2] = 0.3f; + glsafe(::glColor3fv(color)); + + glsafe(::glDisable(GL_DEPTH_TEST)); + + glsafe(::glPushMatrix()); + glsafe(::glLoadIdentity()); + // ensure that the circle is renderered inside the frustrum + glsafe(::glTranslated(0.0, 0.0, -(camera.get_near_z() + 0.5))); + // ensure that the overlay fits the frustrum near z plane + double gui_scale = camera.get_gui_scale(); + glsafe(::glScaled(gui_scale, gui_scale, 1.0)); + + glsafe(::glPushAttrib(GL_ENABLE_BIT)); + glsafe(::glLineStipple(4, 0xAAAA)); + glsafe(::glEnable(GL_LINE_STIPPLE)); + + ::glBegin(GL_LINE_LOOP); + for (double angle=0; angle<2*M_PI; angle+=M_PI/20.) + ::glVertex2f(GLfloat(center.x()+m_cursor_radius*cos(angle)), GLfloat(center.y()+m_cursor_radius*sin(angle))); + glsafe(::glEnd()); + + glsafe(::glPopAttrib()); + + glsafe(::glPopMatrix()); + +} + void GLGizmoFdmSupports::on_render_for_picking() const { @@ -209,6 +256,20 @@ void GLGizmoFdmSupports::update_mesh() m_mesh = &m_model_object->volumes.front()->mesh(); m_its = &m_mesh->its; + m_selected_facets.clear(); + m_selected_facets.resize(m_mesh->its.indices.size()); + + // Prepare vector of vertex_index - facet_index pairs to quickly find adjacent facets + m_neighbors.clear(); + m_neighbors.resize(3 * m_mesh->its.indices.size()); + for (size_t i=0; iits.indices.size(); ++i) { + const stl_triangle_vertex_indices& ind = m_mesh->its.indices[i]; + m_neighbors.emplace_back(ind(0), i); + m_neighbors.emplace_back(ind(1), i); + m_neighbors.emplace_back(ind(2), i); + } + std::sort(m_neighbors.begin(), m_neighbors.end()); + // If this is different mesh than last time or if the AABB tree is uninitialized, recalculate it. if (m_model_object_id != m_model_object->id() || ! m_mesh_raycaster) m_mesh_raycaster.reset(new MeshRaycaster(*m_mesh)); @@ -218,9 +279,10 @@ void GLGizmoFdmSupports::update_mesh() -// 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 GLGizmoFdmSupports::unproject_on_mesh(const Vec2d& mouse_pos, size_t& facet_idx) +// Unprojects the mouse position on the mesh and saves hit facet index into facet_idx +// Position of the hit in mesh coords is copied into *position, if provided. +// Returns false if no intersection was found, true otherwise. +bool GLGizmoFdmSupports::unproject_on_mesh(const Vec2d& mouse_pos, size_t& facet_idx, Vec3f* position) { // if the gizmo doesn't have the V, F structures for igl, calculate them first: if (! m_mesh_raycaster) @@ -235,12 +297,21 @@ bool GLGizmoFdmSupports::unproject_on_mesh(const Vec2d& mouse_pos, size_t& facet // The raycaster query Vec3f hit; Vec3f normal; - if (m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get(), &facet_idx)) + if (m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get(), &facet_idx)) { + if (position) + *position = hit; return true; + } else return false; } +bool operator<(const GLGizmoFdmSupports::NeighborData& a, const GLGizmoFdmSupports::NeighborData& b) { + return a.first < b.first; +} + + + // Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event. // The gizmo has an opportunity to react - if it does, it should return true so that the Canvas3D is // aware that the event was reacted to and stops trying to make different sense of it. If the gizmo @@ -266,9 +337,51 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous if (action == SLAGizmoEventType::LeftDown || (action == SLAGizmoEventType::Dragging && m_wait_for_up_event)) { size_t facet_idx = 0; - if (unproject_on_mesh(mouse_position, facet_idx)) { - m_selected_facets.push_back(facet_idx); + Vec3f hit_pos; + if (unproject_on_mesh(mouse_position, facet_idx, &hit_pos)) { + bool select = ! shift_down; + + // Calculate direction from camera to the hit (in mesh coords): + const Selection& selection = m_parent.get_selection(); + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + Geometry::Transformation trafo = volume->get_instance_transformation(); + trafo.set_offset(trafo.get_offset()); + Vec3f dir = ((trafo.get_matrix().inverse() * m_parent.get_camera().get_position()).cast() - hit_pos).normalized(); + + // Calculate how far can a point be from the line (in mesh coords). + // FIXME: This should account for (possibly non-uniform) scaling of the mesh. + float limit = pow(m_cursor_radius, 2.f); + + + // A lambda to calculate distance from the line: + auto squared_distance_from_line = [&hit_pos, &dir](const Vec3f point) -> float { + Vec3f diff = hit_pos - point; + return (diff - diff.dot(dir) * dir).squaredNorm(); + }; + + // Now go through the facets and de/select those close enough to the line (FIXME: efficiency) + for (size_t i=0; iits.vertices[m_mesh->its.indices[i](0)]); + if (dist < limit) + m_selected_facets[i] = select; + } + + + + //size_t vertex_idx = m_mesh->its.indices[facet_idx](0); + //auto it = m_neighbors.begin(); + //std::vector visited(m_mesh->its.indices.size(), false); + + //while (it != m_neighbors.end() && it->second == facet_idx) + // it = std::lower_bound(it, m_neighbors.end(), std::make_pair(vertex_idx, size_t(0))); + + //facet_idx = it->second; + //vertex_idx = m_mesh->its.indices[facet_idx](0); + + + m_wait_for_up_event = true; + m_parent.set_as_dirty(); return true; } if (action == SLAGizmoEventType::Dragging && m_wait_for_up_event) @@ -294,8 +407,6 @@ ClippingPlane GLGizmoFdmSupports::get_fdm_clipping_plane() const return ClippingPlane(-m_clipping_plane->get_normal(), m_clipping_plane->get_data()[3]); } - - void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_limit) { if (!m_model_object) @@ -337,6 +448,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l if (ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f")) update_clipping_plane(true); + ImGui::SliderFloat(" ", &m_cursor_radius, 0.f, 8.f, "%.2f"); m_imgui->end(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index 1b1cfd1348..fadb993c82 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -21,7 +21,7 @@ private: ObjectID m_model_object_id = 0; int m_active_instance = -1; float m_active_instance_bb_radius; // to cache the bb - bool unproject_on_mesh(const Vec2d& mouse_pos, size_t& facet_idx); + bool unproject_on_mesh(const Vec2d& mouse_pos, size_t& facet_idx, Vec3f* position = nullptr); GLUquadricObj* m_quadric; @@ -30,9 +30,9 @@ private: const TriangleMesh* m_mesh; const indexed_triangle_set* m_its; mutable std::vector m_triangles; + float m_cursor_radius = 2.f; - std::vector m_selected_facets; - + std::vector m_selected_facets; public: GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); @@ -40,6 +40,7 @@ public: void set_fdm_support_data(ModelObject* model_object, const Selection& selection); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); ClippingPlane get_fdm_clipping_plane() const; + using NeighborData = std::pair; private: @@ -49,6 +50,7 @@ private: void render_triangles(const Selection& selection) const; void render_clipping_plane(const Selection& selection) const; + void render_cursor_circle() const; bool is_mesh_update_necessary() const; void update_mesh(); @@ -64,6 +66,8 @@ private: mutable std::unique_ptr m_object_clipper; + std::vector m_neighbors; // pairs of vertex_index - facet_index + bool is_point_clipped(const Vec3d& point) const; void update_clipping_plane(bool keep_normal = false) const; diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 7e886a486d..6c4f5e49d5 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -85,11 +85,11 @@ void MeshClipper::recalculate_triangles() tr = m_trafo.get_matrix().cast() * tr; m_triangles3d.clear(); - m_triangles3d.reserve(m_triangles2d.size()); - for (const Vec2f& pt : m_triangles2d) { - m_triangles3d.push_back(Vec3f(pt(0), pt(1), height_mesh+0.001f)); - m_triangles3d.back() = tr * m_triangles3d.back(); - } + m_triangles3d.reserve(m_triangles2d.size()); + for (const Vec2f& pt : m_triangles2d) { + m_triangles3d.push_back(Vec3f(pt(0), pt(1), height_mesh+0.001f)); + m_triangles3d.back() = tr * m_triangles3d.back(); + } m_triangles_valid = true; } @@ -102,9 +102,8 @@ Vec3f MeshRaycaster::get_triangle_normal(const indexed_triangle_set& its, size_t return Vec3f(a.cross(b)).normalized(); } -bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, - Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane, - size_t* facet_idx) const +void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, + Vec3d& point, Vec3d& direction) const { const std::array& viewport = camera.get_viewport(); const Transform3d& model_mat = camera.get_view_matrix(); diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 3c9580e597..9d37b40ae3 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -113,6 +113,9 @@ public: : m_emesh(mesh) {} + void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, + Vec3d& point, Vec3d& direction) const; + // Given a mouse position, this returns true in case it is on the mesh. bool unproject_on_mesh( const Vec2d& mouse_pos,