From 46da54ffaf6c1877de0710f5fcd4a20cda1932ac Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 3 Jun 2022 12:53:02 +0200 Subject: [PATCH 001/103] First implementation of SurfaceMesh --- src/libslic3r/CMakeLists.txt | 1 + src/libslic3r/SurfaceMesh.hpp | 151 ++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 src/libslic3r/SurfaceMesh.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index e637b1402f..396494a181 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -249,6 +249,7 @@ set(SLIC3R_SOURCES Surface.hpp SurfaceCollection.cpp SurfaceCollection.hpp + SurfaceMesh.hpp SVG.cpp SVG.hpp Technologies.hpp diff --git a/src/libslic3r/SurfaceMesh.hpp b/src/libslic3r/SurfaceMesh.hpp new file mode 100644 index 0000000000..47d5ed0dcc --- /dev/null +++ b/src/libslic3r/SurfaceMesh.hpp @@ -0,0 +1,151 @@ +#ifndef slic3r_SurfaceMesh_hpp_ +#define slic3r_SurfaceMesh_hpp_ + +#include + +namespace Slic3r { + +class TriangleMesh; + + + +enum Face_index : int; + +class Halfedge_index { + friend class SurfaceMesh; + +public: + Halfedge_index() : m_face(Face_index(-1)), m_side(0) {} + Face_index face() const { return m_face; } + bool is_invalid() const { return int(m_face) < 0; } + bool operator!=(const Halfedge_index& rhs) const { return ! ((*this) == rhs); } + bool operator==(const Halfedge_index& rhs) const { return m_face == rhs.m_face && m_side == rhs.m_side; } + +private: + Halfedge_index(int face_idx, unsigned char side_idx) : m_face(Face_index(face_idx)), m_side(side_idx) {} + + Face_index m_face; + unsigned char m_side; +}; + + + +class Vertex_index { + friend class SurfaceMesh; + +public: + Vertex_index() : m_face(Face_index(-1)), m_vertex_idx(0) {} + bool is_invalid() const { return int(m_face) < 0; } + bool operator==(const Vertex_index& rhs) const = delete; // Use SurfaceMesh::is_same_vertex. + +private: + Vertex_index(int face_idx, unsigned char vertex_idx) : m_face(Face_index(face_idx)), m_vertex_idx(vertex_idx) {} + + Face_index m_face; + unsigned char m_vertex_idx; +}; + + + +class SurfaceMesh { +public: + explicit SurfaceMesh(const indexed_triangle_set& its) + : m_its(its), + m_face_neighbors(its_face_neighbors_par(its)) + {} + SurfaceMesh(const SurfaceMesh&) = delete; + SurfaceMesh& operator=(const SurfaceMesh&) = delete; + + Vertex_index source(Halfedge_index h) const { assert(! h.is_invalid()); return Vertex_index(h.m_face, h.m_side); } + Vertex_index target(Halfedge_index h) const { assert(! h.is_invalid()); return Vertex_index(h.m_face, h.m_side == 2 ? 0 : h.m_side + 1); } + Face_index face(Halfedge_index h) const { assert(! h.is_invalid()); return h.m_face; } + + Halfedge_index next(Halfedge_index h) const { assert(! h.is_invalid()); h.m_side = (h.m_side + 1) % 3; return h; } + Halfedge_index prev(Halfedge_index h) const { assert(! h.is_invalid()); h.m_side = (h.m_side == 0 ? 2 : h.m_side - 1); return h; } + Halfedge_index halfedge(Vertex_index v) const { return Halfedge_index(v.m_face, (v.m_vertex_idx == 0 ? 2 : v.m_vertex_idx - 1)); } + Halfedge_index halfedge(Face_index f) const { return Halfedge_index(f, 0); } + Halfedge_index opposite(Halfedge_index h) const { + if (h.is_invalid()) + return h; + + int face_idx = m_face_neighbors[h.m_face][h.m_side]; + Halfedge_index h_candidate = halfedge(Face_index(face_idx)); + + if (h_candidate.is_invalid()) + return Halfedge_index(); // invalid + + for (int i=0; i<3; ++i) { + if (is_same_vertex(source(h_candidate), target(h))) { + // Meshes in PrusaSlicer should be fixed enough for the following not to happen. + assert(is_same_vertex(target(h_candidate), source(h))); + return h_candidate; + } + h_candidate = next(h_candidate); + } + return Halfedge_index(); // invalid + } + + Halfedge_index next_around_target(Halfedge_index h) const { return opposite(next(h)); } + Halfedge_index prev_around_target(Halfedge_index h) const { Halfedge_index op = opposite(h); return (op.is_invalid() ? Halfedge_index() : prev(op)); } + Halfedge_index next_around_source(Halfedge_index h) const { Halfedge_index op = opposite(h); return (op.is_invalid() ? Halfedge_index() : next(op)); } + Halfedge_index prev_around_source(Halfedge_index h) const { return opposite(prev(h)); } + Halfedge_index halfedge(Vertex_index source, Vertex_index target) const + { + Halfedge_index hi(source.m_face, source.m_vertex_idx); + assert(! hi.is_invalid()); + + const Vertex_index orig_target = this->target(hi); + Vertex_index current_target = orig_target; + + while (! is_same_vertex(current_target, target)) { + hi = next_around_source(hi); + if (hi.is_invalid()) + break; + current_target = this->target(hi); + if (is_same_vertex(current_target, orig_target)) + return Halfedge_index(); // invalid + } + + return hi; + } + + const stl_vertex& point(Vertex_index v) const { return m_its.vertices[m_its.indices[v.m_face][v.m_vertex_idx]]; } + + size_t degree(Vertex_index v) const + { + Halfedge_index h_first = halfedge(v); + Halfedge_index h = next_around_target(h_first); + size_t degree = 2; + while (! h.is_invalid() && h != h_first) { + h = next_around_target(h); + ++degree; + } + return h.is_invalid() ? 0 : degree - 1; + } + + size_t degree(Face_index f) const { + size_t total = 0; + for (unsigned char i=0; i<3; ++i) { + size_t d = degree(Vertex_index(f, i)); + if (d == 0) + return 0; + total += d; + } + assert(total - 6 >= 0); + return total - 6; // we counted 3 halfedges from f, and one more for each neighbor + } + + bool is_border(Halfedge_index h) const { return m_face_neighbors[h.m_face][h.m_side] == -1; } + + bool is_same_vertex(const Vertex_index& a, const Vertex_index& b) const { return m_its.indices[a.m_face][a.m_vertex_idx] == m_its.indices[b.m_face][b.m_vertex_idx]; } + + + +private: + const std::vector m_face_neighbors; + const indexed_triangle_set& m_its; +}; + +} //namespace Slic3r + +#endif // slic3r_SurfaceMesh_hpp_ From 9a163c356b713a00600ece17f76fa5805e205198 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 3 Jun 2022 12:53:34 +0200 Subject: [PATCH 002/103] Added some unit tests (SurfaceMesh) --- tests/libslic3r/CMakeLists.txt | 1 + tests/libslic3r/test_surface_mesh.cpp | 122 ++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 tests/libslic3r/test_surface_mesh.cpp diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index cf89b2246f..3622ac2b8e 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -24,6 +24,7 @@ add_executable(${_TEST_NAME}_tests test_voronoi.cpp test_optimizers.cpp test_png_io.cpp + test_surface_mesh.cpp test_timeutils.cpp test_indexed_triangle_set.cpp test_astar.cpp diff --git a/tests/libslic3r/test_surface_mesh.cpp b/tests/libslic3r/test_surface_mesh.cpp new file mode 100644 index 0000000000..34ff356679 --- /dev/null +++ b/tests/libslic3r/test_surface_mesh.cpp @@ -0,0 +1,122 @@ +#include +#include + + +#include + +using namespace Slic3r; + + +// Generate a broken cube mesh. Face 8 is inverted, face 11 is missing. +indexed_triangle_set its_make_cube_broken(double xd, double yd, double zd) +{ + auto x = float(xd), y = float(yd), z = float(zd); + return { + { {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7}, + {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2}, + {2, 5, 6}, {2, 5, 3}, {4, 0, 3} /*missing face*/ }, + { {x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0}, + {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z} } + }; +} + + + +TEST_CASE("SurfaceMesh on a cube", "[SurfaceMesh]") { + indexed_triangle_set cube = its_make_cube(1., 1., 1.); + SurfaceMesh sm(cube); + const Halfedge_index hi_first = sm.halfedge(Face_index(0)); + Halfedge_index hi = hi_first; + + REQUIRE(! hi_first.is_invalid()); + + SECTION("next / prev halfedge") { + hi = sm.next(hi); + REQUIRE(hi != hi_first); + hi = sm.next(hi); + hi = sm.next(hi); + REQUIRE(hi == hi_first); + hi = sm.prev(hi); + REQUIRE(hi != hi_first); + hi = sm.prev(hi); + hi = sm.prev(hi); + REQUIRE(hi == hi_first); + } + + SECTION("next_around_target") { + // Check that we get to the same halfedge after applying next_around_target + // four times. + const Vertex_index target_vert = sm.target(hi_first); + for (int i=0; i<4;++i) { + hi = sm.next_around_target(hi); + REQUIRE((hi == hi_first) == (i == 3)); + REQUIRE(sm.is_same_vertex(sm.target(hi), target_vert)); + REQUIRE(! sm.is_border(hi)); + } + } + + SECTION("iterate around target and source") { + hi = sm.next_around_target(hi); + hi = sm.prev_around_target(hi); + hi = sm.prev_around_source(hi); + hi = sm.next_around_source(hi); + REQUIRE(hi == hi_first); + } + + SECTION("opposite") { + const Vertex_index target = sm.target(hi); + const Vertex_index source = sm.source(hi); + hi = sm.opposite(hi); + REQUIRE(sm.is_same_vertex(target, sm.source(hi))); + REQUIRE(sm.is_same_vertex(source, sm.target(hi))); + hi = sm.opposite(hi); + REQUIRE(hi == hi_first); + } + + SECTION("halfedges walk") { + for (int i=0; i<4; ++i) { + hi = sm.next(hi); + hi = sm.opposite(hi); + } + REQUIRE(hi == hi_first); + } + + SECTION("point accessor") { + Halfedge_index hi = sm.halfedge(Face_index(0)); + hi = sm.opposite(hi); + hi = sm.prev(hi); + hi = sm.opposite(hi); + REQUIRE(hi.face() == Face_index(6)); + REQUIRE(sm.point(sm.target(hi)).isApprox(cube.vertices[7])); + } +} + + + + +TEST_CASE("SurfaceMesh on a broken cube", "[SurfaceMesh]") { + indexed_triangle_set cube = its_make_cube_broken(1., 1., 1.); + SurfaceMesh sm(cube); + + SECTION("Check inverted face") { + Halfedge_index hi = sm.halfedge(Face_index(8)); + for (int i=0; i<3; ++i) { + REQUIRE(! hi.is_invalid()); + REQUIRE(sm.is_border(hi)); + } + REQUIRE(hi == sm.halfedge(Face_index(8))); + hi = sm.opposite(hi); + REQUIRE(hi.is_invalid()); + } + + SECTION("missing face") { + Halfedge_index hi = sm.halfedge(Face_index(0)); + for (int i=0; i<3; ++i) + hi = sm.next_around_source(hi); + hi = sm.next(hi); + REQUIRE(sm.is_border(hi)); + REQUIRE(! hi.is_invalid()); + hi = sm.opposite(hi); + REQUIRE(hi.is_invalid()); + } +} From 1e494e30af62b36c6e6a1ad1196ee1a20ee07957 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 17 Jun 2022 12:19:47 +0200 Subject: [PATCH 003/103] SurfaceMesh testing (to be reverted later) --- src/libslic3r/TriangleMesh.cpp | 11 +- src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp | 253 ++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp | 2 + 3 files changed, 149 insertions(+), 117 deletions(-) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 0dffdaab0e..8dc87c6fb1 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -877,13 +877,20 @@ Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Transfor indexed_triangle_set its_make_cube(double xd, double yd, double zd) { auto x = float(xd), y = float(yd), z = float(zd); - return { + /*return { { {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7}, {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2}, {2, 6, 5}, {2, 5, 3}, {4, 0, 3}, {4, 3, 5} }, { {x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0}, {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z} } - }; + };*/ + return { + { {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7}, + {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2}, + {2, 5, 6}, {2, 5, 3}, {4, 0, 3}, /*{4, 3, 5}*/ }, + { {x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0}, + {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z} } + }; } indexed_triangle_set its_make_prism(float width, float length, float height) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index f854edb817..f8d6abad98 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -12,20 +12,45 @@ #include "libslic3r/Geometry/ConvexHull.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/SurfaceMesh.hpp" #include #include + + namespace Slic3r { namespace GUI { static const Slic3r::ColorRGBA DEFAULT_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.5f }; static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.75f }; + + + +// TESTING: +static Halfedge_index hi; +static bool hi_initialized = false; +static std::unique_ptr sm_ptr; +static Vertex_index src; +static Vertex_index tgt; + + + + + + GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) -{} +{ + indexed_triangle_set a = its_make_cone(0.05, .2); + its_rotate_x(a, M_PI); + its_translate(a, stl_vertex(0., 0., .8)); + indexed_triangle_set b = its_make_cylinder(.02, 0.8); + its_merge(a, b); + arrow.init_from(a); +} bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event) { @@ -39,9 +64,7 @@ bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event) m_mouse_left_down = true; Selection &selection = m_parent.get_selection(); if (selection.is_single_full_instance()) { - // Rotate the object so the normal points downward: - selection.flattening_rotate(m_planes[m_hover_id].normal); - m_parent.do_rotate(L("Gizmo-Place on Face")); + hi = sm_ptr->halfedge(Face_index(m_hover_id)); } return true; } @@ -105,6 +128,19 @@ void GLGizmoFlatten::on_render() const Selection& selection = m_parent.get_selection(); #if ENABLE_LEGACY_OPENGL_REMOVAL + + + + if (! hi_initialized) { + const indexed_triangle_set& its = m_c->selection_info()->model_object()->volumes.front()->mesh().its; + sm_ptr.reset(new SurfaceMesh(its)); + hi = sm_ptr->halfedge(Face_index(0)); + hi_initialized = true; + } + SurfaceMesh& sm = *sm_ptr; + + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); if (shader == nullptr) return; @@ -135,8 +171,12 @@ void GLGizmoFlatten::on_render() update_planes(); for (int i = 0; i < (int)m_planes.size(); ++i) { #if ENABLE_LEGACY_OPENGL_REMOVAL + int cur_face = hi.is_invalid() ? 1000000 : sm.face(hi); + for (int i=0; i < m_planes.size(); ++i) { m_planes[i].vbo.set_color(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); - m_planes[i].vbo.render(); + if (i == cur_face) + m_planes[i].vbo.set_color(i == m_hover_id ? ColorRGBA(.5f, 0.f, 0.f, 1.f) : ColorRGBA(1.f, 0.f, 0.f, 1.f)); + m_planes[i].vbo.render(); #else glsafe(::glColor4fv(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR.data() : DEFAULT_PLANE_COLOR.data())); if (m_planes[i].vbo.has_VBOs()) @@ -146,8 +186,92 @@ void GLGizmoFlatten::on_render() #if !ENABLE_GL_SHADERS_ATTRIBUTES glsafe(::glPopMatrix()); #endif // !ENABLE_GL_SHADERS_ATTRIBUTES + } } + + + + ///////////////// + //////////////// + ////////////////// + + auto draw_arrow = [&](const Vec3d& from, const Vec3d& to) -> void { + Vec3d desired_pos = from; + Vec3d desired_dir = to - from; + double desired_len = desired_dir.norm(); + desired_dir.normalize(); + + Transform3d m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); + m.translate(desired_pos); + Eigen::Quaterniond q; + Transform3d rot = Transform3d::Identity(); + rot.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(Vec3d::UnitZ(), desired_dir).toRotationMatrix(); + Transform3d sc = Transform3d::Identity(); + sc.scale(desired_len); + m = m*sc*rot; + + const Camera& camera = wxGetApp().plater()->get_camera(); + Transform3d view_model_matrix = camera.get_view_matrix() * + Geometry::assemble_transform(selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z() * Vec3d::UnitZ()) * m; + + shader->set_uniform("view_model_matrix", view_model_matrix); + arrow.render(); + }; + + m_imgui->begin(std::string("DEBUG")); + bool invalid = hi.is_invalid(); + if (invalid) { + if (m_imgui->button(std::string("HALFEDGE INVALID (Click to reset)"))) + hi = sm.halfedge(Face_index(0)); + } else { + m_imgui->text(sm.is_border(hi) ? "BORDER HALFEDGE !" : "Halfedge is not border"); + m_imgui->text((std::string("Face: ") + std::to_string(int(hi.face()))).c_str()); + m_imgui->text(std::string("Target degree:" + std::to_string(sm.degree(sm.target(hi))))); + m_imgui->text(std::string("Face degree:" + std::to_string(sm.degree(sm.face(hi))))); + } + m_imgui->disabled_begin(invalid); + if (m_imgui->button(std::string("next"))) + hi = sm.next(hi); + if (m_imgui->button(std::string("prev"))) + hi = sm.prev(hi); + if (m_imgui->button(std::string("opposite"))) + hi = sm.opposite(hi); + if (m_imgui->button(std::string("next_around_target"))) + hi = sm.next_around_target(hi); + if (m_imgui->button(std::string("prev_around_target"))) + hi = sm.prev_around_target(hi); + if (m_imgui->button(std::string("next_around_source"))) + hi = sm.next_around_source(hi); + if (m_imgui->button(std::string("prev_around_source"))) + hi = sm.prev_around_source(hi); + if (m_imgui->button(std::string("remember one"))) + src = sm.target(hi); + if (m_imgui->button(std::string("switch to halfedge"))) { + tgt = sm.target(hi); + hi = sm.halfedge(src, tgt); + } + + if (invalid) + m_imgui->disabled_end(); + m_imgui->end(); + + if (! hi.is_invalid()) { + Vec3d a = sm.point(sm.source(hi)).cast(); + Vec3d b = sm.point(sm.target(hi)).cast(); + draw_arrow(a, b); + } + + + ///////////////// + //////////////// + ////////////////// + + + + + + glsafe(::glEnable(GL_CULL_FACE)); glsafe(::glDisable(GL_BLEND)); @@ -222,11 +346,11 @@ void GLGizmoFlatten::update_planes() for (const ModelVolume* vol : mo->volumes) { if (vol->type() != ModelVolumeType::MODEL_PART) continue; - TriangleMesh vol_ch = vol->get_convex_hull(); + TriangleMesh vol_ch = vol->mesh(); //vol->get_convex_hull(); vol_ch.transform(vol->get_matrix()); ch.merge(vol_ch); } - ch = ch.convex_hull_3d(); + //ch = ch.convex_hull_3d(); m_planes.clear(); #if ENABLE_WORLD_COORDINATE const Transform3d inst_matrix = mo->instances.front()->get_matrix_no_offset(); @@ -247,47 +371,18 @@ void GLGizmoFlatten::update_planes() std::vector facet_visited(num_of_facets, false); int facet_queue_cnt = 0; const stl_normal* normal_ptr = nullptr; - int facet_idx = 0; - while (1) { - // Find next unvisited triangle: - for (; facet_idx < num_of_facets; ++ facet_idx) - if (!facet_visited[facet_idx]) { - facet_queue[facet_queue_cnt ++] = facet_idx; - facet_visited[facet_idx] = true; - normal_ptr = &face_normals[facet_idx]; - m_planes.emplace_back(); - break; - } - if (facet_idx == num_of_facets) - break; // Everything was visited already - - while (facet_queue_cnt > 0) { - int facet_idx = facet_queue[-- facet_queue_cnt]; - const stl_normal& this_normal = face_normals[facet_idx]; - if (std::abs(this_normal(0) - (*normal_ptr)(0)) < 0.001 && std::abs(this_normal(1) - (*normal_ptr)(1)) < 0.001 && std::abs(this_normal(2) - (*normal_ptr)(2)) < 0.001) { - const Vec3i face = ch.its.indices[facet_idx]; - for (int j=0; j<3; ++j) - m_planes.back().vertices.emplace_back(ch.its.vertices[face[j]].cast()); - - facet_visited[facet_idx] = true; - for (int j = 0; j < 3; ++ j) - if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && ! facet_visited[neighbor_idx]) - facet_queue[facet_queue_cnt ++] = neighbor_idx; - } - } - m_planes.back().normal = normal_ptr->cast(); + + for (size_t i=0; i()); + m_planes.back().normal = face_normals[i].cast(); Pointf3s& verts = m_planes.back().vertices; // Now we'll transform all the points into world coordinates, so that the areas, angles and distances // make real sense. verts = transform(verts, inst_matrix); - - // if this is a just a very small triangle, remove it to speed up further calculations (it would be rejected later anyway): - if (verts.size() == 3 && - ((verts[0] - verts[1]).norm() < minimal_side - || (verts[0] - verts[2]).norm() < minimal_side - || (verts[1] - verts[2]).norm() < minimal_side)) - m_planes.pop_back(); } // Let's prepare transformation of the normal vector from mesh to instance coordinates. @@ -319,84 +414,12 @@ void GLGizmoFlatten::update_planes() polygon = Slic3r::Geometry::convex_hull(polygon); polygon = transform(polygon, tr.inverse()); - // Calculate area of the polygons and discard ones that are too small - float& area = m_planes[polygon_id].area; - area = 0.f; - for (unsigned int i = 0; i < polygon.size(); i++) // Shoelace formula - area += polygon[i](0)*polygon[i + 1 < polygon.size() ? i + 1 : 0](1) - polygon[i + 1 < polygon.size() ? i + 1 : 0](0)*polygon[i](1); - area = 0.5f * std::abs(area); - - bool discard = false; - if (area < minimal_area) - discard = true; - else { - // We also check the inner angles and discard polygons with angles smaller than the following threshold - const double angle_threshold = ::cos(10.0 * (double)PI / 180.0); - - for (unsigned int i = 0; i < polygon.size(); ++i) { - const Vec3d& prec = polygon[(i == 0) ? polygon.size() - 1 : i - 1]; - const Vec3d& curr = polygon[i]; - const Vec3d& next = polygon[(i == polygon.size() - 1) ? 0 : i + 1]; - - if ((prec - curr).normalized().dot((next - curr).normalized()) > angle_threshold) { - discard = true; - break; - } - } - } - - if (discard) { - m_planes[polygon_id--] = std::move(m_planes.back()); - m_planes.pop_back(); - continue; - } - // We will shrink the polygon a little bit so it does not touch the object edges: Vec3d centroid = std::accumulate(polygon.begin(), polygon.end(), Vec3d(0.0, 0.0, 0.0)); centroid /= (double)polygon.size(); for (auto& vertex : polygon) vertex = 0.9f*vertex + 0.1f*centroid; - // Polygon is now simple and convex, we'll round the corners to make them look nicer. - // The algorithm takes a vertex, calculates middles of respective sides and moves the vertex - // towards their average (controlled by 'aggressivity'). This is repeated k times. - // In next iterations, the neighbours are not always taken at the middle (to increase the - // rounding effect at the corners, where we need it most). - const unsigned int k = 10; // number of iterations - const float aggressivity = 0.2f; // agressivity - const unsigned int N = polygon.size(); - std::vector> neighbours; - if (k != 0) { - Pointf3s points_out(2*k*N); // vector long enough to store the future vertices - for (unsigned int j=0; j vertices; // should be in fact local in update_planes() #if ENABLE_LEGACY_OPENGL_REMOVAL From f0cf420a8457e1355181ba0e22efcb8f4856dcf8 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 3 Jun 2022 15:37:11 +0200 Subject: [PATCH 004/103] Measuring: separated another gizmo --- resources/icons/measure.svg | 13 + src/libslic3r/SurfaceMesh.hpp | 1 + src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 388 ++++++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 75 +++++ src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 2 + src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 1 + 7 files changed, 482 insertions(+) create mode 100644 resources/icons/measure.svg create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp diff --git a/resources/icons/measure.svg b/resources/icons/measure.svg new file mode 100644 index 0000000000..275c522251 --- /dev/null +++ b/resources/icons/measure.svg @@ -0,0 +1,13 @@ + + + Layer 1 + + + + + + + + + + \ No newline at end of file diff --git a/src/libslic3r/SurfaceMesh.hpp b/src/libslic3r/SurfaceMesh.hpp index 47d5ed0dcc..e8ada4cd80 100644 --- a/src/libslic3r/SurfaceMesh.hpp +++ b/src/libslic3r/SurfaceMesh.hpp @@ -138,6 +138,7 @@ public: bool is_border(Halfedge_index h) const { return m_face_neighbors[h.m_face][h.m_side] == -1; } bool is_same_vertex(const Vertex_index& a, const Vertex_index& b) const { return m_its.indices[a.m_face][a.m_vertex_idx] == m_its.indices[b.m_face][b.m_vertex_idx]; } + Vec3i get_face_neighbors(Face_index face_id) const { assert(int(face_id) < int(m_face_neighbors.size())); return m_face_neighbors[face_id]; } diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 29b8b7e736..a56556baa5 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -61,6 +61,8 @@ set(SLIC3R_GUI_SOURCES GUI/Gizmos/GLGizmoSimplify.hpp GUI/Gizmos/GLGizmoMmuSegmentation.cpp GUI/Gizmos/GLGizmoMmuSegmentation.hpp + GUI/Gizmos/GLGizmoMeasure.cpp + GUI/Gizmos/GLGizmoMeasure.hpp GUI/GLSelectionRectangle.cpp GUI/GLSelectionRectangle.hpp GUI/GLModel.hpp diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp new file mode 100644 index 0000000000..572ea75dbe --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -0,0 +1,388 @@ +// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. +#include "GLGizmoMeasure.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/Plater.hpp" + +#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" + +#include "libslic3r/Geometry/ConvexHull.hpp" +#include "libslic3r/Model.hpp" +#include "libslic3r/SurfaceMesh.hpp" + +#include + +#include + +namespace Slic3r { +namespace GUI { + +static const Slic3r::ColorRGBA DEFAULT_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.5f }; +static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.75f }; + +GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : GLGizmoBase(parent, icon_filename, sprite_id) +{} + + + +bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) +{ + if (mouse_event.Moving()) { + // only for sure + m_mouse_left_down = false; + return false; + } + if (mouse_event.LeftDown()) { + if (m_hover_id != -1) { + m_mouse_left_down = true; + Selection &selection = m_parent.get_selection(); + if (selection.is_single_full_instance()) { + // Rotate the object so the normal points downward: + selection.flattening_rotate(m_planes[m_hover_id].normal); + m_parent.do_rotate(L("Gizmo-Place on Face")); + } + return true; + } + + // fix: prevent restart gizmo when reselect object + // take responsibility for left up + if (m_parent.get_first_hover_volume_idx() >= 0) m_mouse_left_down = true; + + } else if (mouse_event.LeftUp()) { + if (m_mouse_left_down) { + // responsible for mouse left up after selecting plane + m_mouse_left_down = false; + return true; + } + } else if (mouse_event.Leaving()) { + m_mouse_left_down = false; + } + return false; +} + + + +void GLGizmoMeasure::data_changed() +{ + const Selection & selection = m_parent.get_selection(); + const ModelObject *model_object = nullptr; + if (selection.is_single_full_instance() || + selection.is_from_single_object() ) { + model_object = selection.get_model()->objects[selection.get_object_idx()]; + } + set_flattening_data(model_object); +} + + + +bool GLGizmoMeasure::on_init() +{ + // FIXME m_shortcut_key = WXK_CONTROL_F; + return true; +} + + + +void GLGizmoMeasure::on_set_state() +{ +} + + + +CommonGizmosDataID GLGizmoMeasure::on_get_requirements() const +{ + return CommonGizmosDataID::SelectionInfo; +} + + + +std::string GLGizmoMeasure::on_get_name() const +{ + return _u8L("Measure"); +} + + + +bool GLGizmoMeasure::on_is_activable() const +{ + // This is assumed in GLCanvas3D::do_rotate, do not change this + // without updating that function too. + return m_parent.get_selection().is_single_full_instance(); +} + + + +void GLGizmoMeasure::on_render() +{ + const Selection& selection = m_parent.get_selection(); + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + shader->start_using(); + + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); + + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glEnable(GL_BLEND)); + + if (selection.is_single_full_instance()) { + const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d view_model_matrix = camera.get_view_matrix() * + Geometry::assemble_transform(selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z() * Vec3d::UnitZ()) * m; + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + if (this->is_plane_update_necessary()) + update_planes(); + for (int i = 0; i < (int)m_planes.size(); ++i) { + m_planes[i].vbo.set_color(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); + m_planes[i].vbo.render(); + } + } + + glsafe(::glEnable(GL_CULL_FACE)); + glsafe(::glDisable(GL_BLEND)); + + shader->stop_using(); +} + + + + + +#if ! ENABLE_LEGACY_OPENGL_REMOVAL + #error NOT IMPLEMENTED +#endif +#if ! ENABLE_GL_SHADERS_ATTRIBUTES + #error NOT IMPLEMENTED +#endif + + + + + +void GLGizmoMeasure::on_render_for_picking() +{ + const Selection& selection = m_parent.get_selection(); + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + shader->start_using(); + + glsafe(::glDisable(GL_DEPTH_TEST)); + glsafe(::glDisable(GL_BLEND)); + + if (selection.is_single_full_instance() && !wxGetKeyState(WXK_CONTROL)) { + const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d view_model_matrix = camera.get_view_matrix() * + Geometry::assemble_transform(selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z() * Vec3d::UnitZ()) * m; + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + if (this->is_plane_update_necessary()) + update_planes(); + for (int i = 0; i < (int)m_planes.size(); ++i) { + m_planes[i].vbo.set_color(picking_color_component(i)); + m_planes[i].vbo.render(); + } + } + + glsafe(::glEnable(GL_CULL_FACE)); + + shader->stop_using(); +} + + + +void GLGizmoMeasure::set_flattening_data(const ModelObject* model_object) +{ + if (model_object != m_old_model_object) { + m_planes.clear(); + m_planes_valid = false; + } +} + + + +void GLGizmoMeasure::update_planes() +{ + const ModelObject* mo = m_c->selection_info()->model_object(); + TriangleMesh ch; + for (const ModelVolume* vol : mo->volumes) { + if (vol->type() != ModelVolumeType::MODEL_PART) + continue; + TriangleMesh vol_ch = vol->mesh(); + vol_ch.transform(vol->get_matrix()); + ch.merge(vol_ch); + } + m_planes.clear(); + const Transform3d& inst_matrix = mo->instances.front()->get_matrix(); + + // Now we'll go through all the facets and append Points of facets sharing the same normal. + // This part is still performed in mesh coordinate system. + const int num_of_facets = ch.facets_count(); + std::vector face_to_plane(num_of_facets, 0); + const std::vector face_normals = its_face_normals(ch.its); + const std::vector face_neighbors = its_face_neighbors(ch.its); + std::vector facet_queue(num_of_facets, 0); + std::vector facet_visited(num_of_facets, false); + int facet_queue_cnt = 0; + const stl_normal* normal_ptr = nullptr; + int facet_idx = 0; + + auto is_same_normal = [](const stl_normal& a, const stl_normal& b) -> bool { + return (std::abs(a(0) - b(0)) < 0.001 && std::abs(a(1) - b(1)) < 0.001 && std::abs(a(2) - b(2)) < 0.001); + }; + + while (1) { + // Find next unvisited triangle: + for (; facet_idx < num_of_facets; ++ facet_idx) + if (!facet_visited[facet_idx]) { + facet_queue[facet_queue_cnt ++] = facet_idx; + facet_visited[facet_idx] = true; + normal_ptr = &face_normals[facet_idx]; + face_to_plane[facet_idx] = m_planes.size(); + m_planes.emplace_back(); + break; + } + if (facet_idx == num_of_facets) + break; // Everything was visited already + + while (facet_queue_cnt > 0) { + int facet_idx = facet_queue[-- facet_queue_cnt]; + const stl_normal& this_normal = face_normals[facet_idx]; + if (is_same_normal(this_normal, *normal_ptr)) { + const Vec3i& face = ch.its.indices[facet_idx]; + for (int j=0; j<3; ++j) + m_planes.back().vertices.emplace_back(ch.its.vertices[face[j]].cast()); + + facet_visited[facet_idx] = true; + face_to_plane[facet_idx] = m_planes.size() - 1; + for (int j = 0; j < 3; ++ j) + if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && ! facet_visited[neighbor_idx]) + facet_queue[facet_queue_cnt ++] = neighbor_idx; + } + } + m_planes.back().normal = normal_ptr->cast(); + + Pointf3s& verts = m_planes.back().vertices; + // Now we'll transform all the points into world coordinates, so that the areas, angles and distances + // make real sense. + verts = transform(verts, inst_matrix); + } + + // Let's prepare transformation of the normal vector from mesh to instance coordinates. + Geometry::Transformation t(inst_matrix); + Vec3d scaling = t.get_scaling_factor(); + t.set_scaling_factor(Vec3d(1./scaling(0), 1./scaling(1), 1./scaling(2))); + + // Now we'll go through all the polygons, transform the points into xy plane to process them: + for (unsigned int polygon_id=0; polygon_id < m_planes.size(); ++polygon_id) { + Pointf3s& polygon = m_planes[polygon_id].vertices; + const Vec3d& normal = m_planes[polygon_id].normal; + + // transform the normal according to the instance matrix: + Vec3d normal_transformed = t.get_matrix() * normal; + + // We are going to rotate about z and y to flatten the plane + Eigen::Quaterniond q; + Transform3d m = Transform3d::Identity(); + m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(normal_transformed, Vec3d::UnitZ()).toRotationMatrix(); + polygon = transform(polygon, m); + + // Now to remove the inner points. We'll misuse Geometry::convex_hull for that, but since + // it works in fixed point representation, we will rescale the polygon to avoid overflows. + // And yes, it is a nasty thing to do. Whoever has time is free to refactor. + Vec3d bb_size = BoundingBoxf3(polygon).size(); + float sf = std::min(1./bb_size(0), 1./bb_size(1)); + Transform3d tr = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(sf, sf, 1.f)); + polygon = transform(polygon, tr); + polygon = Slic3r::Geometry::convex_hull(polygon); + polygon = transform(polygon, tr.inverse()); + + // We will shrink the polygon a little bit so it does not touch the object edges: + Vec3d centroid = std::accumulate(polygon.begin(), polygon.end(), Vec3d(0.0, 0.0, 0.0)); + centroid /= (double)polygon.size(); + for (auto& vertex : polygon) + vertex = 0.95f*vertex + 0.05f*centroid; + + // Raise a bit above the object surface to avoid flickering: + for (auto& b : polygon) + b(2) += 0.1f; + + // Transform back to 3D (and also back to mesh coordinates) + polygon = transform(polygon, inst_matrix.inverse() * m.inverse()); + } + + // We'll sort the planes by area and only keep the 254 largest ones (because of the picking pass limitations): + std::sort(m_planes.rbegin(), m_planes.rend(), [](const PlaneData& a, const PlaneData& b) { return a.area < b.area; }); + m_planes.resize(std::min((int)m_planes.size(), 254)); + + // Planes are finished - let's save what we calculated it from: + m_volumes_matrices.clear(); + m_volumes_types.clear(); + for (const ModelVolume* vol : mo->volumes) { + m_volumes_matrices.push_back(vol->get_matrix()); + m_volumes_types.push_back(vol->type()); + } + m_first_instance_scale = mo->instances.front()->get_scaling_factor(); + m_first_instance_mirror = mo->instances.front()->get_mirror(); + m_old_model_object = mo; + + // And finally create respective VBOs. The polygon is convex with + // the vertices in order, so triangulation is trivial. + for (auto& plane : m_planes) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::TriangleFan, GLModel::Geometry::EVertexLayout::P3N3 }; + init_data.reserve_vertices(plane.vertices.size()); + init_data.reserve_indices(plane.vertices.size()); + // vertices + indices + for (size_t i = 0; i < plane.vertices.size(); ++i) { + init_data.add_vertex((Vec3f)plane.vertices[i].cast(), (Vec3f)plane.normal.cast()); + init_data.add_index((unsigned int)i); + } + plane.vbo.init_from(std::move(init_data)); + + // FIXME: vertices should really be local, they need not + // persist now when we use VBOs + plane.vertices.clear(); + plane.vertices.shrink_to_fit(); + } + + m_planes_valid = true; +} + + + +bool GLGizmoMeasure::is_plane_update_necessary() const +{ + const ModelObject* mo = m_c->selection_info()->model_object(); + if (m_state != On || ! mo || mo->instances.empty()) + return false; + + if (! m_planes_valid || mo != m_old_model_object + || mo->volumes.size() != m_volumes_matrices.size()) + return true; + + // We want to recalculate when the scale changes - some planes could (dis)appear. + if (! mo->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale) + || ! mo->instances.front()->get_mirror().isApprox(m_first_instance_mirror)) + return true; + + for (unsigned int i=0; i < mo->volumes.size(); ++i) + if (! mo->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i]) + || mo->volumes[i]->type() != m_volumes_types[i]) + return true; + + return false; +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp new file mode 100644 index 0000000000..9bf87a29f6 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -0,0 +1,75 @@ +#ifndef slic3r_GLGizmoMeasure_hpp_ +#define slic3r_GLGizmoMeasure_hpp_ + +#include "GLGizmoBase.hpp" +#if ENABLE_LEGACY_OPENGL_REMOVAL +#include "slic3r/GUI/GLModel.hpp" +#else +#include "slic3r/GUI/3DScene.hpp" +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + + +namespace Slic3r { + +enum class ModelVolumeType : int; + + +namespace GUI { + + +class GLGizmoMeasure : public GLGizmoBase +{ +// This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself. + +private: + + struct PlaneData { + std::vector vertices; // should be in fact local in update_planes() + std::vector borders_facets; + GLModel vbo; + Vec3d normal; + float area; + }; + + // This holds information to decide whether recalculation is necessary: + std::vector m_volumes_matrices; + std::vector m_volumes_types; + Vec3d m_first_instance_scale; + Vec3d m_first_instance_mirror; + + std::vector m_planes; + std::vector m_face_to_plane; + bool m_mouse_left_down = false; // for detection left_up of this gizmo + bool m_planes_valid = false; + const ModelObject* m_old_model_object = nullptr; + std::vector instances_matrices; + + void update_planes(); + bool is_plane_update_necessary() const; + void set_flattening_data(const ModelObject* model_object); + +public: + GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + + /// + /// Apply rotation on select plane + /// + /// Keep information about mouse click + /// Return True when use the information otherwise False. + bool on_mouse(const wxMouseEvent &mouse_event) override; + + void data_changed() override; +protected: + bool on_init() override; + std::string on_get_name() const override; + bool on_is_activable() const override; + void on_render() override; + void on_render_for_picking() override; + void on_set_state() override; + CommonGizmosDataID on_get_requirements() const override; +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLGizmoMeasure_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 39a1cba8e3..2a1ccd176b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -21,6 +21,7 @@ #include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp" #include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoMeasure.hpp" #include "libslic3r/format.hpp" #include "libslic3r/Model.hpp" @@ -106,6 +107,7 @@ bool GLGizmosManager::init() m_gizmos.emplace_back(new GLGizmoSeam(m_parent, "seam.svg", 8)); m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "mmu_segmentation.svg", 9)); m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "cut.svg", 10)); + m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, "measure.svg", 11)); m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 2e9e6bb65c..1a203621d2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -80,6 +80,7 @@ public: Seam, MmuSegmentation, Simplify, + Measure, Undefined }; From 5d8aaed18f97906481ae6bbd53d8bc175ab28e9f Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 21 Jun 2022 12:47:47 +0200 Subject: [PATCH 005/103] Measuring: Initial plane detection --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 144 +++++++++++------------ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 4 +- 2 files changed, 74 insertions(+), 74 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 572ea75dbe..129407764c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -127,6 +127,7 @@ void GLGizmoMeasure::on_render() glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_BLEND)); + glsafe(::glLineWidth(5.f)); if (selection.is_single_full_instance()) { const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); @@ -227,15 +228,14 @@ void GLGizmoMeasure::update_planes() // Now we'll go through all the facets and append Points of facets sharing the same normal. // This part is still performed in mesh coordinate system. - const int num_of_facets = ch.facets_count(); - std::vector face_to_plane(num_of_facets, 0); + const size_t num_of_facets = ch.facets_count(); + std::vector face_to_plane(num_of_facets, size_t(-1)); const std::vector face_normals = its_face_normals(ch.its); const std::vector face_neighbors = its_face_neighbors(ch.its); std::vector facet_queue(num_of_facets, 0); - std::vector facet_visited(num_of_facets, false); int facet_queue_cnt = 0; const stl_normal* normal_ptr = nullptr; - int facet_idx = 0; + size_t seed_facet_idx = 0; auto is_same_normal = [](const stl_normal& a, const stl_normal& b) -> bool { return (std::abs(a(0) - b(0)) < 0.001 && std::abs(a(1) - b(1)) < 0.001 && std::abs(a(2) - b(2)) < 0.001); @@ -243,16 +243,15 @@ void GLGizmoMeasure::update_planes() while (1) { // Find next unvisited triangle: - for (; facet_idx < num_of_facets; ++ facet_idx) - if (!facet_visited[facet_idx]) { - facet_queue[facet_queue_cnt ++] = facet_idx; - facet_visited[facet_idx] = true; - normal_ptr = &face_normals[facet_idx]; - face_to_plane[facet_idx] = m_planes.size(); + for (; seed_facet_idx < num_of_facets; ++ seed_facet_idx) + if (face_to_plane[seed_facet_idx] == size_t(-1)) { + facet_queue[facet_queue_cnt ++] = seed_facet_idx; + normal_ptr = &face_normals[seed_facet_idx]; + face_to_plane[seed_facet_idx] = m_planes.size(); m_planes.emplace_back(); break; } - if (facet_idx == num_of_facets) + if (seed_facet_idx == num_of_facets) break; // Everything was visited already while (facet_queue_cnt > 0) { @@ -260,70 +259,69 @@ void GLGizmoMeasure::update_planes() const stl_normal& this_normal = face_normals[facet_idx]; if (is_same_normal(this_normal, *normal_ptr)) { const Vec3i& face = ch.its.indices[facet_idx]; - for (int j=0; j<3; ++j) - m_planes.back().vertices.emplace_back(ch.its.vertices[face[j]].cast()); - facet_visited[facet_idx] = true; face_to_plane[facet_idx] = m_planes.size() - 1; + m_planes.back().facets.emplace_back(facet_idx); for (int j = 0; j < 3; ++ j) - if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && ! facet_visited[neighbor_idx]) + if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && face_to_plane[neighbor_idx] == size_t(-1)) facet_queue[facet_queue_cnt ++] = neighbor_idx; } } - m_planes.back().normal = normal_ptr->cast(); - Pointf3s& verts = m_planes.back().vertices; - // Now we'll transform all the points into world coordinates, so that the areas, angles and distances - // make real sense. - verts = transform(verts, inst_matrix); + m_planes.back().normal = normal_ptr->cast(); } + assert(std::none_of(face_to_plane.begin(), face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); + + SurfaceMesh sm(ch.its); + for (int plane_id=0; plane_id < m_planes.size(); ++plane_id) { + //int plane_id = 5; { + const auto& facets = m_planes[plane_id].facets; + std::vector pts; + for (int face_id=0; face_id{ sm.point(sm.source(he)).cast() }); + Vertex_index target = sm.target(he); + const Halfedge_index he_start = he; + + do { + const Halfedge_index he_orig = he; + he = sm.next_around_target(he); + while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) + he = sm.next_around_target(he); + he = sm.opposite(he); + m_planes[plane_id].borders.back().emplace_back(sm.point(sm.source(he)).cast()); + } while (he != he_start); + } + } + + + // DEBUGGING: + //m_planes.erase(std::remove_if(m_planes.begin(), m_planes.end(), [](const PlaneData& p) { return p.borders.empty(); }), m_planes.end()); + + + + + // Let's prepare transformation of the normal vector from mesh to instance coordinates. Geometry::Transformation t(inst_matrix); Vec3d scaling = t.get_scaling_factor(); t.set_scaling_factor(Vec3d(1./scaling(0), 1./scaling(1), 1./scaling(2))); - // Now we'll go through all the polygons, transform the points into xy plane to process them: - for (unsigned int polygon_id=0; polygon_id < m_planes.size(); ++polygon_id) { - Pointf3s& polygon = m_planes[polygon_id].vertices; - const Vec3d& normal = m_planes[polygon_id].normal; - - // transform the normal according to the instance matrix: - Vec3d normal_transformed = t.get_matrix() * normal; - - // We are going to rotate about z and y to flatten the plane - Eigen::Quaterniond q; - Transform3d m = Transform3d::Identity(); - m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(normal_transformed, Vec3d::UnitZ()).toRotationMatrix(); - polygon = transform(polygon, m); - - // Now to remove the inner points. We'll misuse Geometry::convex_hull for that, but since - // it works in fixed point representation, we will rescale the polygon to avoid overflows. - // And yes, it is a nasty thing to do. Whoever has time is free to refactor. - Vec3d bb_size = BoundingBoxf3(polygon).size(); - float sf = std::min(1./bb_size(0), 1./bb_size(1)); - Transform3d tr = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(sf, sf, 1.f)); - polygon = transform(polygon, tr); - polygon = Slic3r::Geometry::convex_hull(polygon); - polygon = transform(polygon, tr.inverse()); - - // We will shrink the polygon a little bit so it does not touch the object edges: - Vec3d centroid = std::accumulate(polygon.begin(), polygon.end(), Vec3d(0.0, 0.0, 0.0)); - centroid /= (double)polygon.size(); - for (auto& vertex : polygon) - vertex = 0.95f*vertex + 0.05f*centroid; - - // Raise a bit above the object surface to avoid flickering: - for (auto& b : polygon) - b(2) += 0.1f; - - // Transform back to 3D (and also back to mesh coordinates) - polygon = transform(polygon, inst_matrix.inverse() * m.inverse()); - } - - // We'll sort the planes by area and only keep the 254 largest ones (because of the picking pass limitations): - std::sort(m_planes.rbegin(), m_planes.rend(), [](const PlaneData& a, const PlaneData& b) { return a.area < b.area; }); - m_planes.resize(std::min((int)m_planes.size(), 254)); + // Planes are finished - let's save what we calculated it from: m_volumes_matrices.clear(); @@ -339,21 +337,23 @@ void GLGizmoMeasure::update_planes() // And finally create respective VBOs. The polygon is convex with // the vertices in order, so triangulation is trivial. for (auto& plane : m_planes) { - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::TriangleFan, GLModel::Geometry::EVertexLayout::P3N3 }; - init_data.reserve_vertices(plane.vertices.size()); - init_data.reserve_indices(plane.vertices.size()); - // vertices + indices - for (size_t i = 0; i < plane.vertices.size(); ++i) { - init_data.add_vertex((Vec3f)plane.vertices[i].cast(), (Vec3f)plane.normal.cast()); - init_data.add_index((unsigned int)i); + for (auto& vertices : plane.borders) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3N3 }; + init_data.reserve_vertices(vertices.size()); + init_data.reserve_indices(vertices.size()); + // vertices + indices + for (size_t i = 0; i < vertices.size(); ++i) { + init_data.add_vertex((Vec3f)vertices[i].cast(), (Vec3f)plane.normal.cast()); + init_data.add_index((unsigned int)i); + } + plane.vbo.init_from(std::move(init_data)); } - plane.vbo.init_from(std::move(init_data)); // FIXME: vertices should really be local, they need not // persist now when we use VBOs - plane.vertices.clear(); - plane.vertices.shrink_to_fit(); + plane.borders.clear(); + plane.borders.shrink_to_fit(); } m_planes_valid = true; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 9bf87a29f6..87ad73d8c6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -24,8 +24,8 @@ class GLGizmoMeasure : public GLGizmoBase private: struct PlaneData { - std::vector vertices; // should be in fact local in update_planes() - std::vector borders_facets; + std::vector> borders; // should be in fact local in update_planes() + std::vector facets; GLModel vbo; Vec3d normal; float area; From 985b16e858621579d17c1b716ae42bd55ede7456 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 22 Jun 2022 10:34:58 +0200 Subject: [PATCH 006/103] Measuring: Simple visualization --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 62 ++++++++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 4 +- 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 129407764c..de3fc398b9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -17,15 +17,13 @@ namespace Slic3r { namespace GUI { -static const Slic3r::ColorRGBA DEFAULT_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.5f }; -static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.75f }; +static const Slic3r::ColorRGBA DEFAULT_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.9f }; +static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.2f, 0.2f, 1.f }; GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) {} - - bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) { if (mouse_event.Moving()) { @@ -139,9 +137,29 @@ void GLGizmoMeasure::on_render() shader->set_uniform("projection_matrix", camera.get_projection_matrix()); if (this->is_plane_update_necessary()) update_planes(); - for (int i = 0; i < (int)m_planes.size(); ++i) { - m_planes[i].vbo.set_color(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); - m_planes[i].vbo.render(); + + m_imgui->begin(std::string("DEBUG")); + if (m_imgui->button("<-")) + --m_currently_shown_plane; + ImGui::SameLine(); + if (m_imgui->button("->")) + ++m_currently_shown_plane; + m_currently_shown_plane = std::clamp(m_currently_shown_plane, 0, int(m_planes.size())-1); + m_imgui->text(std::to_string(m_currently_shown_plane)); + m_imgui->end(); + + + //for (int i = 0; i < (int)m_planes.size(); ++i) { + int i = m_currently_shown_plane; + std::cout << m_hover_id << "\t" << m_currently_shown_plane << "\t" << std::endl; + if (i < m_planes.size()) { + for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { + m_planes[i].vbos[j].set_color(j == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); + if (j == m_hover_id) + m_planes[i].vbos[j].render(); + std::cout << " * " << j; + } + std::cout <get_instance_transformation().get_matrix(); @@ -189,9 +208,13 @@ void GLGizmoMeasure::on_render_for_picking() shader->set_uniform("projection_matrix", camera.get_projection_matrix()); if (this->is_plane_update_necessary()) update_planes(); - for (int i = 0; i < (int)m_planes.size(); ++i) { - m_planes[i].vbo.set_color(picking_color_component(i)); - m_planes[i].vbo.render(); + //for (int i = 0; i < (int)m_planes.size(); ++i) { + int i = m_currently_shown_plane; + if (i < m_planes.size()) { + for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { + m_planes[i].vbos[j].set_color(picking_color_component(j)); + m_planes[i].vbos[j].render(); + } } } @@ -277,7 +300,7 @@ void GLGizmoMeasure::update_planes() for (int plane_id=0; plane_id < m_planes.size(); ++plane_id) { //int plane_id = 5; { const auto& facets = m_planes[plane_id].facets; - std::vector pts; + std::vector pts; for (int face_id=0; face_id{ sm.point(sm.source(he)).cast() }); + pts.emplace_back(sm.point(sm.source(he)).cast()); Vertex_index target = sm.target(he); const Halfedge_index he_start = he; @@ -303,14 +326,20 @@ void GLGizmoMeasure::update_planes() while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) he = sm.next_around_target(he); he = sm.opposite(he); - m_planes[plane_id].borders.back().emplace_back(sm.point(sm.source(he)).cast()); + pts.emplace_back(sm.point(sm.source(he)).cast()); } while (he != he_start); + + if (pts.size() != 1) { + m_planes[plane_id].borders.emplace_back(pts); + pts.clear(); + } + } } // DEBUGGING: - //m_planes.erase(std::remove_if(m_planes.begin(), m_planes.end(), [](const PlaneData& p) { return p.borders.empty(); }), m_planes.end()); + m_planes.erase(std::remove_if(m_planes.begin(), m_planes.end(), [](const PlaneData& p) { return p.borders.empty(); }), m_planes.end()); @@ -337,7 +366,7 @@ void GLGizmoMeasure::update_planes() // And finally create respective VBOs. The polygon is convex with // the vertices in order, so triangulation is trivial. for (auto& plane : m_planes) { - for (auto& vertices : plane.borders) { + for (const auto& vertices : plane.borders) { GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3N3 }; init_data.reserve_vertices(vertices.size()); @@ -347,7 +376,8 @@ void GLGizmoMeasure::update_planes() init_data.add_vertex((Vec3f)vertices[i].cast(), (Vec3f)plane.normal.cast()); init_data.add_index((unsigned int)i); } - plane.vbo.init_from(std::move(init_data)); + plane.vbos.emplace_back(); + plane.vbos.back().init_from(std::move(init_data)); } // FIXME: vertices should really be local, they need not diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 87ad73d8c6..32b43e6f84 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -23,10 +23,12 @@ class GLGizmoMeasure : public GLGizmoBase private: + int m_currently_shown_plane = 0; + struct PlaneData { std::vector> borders; // should be in fact local in update_planes() std::vector facets; - GLModel vbo; + std::vector vbos; Vec3d normal; float area; }; From 9aa706c0a743d68847b3237c4027abafe0b7adee Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 1 Jul 2022 15:51:51 +0200 Subject: [PATCH 007/103] Measuring: First steps on extracting features --- src/libslic3r/SurfaceMesh.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 204 ++++++++++++++++++----- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 20 ++- 3 files changed, 183 insertions(+), 42 deletions(-) diff --git a/src/libslic3r/SurfaceMesh.hpp b/src/libslic3r/SurfaceMesh.hpp index e8ada4cd80..a4b261ceb9 100644 --- a/src/libslic3r/SurfaceMesh.hpp +++ b/src/libslic3r/SurfaceMesh.hpp @@ -17,6 +17,7 @@ class Halfedge_index { public: Halfedge_index() : m_face(Face_index(-1)), m_side(0) {} Face_index face() const { return m_face; } + unsigned char side() const { return m_side; } bool is_invalid() const { return int(m_face) < 0; } bool operator!=(const Halfedge_index& rhs) const { return ! ((*this) == rhs); } bool operator==(const Halfedge_index& rhs) const { return m_face == rhs.m_face && m_side == rhs.m_side; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index de3fc398b9..6c95e70b7f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -22,7 +22,10 @@ static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.2f, 0.2f, 1 GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) -{} +{ + m_vbo_sphere.init_from(its_make_sphere(1., M_PI/32.)); + m_vbo_cylinder.init_from(its_make_cylinder(1., 1.)); +} bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) { @@ -125,7 +128,7 @@ void GLGizmoMeasure::on_render() glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_BLEND)); - glsafe(::glLineWidth(5.f)); + glsafe(::glLineWidth(2.f)); if (selection.is_single_full_instance()) { const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); @@ -144,22 +147,47 @@ void GLGizmoMeasure::on_render() ImGui::SameLine(); if (m_imgui->button("->")) ++m_currently_shown_plane; - m_currently_shown_plane = std::clamp(m_currently_shown_plane, 0, int(m_planes.size())-1); - m_imgui->text(std::to_string(m_currently_shown_plane)); + m_currently_shown_plane = std::clamp(m_currently_shown_plane, 0, std::max(0, int(m_planes.size())-1)); + m_imgui->text(std::to_string(m_currently_shown_plane)); + m_imgui->checkbox(wxString("Show all"), m_show_all); m_imgui->end(); - //for (int i = 0; i < (int)m_planes.size(); ++i) { - int i = m_currently_shown_plane; - std::cout << m_hover_id << "\t" << m_currently_shown_plane << "\t" << std::endl; - if (i < m_planes.size()) { + int i = m_show_all ? 0 : m_currently_shown_plane; + for (int i = 0; i < (int)m_planes.size(); ++i) { + // Render all the borders. for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { m_planes[i].vbos[j].set_color(j == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); - if (j == m_hover_id) m_planes[i].vbos[j].render(); - std::cout << " * " << j; } - std::cout <::FromTwoVectors(Vec3d::UnitZ(), feature.endpoint - feature.pos); + view_feature_matrix *= q; + view_feature_matrix.scale(Vec3d(0.3, 0.3, (feature.endpoint - feature.pos).norm())); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_cylinder.set_color(ColorRGBA(0.7f, 0.7f, 0.f, 1.f)); + m_vbo_cylinder.render(); + } + + view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(feature.type == SurfaceFeature::Line + ? ColorRGBA(1.f, 0.f, 0.f, 1.f) + : ColorRGBA(0.f, 1.f, 0.f, 1.f)); + m_vbo_sphere.render(); + + + shader->set_uniform("view_model_matrix", view_model_matrix); + } + + if (! m_show_all) + break; } } @@ -196,7 +224,7 @@ void GLGizmoMeasure::on_render_for_picking() glsafe(::glDisable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_BLEND)); - glsafe(::glLineWidth(5.f)); + glsafe(::glLineWidth(2.f)); if (selection.is_single_full_instance() && !wxGetKeyState(WXK_CONTROL)) { const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); @@ -235,6 +263,73 @@ void GLGizmoMeasure::set_flattening_data(const ModelObject* model_object) +void GLGizmoMeasure::extract_features(GLGizmoMeasure::PlaneData& plane) +{ + plane.surface_features.clear(); + const Vec3d& normal = plane.normal; + + const double edge_threshold = 10. * (M_PI/180.); + + + + for (const std::vector& border : plane.borders) { + assert(border.size() > 1); + assert(! border.front().isApprox(border.back())); + double last_angle = 0.; + size_t first_idx = 0; + + for (size_t i=0; i edge_threshold || i == border.size() - 1) { + // Current feature ended. Save it and remember current point as beginning of the next. + bool is_line = (i == first_idx + 1); + plane.surface_features.emplace_back(SurfaceFeature{ + is_line ? SurfaceFeature::Line : SurfaceFeature::Circle, + is_line ? border[first_idx] : border[first_idx], // FIXME + border[i], + 0. // FIXME + }); + first_idx = i; + } else if (Slic3r::is_approx(angle, last_angle)) { + // possibly a segment of a circle + } else { + first_idx = i; + + } + } + last_angle = angle; + } + + // FIXME: Possibly merge the first and last feature. + + + + std::cout << "==================== " << std::endl; + } + + + for (const SurfaceFeature& f : plane.surface_features) { + std::cout << "- detected " << (f.type == SurfaceFeature::Line ? "Line" : "Circle") << std::endl; + std::cout<< f.pos << std::endl << std::endl << f.endpoint << std::endl; + std::cout << "----------------" << std::endl; + } + + + +} + + + void GLGizmoMeasure::update_planes() { const ModelObject* mo = m_c->selection_info()->model_object(); @@ -292,6 +387,7 @@ void GLGizmoMeasure::update_planes() } m_planes.back().normal = normal_ptr->cast(); + std::sort(m_planes.back().facets.begin(), m_planes.back().facets.end()); } assert(std::none_of(face_to_plane.begin(), face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); @@ -300,40 +396,57 @@ void GLGizmoMeasure::update_planes() for (int plane_id=0; plane_id < m_planes.size(); ++plane_id) { //int plane_id = 5; { const auto& facets = m_planes[plane_id].facets; - std::vector pts; + m_planes[plane_id].borders.clear(); + std::vector> visited(facets.size(), {false, false, false}); + for (int face_id=0; face_id()); - Vertex_index target = sm.target(he); - const Halfedge_index he_start = he; - - do { + // he is the first halfedge on the border. Now walk around and append the points. const Halfedge_index he_orig = he; - he = sm.next_around_target(he); - while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) - he = sm.next_around_target(he); - he = sm.opposite(he); - pts.emplace_back(sm.point(sm.source(he)).cast()); - } while (he != he_start); + m_planes[plane_id].borders.emplace_back(); + m_planes[plane_id].borders.back().emplace_back(sm.point(sm.source(he)).cast()); + Vertex_index target = sm.target(he); + const Halfedge_index he_start = he; + + Face_index fi = he.face(); + auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); + assert(face_it != facets.end()); + assert(*face_it == int(fi)); + visited[face_it - facets.begin()][he.side()] = true; - if (pts.size() != 1) { - m_planes[plane_id].borders.emplace_back(pts); - pts.clear(); + do { + const Halfedge_index he_orig = he; + he = sm.next_around_target(he); + while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) + he = sm.next_around_target(he); + he = sm.opposite(he); + + Face_index fi = he.face(); + auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); + assert(face_it != facets.end()); + assert(*face_it == int(fi)); + if (visited[face_it - facets.begin()][he.side()] && he != he_start) { + m_planes[plane_id].borders.back().resize(1); + break; + } + visited[face_it - facets.begin()][he.side()] = true; + + m_planes[plane_id].borders.back().emplace_back(sm.point(sm.source(he)).cast()); + } while (he != he_start); + + if (m_planes[plane_id].borders.back().size() == 1) + m_planes[plane_id].borders.pop_back(); } - } } @@ -365,8 +478,8 @@ void GLGizmoMeasure::update_planes() // And finally create respective VBOs. The polygon is convex with // the vertices in order, so triangulation is trivial. - for (auto& plane : m_planes) { - for (const auto& vertices : plane.borders) { + for (PlaneData& plane : m_planes) { + for (std::vector& vertices : plane.borders) { GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3N3 }; init_data.reserve_vertices(vertices.size()); @@ -378,8 +491,17 @@ void GLGizmoMeasure::update_planes() } plane.vbos.emplace_back(); plane.vbos.back().init_from(std::move(init_data)); + vertices.pop_back(); // first and last are the same } + static int n=0; + std::cout << "==================== " << std::endl; + std::cout << "==================== " << std::endl; + std::cout << "==================== " << std::endl; + std::cout << "Plane num. " << n++ << std::endl; + extract_features(plane); + + // FIXME: vertices should really be local, they need not // persist now when we use VBOs plane.borders.clear(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 32b43e6f84..3099366736 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -24,15 +24,33 @@ class GLGizmoMeasure : public GLGizmoBase private: int m_currently_shown_plane = 0; + bool m_show_all = false; + + GLModel m_vbo_sphere; + GLModel m_vbo_cylinder; + + struct SurfaceFeature { + enum Type { + Circle, + Line + }; + Type type; + Vec3d pos; + Vec3d endpoint; // for type == Line + double radius; // for type == Circle; + }; struct PlaneData { - std::vector> borders; // should be in fact local in update_planes() std::vector facets; + std::vector> borders; // should be in fact local in update_planes() + std::vector surface_features; std::vector vbos; Vec3d normal; float area; }; + static void extract_features(PlaneData& plane); + // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; std::vector m_volumes_types; From 9644fd4c595a382f3bd0dd1b1eb49092d123b704 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 7 Jul 2022 12:30:26 +0200 Subject: [PATCH 008/103] Measuring: Improved visualization --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 50 ++++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 5 ++- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 6c95e70b7f..0cb1067c39 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -149,12 +149,15 @@ void GLGizmoMeasure::on_render() ++m_currently_shown_plane; m_currently_shown_plane = std::clamp(m_currently_shown_plane, 0, std::max(0, int(m_planes.size())-1)); m_imgui->text(std::to_string(m_currently_shown_plane)); - m_imgui->checkbox(wxString("Show all"), m_show_all); + m_imgui->checkbox(wxString("Show all"), m_show_all_planes); + m_imgui->checkbox(wxString("Show points"), m_show_points); + m_imgui->checkbox(wxString("Show edges"), m_show_edges); + m_imgui->checkbox(wxString("Show circles"), m_show_circles); m_imgui->end(); - int i = m_show_all ? 0 : m_currently_shown_plane; - for (int i = 0; i < (int)m_planes.size(); ++i) { + int i = m_show_all_planes ? 0 : m_currently_shown_plane; + for (; i < (int)m_planes.size(); ++i) { // Render all the borders. for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { m_planes[i].vbos[j].set_color(j == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); @@ -165,7 +168,7 @@ void GLGizmoMeasure::on_render() // Render features: for (const SurfaceFeature& feature : m_planes[i].surface_features) { Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); - if (feature.type == SurfaceFeature::Line) { + if (m_show_edges && feature.type == SurfaceFeature::Line) { auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), feature.endpoint - feature.pos); view_feature_matrix *= q; view_feature_matrix.scale(Vec3d(0.3, 0.3, (feature.endpoint - feature.pos).norm())); @@ -174,19 +177,21 @@ void GLGizmoMeasure::on_render() m_vbo_cylinder.render(); } - view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(feature.type == SurfaceFeature::Line - ? ColorRGBA(1.f, 0.f, 0.f, 1.f) - : ColorRGBA(0.f, 1.f, 0.f, 1.f)); - m_vbo_sphere.render(); + if (m_show_points && feature.type == SurfaceFeature::Line || m_show_circles && feature.type == SurfaceFeature::Circle) { + view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(feature.type == SurfaceFeature::Line + ? ColorRGBA(1.f, 0.f, 0.f, 1.f) + : ColorRGBA(0.f, 1.f, 0.f, 1.f)); + m_vbo_sphere.render(); + } shader->set_uniform("view_model_matrix", view_model_matrix); } - if (! m_show_all) + if (! m_show_all_planes) break; } } @@ -268,7 +273,7 @@ void GLGizmoMeasure::extract_features(GLGizmoMeasure::PlaneData& plane) plane.surface_features.clear(); const Vec3d& normal = plane.normal; - const double edge_threshold = 10. * (M_PI/180.); + const double edge_threshold = 25. * (M_PI/180.); @@ -276,9 +281,10 @@ void GLGizmoMeasure::extract_features(GLGizmoMeasure::PlaneData& plane) assert(border.size() > 1); assert(! border.front().isApprox(border.back())); double last_angle = 0.; - size_t first_idx = 0; + int first_idx = 0; + bool circle = false; - for (size_t i=0; i edge_threshold || i == border.size() - 1) { + bool same_as_last = Slic3r::is_approx(angle, last_angle); + + if (std::abs(angle) > edge_threshold || (! same_as_last && circle) || i == border.size() - 1) { // Current feature ended. Save it and remember current point as beginning of the next. bool is_line = (i == first_idx + 1); plane.surface_features.emplace_back(SurfaceFeature{ @@ -300,11 +308,13 @@ void GLGizmoMeasure::extract_features(GLGizmoMeasure::PlaneData& plane) 0. // FIXME }); first_idx = i; - } else if (Slic3r::is_approx(angle, last_angle)) { + circle = false; + } else if (same_as_last && ! circle) { // possibly a segment of a circle - } else { + first_idx = std::max(i-2, 0); + circle = true; + } else if (! circle) { first_idx = i; - } } last_angle = angle; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 3099366736..9534796a71 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -24,7 +24,10 @@ class GLGizmoMeasure : public GLGizmoBase private: int m_currently_shown_plane = 0; - bool m_show_all = false; + bool m_show_all_planes = false; + bool m_show_points = true; + bool m_show_edges = true; + bool m_show_circles = true; GLModel m_vbo_sphere; GLModel m_vbo_cylinder; From 00eb8661c0bfdc057dad9f3fd9472bb6069c1ebe Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 13 Jul 2022 16:37:16 +0200 Subject: [PATCH 009/103] Measuring: Improved feature detection, added circle center calculation --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 120 ++++++++++++++--------- 1 file changed, 75 insertions(+), 45 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 0cb1067c39..e00d587814 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -9,6 +9,7 @@ #include "libslic3r/Geometry/ConvexHull.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/SurfaceMesh.hpp" +#include "libslic3r/Geometry/Circle.hpp" #include @@ -177,7 +178,7 @@ void GLGizmoMeasure::on_render() m_vbo_cylinder.render(); } - if (m_show_points && feature.type == SurfaceFeature::Line || m_show_circles && feature.type == SurfaceFeature::Circle) { + if ((m_show_points && feature.type == SurfaceFeature::Line) || m_show_circles && feature.type == SurfaceFeature::Circle) { view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); @@ -185,6 +186,14 @@ void GLGizmoMeasure::on_render() ? ColorRGBA(1.f, 0.f, 0.f, 1.f) : ColorRGBA(0.f, 1.f, 0.f, 1.f)); m_vbo_sphere.render(); + + /*view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.endpoint)); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(feature.type == SurfaceFeature::Line + ? ColorRGBA(1.f, 0.f, 0.f, 1.f) + : ColorRGBA(1.f, 1.f, 0.f, 1.f)); + m_vbo_sphere.render();*/ } @@ -243,7 +252,7 @@ void GLGizmoMeasure::on_render_for_picking() update_planes(); //for (int i = 0; i < (int)m_planes.size(); ++i) { int i = m_currently_shown_plane; - if (i < m_planes.size()) { + if (i < int(m_planes.size())) { for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { m_planes[i].vbos[j].set_color(picking_color_component(j)); m_planes[i].vbos[j].render(); @@ -268,60 +277,84 @@ void GLGizmoMeasure::set_flattening_data(const ModelObject* model_object) +static std::pair get_center_and_radius(const std::vector& border, int start_idx, int end_idx, const Transform3d& trafo) +{ + Vec2ds pts; + double z = 0.; + for (int i=start_idx; i<=end_idx; ++i) { + Vec3d pt_transformed = trafo * border[i]; + z = pt_transformed.z(); + pts.emplace_back(pt_transformed.x(), pt_transformed.y()); + } + + auto circle = Geometry::circle_ransac(pts, 20); // FIXME: iterations? + + return std::make_pair(trafo.inverse() * Vec3d(circle.center.x(), circle.center.y(), z), circle.radius); +} + + + void GLGizmoMeasure::extract_features(GLGizmoMeasure::PlaneData& plane) { plane.surface_features.clear(); const Vec3d& normal = plane.normal; const double edge_threshold = 25. * (M_PI/180.); + std::vector angles; + + Eigen::Quaterniond q; + q.setFromTwoVectors(plane.normal, Vec3d::UnitZ()); + Transform3d trafo = Transform3d::Identity(); + trafo.rotate(q); for (const std::vector& border : plane.borders) { assert(border.size() > 1); assert(! border.front().isApprox(border.back())); - double last_angle = 0.; - int first_idx = 0; - bool circle = false; + int start_idx = -1; + + // First calculate angles at all the vertices. + angles.clear(); for (int i=0; i edge_threshold || (! same_as_last && circle) || i == border.size() - 1) { - // Current feature ended. Save it and remember current point as beginning of the next. - bool is_line = (i == first_idx + 1); - plane.surface_features.emplace_back(SurfaceFeature{ - is_line ? SurfaceFeature::Line : SurfaceFeature::Circle, - is_line ? border[first_idx] : border[first_idx], // FIXME - border[i], - 0. // FIXME - }); - first_idx = i; - circle = false; - } else if (same_as_last && ! circle) { - // possibly a segment of a circle - first_idx = std::max(i-2, 0); + + bool circle = false; + std::vector> circles; + for (int i=1; i center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); + plane.surface_features.emplace_back(SurfaceFeature{ + SurfaceFeature::Circle, + // border[start_idx], border[end_idx], + center_and_radius.first, center_and_radius.first, center_and_radius.second + }); + } std::cout << "==================== " << std::endl; @@ -352,7 +385,7 @@ void GLGizmoMeasure::update_planes() ch.merge(vol_ch); } m_planes.clear(); - const Transform3d& inst_matrix = mo->instances.front()->get_matrix(); + // Now we'll go through all the facets and append Points of facets sharing the same normal. // This part is still performed in mesh coordinate system. @@ -403,13 +436,13 @@ void GLGizmoMeasure::update_planes() assert(std::none_of(face_to_plane.begin(), face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); SurfaceMesh sm(ch.its); - for (int plane_id=0; plane_id < m_planes.size(); ++plane_id) { + for (int plane_id=0; plane_id < int(m_planes.size()); ++plane_id) { //int plane_id = 5; { const auto& facets = m_planes[plane_id].facets; m_planes[plane_id].borders.clear(); std::vector> visited(facets.size(), {false, false, false}); - for (int face_id=0; face_id()); - Vertex_index target = sm.target(he); + std::vector& last_border = m_planes[plane_id].borders.back(); + last_border.emplace_back(sm.point(sm.source(he)).cast()); + //Vertex_index target = sm.target(he); const Halfedge_index he_start = he; Face_index fi = he.face(); @@ -446,15 +480,15 @@ void GLGizmoMeasure::update_planes() assert(face_it != facets.end()); assert(*face_it == int(fi)); if (visited[face_it - facets.begin()][he.side()] && he != he_start) { - m_planes[plane_id].borders.back().resize(1); + last_border.resize(1); break; } visited[face_it - facets.begin()][he.side()] = true; - m_planes[plane_id].borders.back().emplace_back(sm.point(sm.source(he)).cast()); + last_border.emplace_back(sm.point(sm.source(he)).cast()); } while (he != he_start); - if (m_planes[plane_id].borders.back().size() == 1) + if (last_border.size() == 1) m_planes[plane_id].borders.pop_back(); } } @@ -468,11 +502,7 @@ void GLGizmoMeasure::update_planes() - // Let's prepare transformation of the normal vector from mesh to instance coordinates. - Geometry::Transformation t(inst_matrix); - Vec3d scaling = t.get_scaling_factor(); - t.set_scaling_factor(Vec3d(1./scaling(0), 1./scaling(1), 1./scaling(2))); - + // Planes are finished - let's save what we calculated it from: From fe9540130aaad4d0b5a76e6ce209fd1e49d7d4af Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 26 Jul 2022 10:12:59 +0200 Subject: [PATCH 010/103] Measuring: Separating frontend and backend --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/Measure.cpp | 323 ++++++++++++++++ src/libslic3r/Measure.hpp | 98 +++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 448 ++++------------------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 43 +-- 5 files changed, 499 insertions(+), 415 deletions(-) create mode 100644 src/libslic3r/Measure.cpp create mode 100644 src/libslic3r/Measure.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 396494a181..1c060243da 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -178,6 +178,8 @@ set(SLIC3R_SOURCES MultiMaterialSegmentation.hpp MeshNormals.hpp MeshNormals.cpp + Measure.hpp + Measure.cpp CustomGCode.cpp CustomGCode.hpp Arrange.hpp diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp new file mode 100644 index 0000000000..d5cb9c24b3 --- /dev/null +++ b/src/libslic3r/Measure.cpp @@ -0,0 +1,323 @@ +#include "Measure.hpp" + +#include "libslic3r/Geometry/Circle.hpp" +#include "libslic3r/SurfaceMesh.hpp" + + + +namespace Slic3r { +namespace Measure { + + + +static std::pair get_center_and_radius(const std::vector& border, int start_idx, int end_idx, const Transform3d& trafo) +{ + Vec2ds pts; + double z = 0.; + for (int i=start_idx; i<=end_idx; ++i) { + Vec3d pt_transformed = trafo * border[i]; + z = pt_transformed.z(); + pts.emplace_back(pt_transformed.x(), pt_transformed.y()); + } + + auto circle = Geometry::circle_ransac(pts, 20); // FIXME: iterations? + + return std::make_pair(trafo.inverse() * Vec3d(circle.center.x(), circle.center.y(), z), circle.radius); +} + + + + +class MeasuringImpl { +public: + explicit MeasuringImpl(const indexed_triangle_set& its); + struct PlaneData { + std::vector facets; + std::vector> borders; // FIXME: should be in fact local in update_planes() + std::vector> surface_features; + Vec3d normal; + float area; + }; + + const std::vector& get_features() const; + +private: + void update_planes(); + void extract_features(PlaneData& plane); + void save_features(); + + + std::vector m_planes; + std::vector m_features; + const indexed_triangle_set& m_its; +}; + + + + + + +MeasuringImpl::MeasuringImpl(const indexed_triangle_set& its) +: m_its{its} +{ + update_planes(); + + for (PlaneData& plane : m_planes) { + extract_features(plane); + + plane.borders.clear(); + plane.borders.shrink_to_fit(); + } + + save_features(); +} + + +void MeasuringImpl::update_planes() +{ + m_planes.clear(); + + // Now we'll go through all the facets and append Points of facets sharing the same normal. + // This part is still performed in mesh coordinate system. + const size_t num_of_facets = m_its.indices.size(); + std::vector face_to_plane(num_of_facets, size_t(-1)); + const std::vector face_normals = its_face_normals(m_its); + const std::vector face_neighbors = its_face_neighbors(m_its); + std::vector facet_queue(num_of_facets, 0); + int facet_queue_cnt = 0; + const stl_normal* normal_ptr = nullptr; + size_t seed_facet_idx = 0; + + auto is_same_normal = [](const stl_normal& a, const stl_normal& b) -> bool { + return (std::abs(a(0) - b(0)) < 0.001 && std::abs(a(1) - b(1)) < 0.001 && std::abs(a(2) - b(2)) < 0.001); + }; + + while (1) { + // Find next unvisited triangle: + for (; seed_facet_idx < num_of_facets; ++ seed_facet_idx) + if (face_to_plane[seed_facet_idx] == size_t(-1)) { + facet_queue[facet_queue_cnt ++] = seed_facet_idx; + normal_ptr = &face_normals[seed_facet_idx]; + face_to_plane[seed_facet_idx] = m_planes.size(); + m_planes.emplace_back(); + break; + } + if (seed_facet_idx == num_of_facets) + break; // Everything was visited already + + while (facet_queue_cnt > 0) { + int facet_idx = facet_queue[-- facet_queue_cnt]; + const stl_normal& this_normal = face_normals[facet_idx]; + if (is_same_normal(this_normal, *normal_ptr)) { + const Vec3i& face = m_its.indices[facet_idx]; + + face_to_plane[facet_idx] = m_planes.size() - 1; + m_planes.back().facets.emplace_back(facet_idx); + for (int j = 0; j < 3; ++ j) + if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && face_to_plane[neighbor_idx] == size_t(-1)) + facet_queue[facet_queue_cnt ++] = neighbor_idx; + } + } + + m_planes.back().normal = normal_ptr->cast(); + std::sort(m_planes.back().facets.begin(), m_planes.back().facets.end()); + } + + assert(std::none_of(face_to_plane.begin(), face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); + + SurfaceMesh sm(m_its); + for (int plane_id=0; plane_id < int(m_planes.size()); ++plane_id) { + //int plane_id = 5; { + const auto& facets = m_planes[plane_id].facets; + m_planes[plane_id].borders.clear(); + std::vector> visited(facets.size(), {false, false, false}); + + for (int face_id=0; face_id& last_border = m_planes[plane_id].borders.back(); + last_border.emplace_back(sm.point(sm.source(he)).cast()); + //Vertex_index target = sm.target(he); + const Halfedge_index he_start = he; + + Face_index fi = he.face(); + auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); + assert(face_it != facets.end()); + assert(*face_it == int(fi)); + visited[face_it - facets.begin()][he.side()] = true; + + do { + const Halfedge_index he_orig = he; + he = sm.next_around_target(he); + while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) + he = sm.next_around_target(he); + he = sm.opposite(he); + + Face_index fi = he.face(); + auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); + assert(face_it != facets.end()); + assert(*face_it == int(fi)); + if (visited[face_it - facets.begin()][he.side()] && he != he_start) { + last_border.resize(1); + break; + } + visited[face_it - facets.begin()][he.side()] = true; + + last_border.emplace_back(sm.point(sm.source(he)).cast()); + } while (he != he_start); + + if (last_border.size() == 1) + m_planes[plane_id].borders.pop_back(); + } + } + } + + m_planes.erase(std::remove_if(m_planes.begin(), m_planes.end(), + [](const PlaneData& p) { return p.borders.empty(); }), + m_planes.end()); +} + + + + + + +void MeasuringImpl::extract_features(PlaneData& plane) +{ + plane.surface_features.clear(); + const Vec3d& normal = plane.normal; + + const double edge_threshold = 25. * (M_PI/180.); + std::vector angles; + + Eigen::Quaterniond q; + q.setFromTwoVectors(plane.normal, Vec3d::UnitZ()); + Transform3d trafo = Transform3d::Identity(); + trafo.rotate(q); + + + + for (const std::vector& border : plane.borders) { + assert(border.size() > 1); + int start_idx = -1; + + + // First calculate angles at all the vertices. + angles.clear(); + for (int i=0; i> circles; + for (int i=1; i circles[cidx].first) + i = circles[cidx++].second; + else plane.surface_features.emplace_back(std::unique_ptr( + new Edge(border[i-1], border[i]))); + } + + // FIXME Throw away / do not create edges which are parts of circles. + + // FIXME Check and maybe merge first and last circle. + + for (const auto& [start_idx, end_idx] : circles) { + std::pair center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); + plane.surface_features.emplace_back(std::unique_ptr( + new Circle(center_and_radius.first, center_and_radius.second) + )); + } + + } +} + + + +void MeasuringImpl::save_features() +{ + m_features.clear(); + for (PlaneData& plane : m_planes) + //PlaneData& plane = m_planes[0]; + { + for (std::unique_ptr& feature : plane.surface_features) { + m_features.emplace_back(feature.get()); + } + } +} + + + +const std::vector& MeasuringImpl::get_features() const +{ + return m_features; +} + + + + + + + + + + + + + +Measuring::Measuring(const indexed_triangle_set& its) +: priv{std::make_unique(its)} +{} + +Measuring::~Measuring() {} + + +const std::vector& Measuring::get_features() const +{ + return priv->get_features(); +} + + + + + + +} // namespace Measure +} // namespace Slic3r diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp new file mode 100644 index 0000000000..bbc7d9e1e9 --- /dev/null +++ b/src/libslic3r/Measure.hpp @@ -0,0 +1,98 @@ +#ifndef Slic3r_Measure_hpp_ +#define Slic3r_Measure_hpp_ + +#include + +#include "Point.hpp" + + +struct indexed_triangle_set; + + + +namespace Slic3r { +namespace Measure { + + +enum class SurfaceFeatureType { + Edge = 1 << 0, + Circle = 1 << 1, + Plane = 1 << 2 + }; + +class SurfaceFeature { +public: + virtual SurfaceFeatureType get_type() const = 0; +}; + +class Edge : public SurfaceFeature { +public: + Edge(const Vec3d& start, const Vec3d& end) : m_start{start}, m_end{end} {} + SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Edge; } + std::pair get_edge() const { return std::make_pair(m_start, m_end); } +private: + Vec3d m_start; + Vec3d m_end; +}; + +class Circle : public SurfaceFeature { +public: + Circle(const Vec3d& center, double radius) : m_center{center}, m_radius{radius} {} + SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Circle; } + Vec3d get_center() const { return m_center; } + double get_radius() const { return m_radius; } +private: + Vec3d m_center; + double m_radius; +}; + +class Plane : public SurfaceFeature { +public: + SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Plane; } + +}; + + +class MeasuringImpl; + + +class Measuring { +public: + // Construct the measurement object on a given its. The its must remain + // valid and unchanged during the whole lifetime of the object. + explicit Measuring(const indexed_triangle_set& its); + ~Measuring(); + + // Return a reference to a list of all features identified on the its. + const std::vector& get_features() const; + + // Given a face_idx where the mouse cursor points, return a feature that + // should be highlighted or nullptr. + const SurfaceFeature* get_feature(size_t face_idx, const Vec3d& point) const; + + // Returns distance between two SurfaceFeatures. + static double get_distance(const SurfaceFeature* a, const SurfaceFeature* b); + + // Returns true if an x/y/z distance between features makes sense. + // If so, result contains the distances. + static bool get_distances(const SurfaceFeature* a, const SurfaceFeature* b, std::array& result); + + // Returns true if an x/y/z distance between feature and a point makes sense. + // If so, result contains the distances. + static bool get_axis_aligned_distances(const SurfaceFeature* feature, const Vec3d* pt, std::array& result); + + // Returns true if measuring angles between features makes sense. + // If so, result contains the angle in radians. + static bool get_angle(const SurfaceFeature* a, const SurfaceFeature* b, double& result); + + +private: + + std::unique_ptr priv; +}; + + +} // namespace Measure +} // namespace Slic3r + +#endif // Slic3r_Measure_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index e00d587814..043adba976 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -6,10 +6,8 @@ #include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" -#include "libslic3r/Geometry/ConvexHull.hpp" #include "libslic3r/Model.hpp" -#include "libslic3r/SurfaceMesh.hpp" -#include "libslic3r/Geometry/Circle.hpp" +#include "libslic3r/Measure.hpp" #include @@ -30,6 +28,10 @@ GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filen bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) { + m_mouse_pos_x = mouse_event.GetX(); + m_mouse_pos_y = mouse_event.GetY(); + + if (mouse_event.Moving()) { // only for sure m_mouse_left_down = false; @@ -38,12 +40,7 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) if (mouse_event.LeftDown()) { if (m_hover_id != -1) { m_mouse_left_down = true; - Selection &selection = m_parent.get_selection(); - if (selection.is_single_full_instance()) { - // Rotate the object so the normal points downward: - selection.flattening_rotate(m_planes[m_hover_id].normal); - m_parent.do_rotate(L("Gizmo-Place on Face")); - } + return true; } @@ -94,7 +91,7 @@ void GLGizmoMeasure::on_set_state() CommonGizmosDataID GLGizmoMeasure::on_get_requirements() const { - return CommonGizmosDataID::SelectionInfo; + return CommonGizmosDataID(int(CommonGizmosDataID::SelectionInfo) | int(CommonGizmosDataID::Raycaster)); } @@ -139,70 +136,59 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - if (this->is_plane_update_necessary()) - update_planes(); + + + update_if_needed(); + m_imgui->begin(std::string("DEBUG")); - if (m_imgui->button("<-")) - --m_currently_shown_plane; - ImGui::SameLine(); - if (m_imgui->button("->")) - ++m_currently_shown_plane; - m_currently_shown_plane = std::clamp(m_currently_shown_plane, 0, std::max(0, int(m_planes.size())-1)); - m_imgui->text(std::to_string(m_currently_shown_plane)); - m_imgui->checkbox(wxString("Show all"), m_show_all_planes); - m_imgui->checkbox(wxString("Show points"), m_show_points); - m_imgui->checkbox(wxString("Show edges"), m_show_edges); - m_imgui->checkbox(wxString("Show circles"), m_show_circles); - m_imgui->end(); + + m_imgui->checkbox(wxString("Show all features"), m_show_all); + + Vec3f pos; + Vec3f normal; + size_t facet_idx; + m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), m, camera, pos, normal, nullptr, &facet_idx); + ImGui::Separator(); + m_imgui->text(std::string("face_idx: ") + std::to_string(facet_idx)); + m_imgui->text(std::string("pos_x: ") + std::to_string(pos.x())); + m_imgui->text(std::string("pos_y: ") + std::to_string(pos.y())); + m_imgui->text(std::string("pos_z: ") + std::to_string(pos.z())); - int i = m_show_all_planes ? 0 : m_currently_shown_plane; - for (; i < (int)m_planes.size(); ++i) { - // Render all the borders. - for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { - m_planes[i].vbos[j].set_color(j == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); - m_planes[i].vbos[j].render(); - } + + if (m_show_all) { + const std::vector features = m_measuring->get_features(); + for (const Measure::SurfaceFeature* feature : features) { + + if (feature->get_type() == Measure::SurfaceFeatureType::Circle) { + const auto* circle = static_cast(feature); + Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); + view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(ColorRGBA(0.f, 1.f, 0.f, 1.f)); + m_vbo_sphere.render(); + } - // Render features: - for (const SurfaceFeature& feature : m_planes[i].surface_features) { - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); - if (m_show_edges && feature.type == SurfaceFeature::Line) { - auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), feature.endpoint - feature.pos); + else if (feature->get_type() == Measure::SurfaceFeatureType::Edge) { + const auto* edge = static_cast(feature); + auto& [start, end] = edge->get_edge(); + Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(start)); + auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); view_feature_matrix *= q; - view_feature_matrix.scale(Vec3d(0.3, 0.3, (feature.endpoint - feature.pos).norm())); + view_feature_matrix.scale(Vec3d(0.075, 0.075, (end - start).norm())); shader->set_uniform("view_model_matrix", view_feature_matrix); m_vbo_cylinder.set_color(ColorRGBA(0.7f, 0.7f, 0.f, 1.f)); m_vbo_cylinder.render(); } - if ((m_show_points && feature.type == SurfaceFeature::Line) || m_show_circles && feature.type == SurfaceFeature::Circle) { - view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(feature.type == SurfaceFeature::Line - ? ColorRGBA(1.f, 0.f, 0.f, 1.f) - : ColorRGBA(0.f, 1.f, 0.f, 1.f)); - m_vbo_sphere.render(); - - /*view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.endpoint)); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(feature.type == SurfaceFeature::Line - ? ColorRGBA(1.f, 0.f, 0.f, 1.f) - : ColorRGBA(1.f, 1.f, 0.f, 1.f)); - m_vbo_sphere.render();*/ - } - - shader->set_uniform("view_model_matrix", view_model_matrix); } - - if (! m_show_all_planes) - break; + shader->set_uniform("view_model_matrix", view_model_matrix); } + m_imgui->end(); } glsafe(::glEnable(GL_CULL_FACE)); @@ -222,290 +208,45 @@ void GLGizmoMeasure::on_render() #error NOT IMPLEMENTED #endif - - - - void GLGizmoMeasure::on_render_for_picking() { - const Selection& selection = m_parent.get_selection(); - - GLShaderProgram* shader = wxGetApp().get_shader("flat"); - if (shader == nullptr) - return; - - shader->start_using(); - - glsafe(::glDisable(GL_DEPTH_TEST)); - glsafe(::glDisable(GL_BLEND)); - glsafe(::glLineWidth(2.f)); - - if (selection.is_single_full_instance() && !wxGetKeyState(WXK_CONTROL)) { - const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); - const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d view_model_matrix = camera.get_view_matrix() * - Geometry::assemble_transform(selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z() * Vec3d::UnitZ()) * m; - - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - if (this->is_plane_update_necessary()) - update_planes(); - //for (int i = 0; i < (int)m_planes.size(); ++i) { - int i = m_currently_shown_plane; - if (i < int(m_planes.size())) { - for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { - m_planes[i].vbos[j].set_color(picking_color_component(j)); - m_planes[i].vbos[j].render(); - } - } - } - - glsafe(::glEnable(GL_CULL_FACE)); - - shader->stop_using(); } void GLGizmoMeasure::set_flattening_data(const ModelObject* model_object) { - if (model_object != m_old_model_object) { - m_planes.clear(); - m_planes_valid = false; - } + if (model_object != m_old_model_object) + update_if_needed(); } - -static std::pair get_center_and_radius(const std::vector& border, int start_idx, int end_idx, const Transform3d& trafo) -{ - Vec2ds pts; - double z = 0.; - for (int i=start_idx; i<=end_idx; ++i) { - Vec3d pt_transformed = trafo * border[i]; - z = pt_transformed.z(); - pts.emplace_back(pt_transformed.x(), pt_transformed.y()); - } - - auto circle = Geometry::circle_ransac(pts, 20); // FIXME: iterations? - - return std::make_pair(trafo.inverse() * Vec3d(circle.center.x(), circle.center.y(), z), circle.radius); -} - - - -void GLGizmoMeasure::extract_features(GLGizmoMeasure::PlaneData& plane) -{ - plane.surface_features.clear(); - const Vec3d& normal = plane.normal; - - const double edge_threshold = 25. * (M_PI/180.); - std::vector angles; - - Eigen::Quaterniond q; - q.setFromTwoVectors(plane.normal, Vec3d::UnitZ()); - Transform3d trafo = Transform3d::Identity(); - trafo.rotate(q); - - - - for (const std::vector& border : plane.borders) { - assert(border.size() > 1); - assert(! border.front().isApprox(border.back())); - int start_idx = -1; - - - // First calculate angles at all the vertices. - angles.clear(); - for (int i=0; i> circles; - for (int i=1; i center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); - plane.surface_features.emplace_back(SurfaceFeature{ - SurfaceFeature::Circle, - // border[start_idx], border[end_idx], - center_and_radius.first, center_and_radius.first, center_and_radius.second - }); - } - - - std::cout << "==================== " << std::endl; - } - - - for (const SurfaceFeature& f : plane.surface_features) { - std::cout << "- detected " << (f.type == SurfaceFeature::Line ? "Line" : "Circle") << std::endl; - std::cout<< f.pos << std::endl << std::endl << f.endpoint << std::endl; - std::cout << "----------------" << std::endl; - } - - - -} - - - -void GLGizmoMeasure::update_planes() +void GLGizmoMeasure::update_if_needed() { const ModelObject* mo = m_c->selection_info()->model_object(); - TriangleMesh ch; - for (const ModelVolume* vol : mo->volumes) { - if (vol->type() != ModelVolumeType::MODEL_PART) - continue; - TriangleMesh vol_ch = vol->mesh(); - vol_ch.transform(vol->get_matrix()); - ch.merge(vol_ch); - } - m_planes.clear(); - + if (m_state != On || ! mo || mo->instances.empty()) + return; - // Now we'll go through all the facets and append Points of facets sharing the same normal. - // This part is still performed in mesh coordinate system. - const size_t num_of_facets = ch.facets_count(); - std::vector face_to_plane(num_of_facets, size_t(-1)); - const std::vector face_normals = its_face_normals(ch.its); - const std::vector face_neighbors = its_face_neighbors(ch.its); - std::vector facet_queue(num_of_facets, 0); - int facet_queue_cnt = 0; - const stl_normal* normal_ptr = nullptr; - size_t seed_facet_idx = 0; + if (! m_measuring || mo != m_old_model_object + || mo->volumes.size() != m_volumes_matrices.size()) + goto UPDATE; - auto is_same_normal = [](const stl_normal& a, const stl_normal& b) -> bool { - return (std::abs(a(0) - b(0)) < 0.001 && std::abs(a(1) - b(1)) < 0.001 && std::abs(a(2) - b(2)) < 0.001); - }; + // We want to recalculate when the scale changes - some planes could (dis)appear. + if (! mo->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale) + || ! mo->instances.front()->get_mirror().isApprox(m_first_instance_mirror)) + goto UPDATE; - while (1) { - // Find next unvisited triangle: - for (; seed_facet_idx < num_of_facets; ++ seed_facet_idx) - if (face_to_plane[seed_facet_idx] == size_t(-1)) { - facet_queue[facet_queue_cnt ++] = seed_facet_idx; - normal_ptr = &face_normals[seed_facet_idx]; - face_to_plane[seed_facet_idx] = m_planes.size(); - m_planes.emplace_back(); - break; - } - if (seed_facet_idx == num_of_facets) - break; // Everything was visited already + for (unsigned int i=0; i < mo->volumes.size(); ++i) + if (! mo->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i]) + || mo->volumes[i]->type() != m_volumes_types[i]) + goto UPDATE; - while (facet_queue_cnt > 0) { - int facet_idx = facet_queue[-- facet_queue_cnt]; - const stl_normal& this_normal = face_normals[facet_idx]; - if (is_same_normal(this_normal, *normal_ptr)) { - const Vec3i& face = ch.its.indices[facet_idx]; + return; - face_to_plane[facet_idx] = m_planes.size() - 1; - m_planes.back().facets.emplace_back(facet_idx); - for (int j = 0; j < 3; ++ j) - if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && face_to_plane[neighbor_idx] == size_t(-1)) - facet_queue[facet_queue_cnt ++] = neighbor_idx; - } - } +UPDATE: + m_measuring.reset(new Measure::Measuring(mo->volumes.front()->mesh().its)); - m_planes.back().normal = normal_ptr->cast(); - std::sort(m_planes.back().facets.begin(), m_planes.back().facets.end()); - } - - assert(std::none_of(face_to_plane.begin(), face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); - - SurfaceMesh sm(ch.its); - for (int plane_id=0; plane_id < int(m_planes.size()); ++plane_id) { - //int plane_id = 5; { - const auto& facets = m_planes[plane_id].facets; - m_planes[plane_id].borders.clear(); - std::vector> visited(facets.size(), {false, false, false}); - - for (int face_id=0; face_id& last_border = m_planes[plane_id].borders.back(); - last_border.emplace_back(sm.point(sm.source(he)).cast()); - //Vertex_index target = sm.target(he); - const Halfedge_index he_start = he; - - Face_index fi = he.face(); - auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); - assert(face_it != facets.end()); - assert(*face_it == int(fi)); - visited[face_it - facets.begin()][he.side()] = true; - - do { - const Halfedge_index he_orig = he; - he = sm.next_around_target(he); - while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) - he = sm.next_around_target(he); - he = sm.opposite(he); - - Face_index fi = he.face(); - auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); - assert(face_it != facets.end()); - assert(*face_it == int(fi)); - if (visited[face_it - facets.begin()][he.side()] && he != he_start) { - last_border.resize(1); - break; - } - visited[face_it - facets.begin()][he.side()] = true; - - last_border.emplace_back(sm.point(sm.source(he)).cast()); - } while (he != he_start); - - if (last_border.size() == 1) - m_planes[plane_id].borders.pop_back(); - } - } - } - - - // DEBUGGING: - m_planes.erase(std::remove_if(m_planes.begin(), m_planes.end(), [](const PlaneData& p) { return p.borders.empty(); }), m_planes.end()); - - - - - - - - - // Planes are finished - let's save what we calculated it from: + // Let's save what we calculated it from: m_volumes_matrices.clear(); m_volumes_types.clear(); for (const ModelVolume* vol : mo->volumes) { @@ -515,65 +256,6 @@ void GLGizmoMeasure::update_planes() m_first_instance_scale = mo->instances.front()->get_scaling_factor(); m_first_instance_mirror = mo->instances.front()->get_mirror(); m_old_model_object = mo; - - // And finally create respective VBOs. The polygon is convex with - // the vertices in order, so triangulation is trivial. - for (PlaneData& plane : m_planes) { - for (std::vector& vertices : plane.borders) { - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3N3 }; - init_data.reserve_vertices(vertices.size()); - init_data.reserve_indices(vertices.size()); - // vertices + indices - for (size_t i = 0; i < vertices.size(); ++i) { - init_data.add_vertex((Vec3f)vertices[i].cast(), (Vec3f)plane.normal.cast()); - init_data.add_index((unsigned int)i); - } - plane.vbos.emplace_back(); - plane.vbos.back().init_from(std::move(init_data)); - vertices.pop_back(); // first and last are the same - } - - static int n=0; - std::cout << "==================== " << std::endl; - std::cout << "==================== " << std::endl; - std::cout << "==================== " << std::endl; - std::cout << "Plane num. " << n++ << std::endl; - extract_features(plane); - - - // FIXME: vertices should really be local, they need not - // persist now when we use VBOs - plane.borders.clear(); - plane.borders.shrink_to_fit(); - } - - m_planes_valid = true; -} - - - -bool GLGizmoMeasure::is_plane_update_necessary() const -{ - const ModelObject* mo = m_c->selection_info()->model_object(); - if (m_state != On || ! mo || mo->instances.empty()) - return false; - - if (! m_planes_valid || mo != m_old_model_object - || mo->volumes.size() != m_volumes_matrices.size()) - return true; - - // We want to recalculate when the scale changes - some planes could (dis)appear. - if (! mo->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale) - || ! mo->instances.front()->get_mirror().isApprox(m_first_instance_mirror)) - return true; - - for (unsigned int i=0; i < mo->volumes.size(); ++i) - if (! mo->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i]) - || mo->volumes[i]->type() != m_volumes_types[i]) - return true; - - return false; } } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 9534796a71..2781d2b353 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -9,10 +9,15 @@ #endif // ENABLE_LEGACY_OPENGL_REMOVAL +#include + + namespace Slic3r { enum class ModelVolumeType : int; +namespace Measure { class Measuring; } + namespace GUI { @@ -22,53 +27,27 @@ class GLGizmoMeasure : public GLGizmoBase // This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself. private: - - int m_currently_shown_plane = 0; - bool m_show_all_planes = false; - bool m_show_points = true; - bool m_show_edges = true; - bool m_show_circles = true; + std::unique_ptr m_measuring; GLModel m_vbo_sphere; GLModel m_vbo_cylinder; - struct SurfaceFeature { - enum Type { - Circle, - Line - }; - Type type; - Vec3d pos; - Vec3d endpoint; // for type == Line - double radius; // for type == Circle; - }; - - struct PlaneData { - std::vector facets; - std::vector> borders; // should be in fact local in update_planes() - std::vector surface_features; - std::vector vbos; - Vec3d normal; - float area; - }; - - static void extract_features(PlaneData& plane); - // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; std::vector m_volumes_types; Vec3d m_first_instance_scale; Vec3d m_first_instance_mirror; - std::vector m_planes; - std::vector m_face_to_plane; bool m_mouse_left_down = false; // for detection left_up of this gizmo bool m_planes_valid = false; const ModelObject* m_old_model_object = nullptr; std::vector instances_matrices; - void update_planes(); - bool is_plane_update_necessary() const; + int m_mouse_pos_x; + int m_mouse_pos_y; + bool m_show_all = true; + + void update_if_needed(); void set_flattening_data(const ModelObject* model_object); public: From c8e9622ab21e186f24e580a596d05ecc78e1905c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 27 Jul 2022 09:58:21 +0200 Subject: [PATCH 011/103] Measuring: further separating frontend and backend --- src/libslic3r/Measure.cpp | 236 +++++++++++++++-------- src/libslic3r/Measure.hpp | 24 ++- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 91 ++++++--- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 4 +- 4 files changed, 233 insertions(+), 122 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index d5cb9c24b3..a8ee3d3265 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -39,16 +39,19 @@ public: float area; }; - const std::vector& get_features() const; + const std::vector& get_features() const; + const SurfaceFeature* get_feature(size_t face_idx, const Vec3d& point) const; + const std::vector> get_planes_triangle_indices() const; private: void update_planes(); - void extract_features(PlaneData& plane); + void extract_features(); void save_features(); std::vector m_planes; - std::vector m_features; + std::vector m_face_to_plane; + std::vector m_features; const indexed_triangle_set& m_its; }; @@ -61,14 +64,7 @@ MeasuringImpl::MeasuringImpl(const indexed_triangle_set& its) : m_its{its} { update_planes(); - - for (PlaneData& plane : m_planes) { - extract_features(plane); - - plane.borders.clear(); - plane.borders.shrink_to_fit(); - } - + extract_features(); save_features(); } @@ -80,7 +76,7 @@ void MeasuringImpl::update_planes() // Now we'll go through all the facets and append Points of facets sharing the same normal. // This part is still performed in mesh coordinate system. const size_t num_of_facets = m_its.indices.size(); - std::vector face_to_plane(num_of_facets, size_t(-1)); + m_face_to_plane.resize(num_of_facets, size_t(-1)); const std::vector face_normals = its_face_normals(m_its); const std::vector face_neighbors = its_face_neighbors(m_its); std::vector facet_queue(num_of_facets, 0); @@ -95,10 +91,10 @@ void MeasuringImpl::update_planes() while (1) { // Find next unvisited triangle: for (; seed_facet_idx < num_of_facets; ++ seed_facet_idx) - if (face_to_plane[seed_facet_idx] == size_t(-1)) { + if (m_face_to_plane[seed_facet_idx] == size_t(-1)) { facet_queue[facet_queue_cnt ++] = seed_facet_idx; normal_ptr = &face_normals[seed_facet_idx]; - face_to_plane[seed_facet_idx] = m_planes.size(); + m_face_to_plane[seed_facet_idx] = m_planes.size(); m_planes.emplace_back(); break; } @@ -111,10 +107,10 @@ void MeasuringImpl::update_planes() if (is_same_normal(this_normal, *normal_ptr)) { const Vec3i& face = m_its.indices[facet_idx]; - face_to_plane[facet_idx] = m_planes.size() - 1; + m_face_to_plane[facet_idx] = m_planes.size() - 1; m_planes.back().facets.emplace_back(facet_idx); for (int j = 0; j < 3; ++ j) - if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && face_to_plane[neighbor_idx] == size_t(-1)) + if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && m_face_to_plane[neighbor_idx] == size_t(-1)) facet_queue[facet_queue_cnt ++] = neighbor_idx; } } @@ -123,7 +119,7 @@ void MeasuringImpl::update_planes() std::sort(m_planes.back().facets.begin(), m_planes.back().facets.end()); } - assert(std::none_of(face_to_plane.begin(), face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); + assert(std::none_of(m_face_to_plane.begin(), m_face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); SurfaceMesh sm(m_its); for (int plane_id=0; plane_id < int(m_planes.size()); ++plane_id) { @@ -133,9 +129,9 @@ void MeasuringImpl::update_planes() std::vector> visited(facets.size(), {false, false, false}); for (int face_id=0; face_id angles; - Eigen::Quaterniond q; - q.setFromTwoVectors(plane.normal, Vec3d::UnitZ()); - Transform3d trafo = Transform3d::Identity(); - trafo.rotate(q); - - - for (const std::vector& border : plane.borders) { - assert(border.size() > 1); - int start_idx = -1; + for (int i=0; i& border : plane.borders) { + assert(border.size() > 1); + int start_idx = -1; + + // First calculate angles at all the vertices. + angles.clear(); + for (int i=0; i> circles; - for (int i=1; i> circles; + for (int i=1; i circles[cidx].first) + i = circles[cidx++].second; + else plane.surface_features.emplace_back(std::unique_ptr( + new Edge(border[i-1], border[i]))); + } + + // FIXME Throw away / do not create edges which are parts of circles or + // which lead to circle points (unless they belong to the same plane.) + + // FIXME Check and merge first and last circle if needed. + + // Now create the circle-typed surface features. + for (const auto& [start_idx, end_idx] : circles) { + std::pair center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); + plane.surface_features.emplace_back(std::unique_ptr( + new Circle(center_and_radius.first, center_and_radius.second))); + } + } - // We have the circles. Now go around again and pick edges. - int cidx = 0; // index of next circle in the way - for (int i=1; i circles[cidx].first) - i = circles[cidx++].second; - else plane.surface_features.emplace_back(std::unique_ptr( - new Edge(border[i-1], border[i]))); - } - - // FIXME Throw away / do not create edges which are parts of circles. - - // FIXME Check and maybe merge first and last circle. - - for (const auto& [start_idx, end_idx] : circles) { - std::pair center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); - plane.surface_features.emplace_back(std::unique_ptr( - new Circle(center_and_radius.first, center_and_radius.second) - )); - } + // The last surface feature is the plane itself. + plane.surface_features.emplace_back(std::unique_ptr( + new Plane(i))); + plane.borders.clear(); + plane.borders.shrink_to_fit(); } } @@ -277,7 +284,7 @@ void MeasuringImpl::save_features() for (PlaneData& plane : m_planes) //PlaneData& plane = m_planes[0]; { - for (std::unique_ptr& feature : plane.surface_features) { + for (const std::unique_ptr& feature : plane.surface_features) { m_features.emplace_back(feature.get()); } } @@ -285,13 +292,51 @@ void MeasuringImpl::save_features() -const std::vector& MeasuringImpl::get_features() const +const SurfaceFeature* MeasuringImpl::get_feature(size_t face_idx, const Vec3d& point) const +{ + if (face_idx >= m_face_to_plane.size()) + return nullptr; + + const PlaneData& plane = m_planes[m_face_to_plane[face_idx]]; + + const SurfaceFeature* closest_feature = nullptr; + double min_dist = std::numeric_limits::max(); + + for (const std::unique_ptr& feature : plane.surface_features) { + double dist = Measuring::get_distance(feature.get(), &point); + if (dist < 0.5 && dist < min_dist) { + min_dist = std::min(dist, min_dist); + closest_feature = feature.get(); + } + } + + if (closest_feature) + return closest_feature; + + // Nothing detected, return the plane as a whole. + assert(plane.surface_features.back().get()->get_type() == SurfaceFeatureType::Plane); + return plane.surface_features.back().get(); +} + + + +const std::vector& MeasuringImpl::get_features() const { return m_features; } +const std::vector> MeasuringImpl::get_planes_triangle_indices() const +{ + std::vector> out; + for (const PlaneData& plane : m_planes) + out.emplace_back(plane.facets); + return out; +} + + + @@ -309,12 +354,39 @@ Measuring::Measuring(const indexed_triangle_set& its) Measuring::~Measuring() {} -const std::vector& Measuring::get_features() const +const std::vector& Measuring::get_features() const { return priv->get_features(); } +const SurfaceFeature* Measuring::get_feature(size_t face_idx, const Vec3d& point) const +{ + return priv->get_feature(face_idx, point); +} + + + +const std::vector> Measuring::get_planes_triangle_indices() const +{ + return priv->get_planes_triangle_indices(); +} + + + +double Measuring::get_distance(const SurfaceFeature* feature, const Vec3d* pt) +{ + if (feature->get_type() == SurfaceFeatureType::Edge) { + const Edge* edge = static_cast(feature); + const auto& [s,e] = edge->get_edge(); + Eigen::ParametrizedLine line(s, (e-s).normalized()); + return line.distance(*pt); + } + + return std::numeric_limits::max(); +} + + diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index bbc7d9e1e9..1360b47ff7 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -48,8 +48,12 @@ private: class Plane : public SurfaceFeature { public: + Plane(int idx) : m_idx(idx) {} SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Plane; } + int get_plane_idx() const { return m_idx; } // index into vector provided by Measuring::get_plane_triangle_indices +private: + int m_idx; }; @@ -64,30 +68,30 @@ public: ~Measuring(); // Return a reference to a list of all features identified on the its. - const std::vector& get_features() const; + [[deprecated]]const std::vector& get_features() const; // Given a face_idx where the mouse cursor points, return a feature that // should be highlighted or nullptr. const SurfaceFeature* get_feature(size_t face_idx, const Vec3d& point) const; + // Returns a list of triangle indices for each identified plane. Each + // Plane object contains an index into this vector. + const std::vector> get_planes_triangle_indices() const; + + + // Returns distance between two SurfaceFeatures. static double get_distance(const SurfaceFeature* a, const SurfaceFeature* b); - // Returns true if an x/y/z distance between features makes sense. - // If so, result contains the distances. - static bool get_distances(const SurfaceFeature* a, const SurfaceFeature* b, std::array& result); - - // Returns true if an x/y/z distance between feature and a point makes sense. - // If so, result contains the distances. - static bool get_axis_aligned_distances(const SurfaceFeature* feature, const Vec3d* pt, std::array& result); + // Returns distance between a SurfaceFeature and a point. + static double get_distance(const SurfaceFeature* a, const Vec3d* pt); // Returns true if measuring angles between features makes sense. // If so, result contains the angle in radians. static bool get_angle(const SurfaceFeature* a, const SurfaceFeature* b, double& result); -private: - +private: std::unique_ptr priv; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 043adba976..a8b257a937 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -144,6 +144,7 @@ void GLGizmoMeasure::on_render() m_imgui->begin(std::string("DEBUG")); m_imgui->checkbox(wxString("Show all features"), m_show_all); + m_imgui->checkbox(wxString("Show all planes"), m_show_planes); Vec3f pos; Vec3f normal; @@ -157,37 +158,51 @@ void GLGizmoMeasure::on_render() - if (m_show_all) { - const std::vector features = m_measuring->get_features(); - for (const Measure::SurfaceFeature* feature : features) { + std::vector features = {m_measuring->get_feature(facet_idx, pos.cast())}; + if (m_show_all) { + features = m_measuring->get_features(); + features.erase(std::remove_if(features.begin(), features.end(), + [](const Measure::SurfaceFeature* f) { + return f->get_type() == Measure::SurfaceFeatureType::Plane; + }), features.end()); + } + + + for (const Measure::SurfaceFeature* feature : features) { + if (! feature) + continue; - if (feature->get_type() == Measure::SurfaceFeatureType::Circle) { - const auto* circle = static_cast(feature); - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); - view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(ColorRGBA(0.f, 1.f, 0.f, 1.f)); - m_vbo_sphere.render(); - } - - - else if (feature->get_type() == Measure::SurfaceFeatureType::Edge) { - const auto* edge = static_cast(feature); - auto& [start, end] = edge->get_edge(); - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(start)); - auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); - view_feature_matrix *= q; - view_feature_matrix.scale(Vec3d(0.075, 0.075, (end - start).norm())); - shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_cylinder.set_color(ColorRGBA(0.7f, 0.7f, 0.f, 1.f)); - m_vbo_cylinder.render(); - } - - + if (feature->get_type() == Measure::SurfaceFeatureType::Circle) { + const auto* circle = static_cast(feature); + Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); + view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(ColorRGBA(0.f, 1.f, 0.f, 1.f)); + m_vbo_sphere.render(); } - shader->set_uniform("view_model_matrix", view_model_matrix); + else if (feature->get_type() == Measure::SurfaceFeatureType::Edge) { + const auto* edge = static_cast(feature); + auto& [start, end] = edge->get_edge(); + Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(start)); + auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); + view_feature_matrix *= q; + view_feature_matrix.scale(Vec3d(0.075, 0.075, (end - start).norm())); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_cylinder.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_cylinder.render(); + } + else if (feature->get_type() == Measure::SurfaceFeatureType::Plane) { + const auto* plane = static_cast(feature); + assert(plane->get_plane_idx() < m_plane_models.size()); + m_plane_models[plane->get_plane_idx()]->render(); + } } + shader->set_uniform("view_model_matrix", view_model_matrix); + if (m_show_planes) + for (const auto& glmodel : m_plane_models) + glmodel->render(); + m_imgui->end(); } @@ -244,7 +259,25 @@ void GLGizmoMeasure::update_if_needed() return; UPDATE: - m_measuring.reset(new Measure::Measuring(mo->volumes.front()->mesh().its)); + const indexed_triangle_set& its = mo->volumes.front()->mesh().its; + m_measuring.reset(new Measure::Measuring(its)); + m_plane_models.clear(); + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + for (const std::vector& triangle_indices : planes_triangles) { + m_plane_models.emplace_back(std::unique_ptr(new GLModel())); + GUI::GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA(0.9f, 0.9f, 0.9f, 0.5f); + int i = 0; + for (int idx : triangle_indices) { + init_data.add_vertex(its.vertices[its.indices[idx][0]]); + init_data.add_vertex(its.vertices[its.indices[idx][1]]); + init_data.add_vertex(its.vertices[its.indices[idx][2]]); + init_data.add_triangle(i, i+1, i+2); + i+=3; + } + m_plane_models.back()->init_from(std::move(init_data)); + } // Let's save what we calculated it from: m_volumes_matrices.clear(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 2781d2b353..f74b82fa46 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -45,7 +45,9 @@ private: int m_mouse_pos_x; int m_mouse_pos_y; - bool m_show_all = true; + bool m_show_all = false; + bool m_show_planes = false; + std::vector> m_plane_models; void update_if_needed(); void set_flattening_data(const ModelObject* model_object); From f68e7526b215428315d8fc269fe78cce80bb8303 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 27 Jul 2022 11:45:42 +0200 Subject: [PATCH 012/103] Measuring: added getters for circle visualization --- src/libslic3r/Measure.cpp | 11 ++++++++++- src/libslic3r/Measure.hpp | 8 +++++++- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 22 +++++++++++++++++++--- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index a8ee3d3265..724f4ab806 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -262,7 +262,7 @@ void MeasuringImpl::extract_features() for (const auto& [start_idx, end_idx] : circles) { std::pair center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); plane.surface_features.emplace_back(std::unique_ptr( - new Circle(center_and_radius.first, center_and_radius.second))); + new Circle(center_and_radius.first, center_and_radius.second, plane.normal))); } } @@ -382,6 +382,15 @@ double Measuring::get_distance(const SurfaceFeature* feature, const Vec3d* pt) Eigen::ParametrizedLine line(s, (e-s).normalized()); return line.distance(*pt); } + else if (feature->get_type() == SurfaceFeatureType::Circle) { + const Circle* circle = static_cast(feature); + // Find a plane containing normal, center and the point. + const Vec3d& c = circle->get_center(); + const Vec3d& n = circle->get_normal(); + Eigen::Hyperplane circle_plane(n, c); + Vec3d proj = circle_plane.projection(*pt); + return std::sqrt( std::pow((proj - c).norm() - circle->get_radius(), 2.) + (*pt - proj).squaredNorm()); + } return std::numeric_limits::max(); } diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 1360b47ff7..0455291bfc 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -28,22 +28,28 @@ public: class Edge : public SurfaceFeature { public: Edge(const Vec3d& start, const Vec3d& end) : m_start{start}, m_end{end} {} + Edge(const Vec3d& start, const Vec3d& end, const Vec3d& pin) : m_start{start}, m_end{end}, + m_pin{std::unique_ptr(new Vec3d(pin))} {} SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Edge; } std::pair get_edge() const { return std::make_pair(m_start, m_end); } private: Vec3d m_start; Vec3d m_end; + std::unique_ptr m_pin; }; class Circle : public SurfaceFeature { public: - Circle(const Vec3d& center, double radius) : m_center{center}, m_radius{radius} {} + Circle(const Vec3d& center, double radius, const Vec3d& normal) + : m_center{center}, m_radius{radius}, m_normal{normal} {} SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Circle; } Vec3d get_center() const { return m_center; } double get_radius() const { return m_radius; } + Vec3d get_normal() const { return m_normal; } private: Vec3d m_center; double m_radius; + Vec3d m_normal; }; class Plane : public SurfaceFeature { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index a8b257a937..44dd44cc55 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -174,12 +174,28 @@ void GLGizmoMeasure::on_render() if (feature->get_type() == Measure::SurfaceFeatureType::Circle) { const auto* circle = static_cast(feature); - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); - view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); + const Vec3d& c = circle->get_center(); + const Vec3d& n = circle->get_normal(); + Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(c)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(ColorRGBA(0.f, 1.f, 0.f, 1.f)); + m_vbo_sphere.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); m_vbo_sphere.render(); + + // Now draw the circle itself - let's take a funny shortcut: + Vec3d rad = n.cross(Vec3d::UnitX()); + if (rad.squaredNorm() < 0.1) + rad = n.cross(Vec3d::UnitY()); + rad *= circle->get_radius() * rad.norm(); + const int N = 20; + for (int i=0; iset_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.render(); + } } else if (feature->get_type() == Measure::SurfaceFeatureType::Edge) { const auto* edge = static_cast(feature); From 31baf5859bb85df8f977ce173d5e9f30b7544c75 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 27 Jul 2022 15:36:21 +0200 Subject: [PATCH 013/103] Measuring: Add detection of polygons and their centers --- src/libslic3r/Measure.cpp | 72 ++++++++++++++++++------ src/libslic3r/Measure.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 8 +++ 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 724f4ab806..056178bc4e 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -192,10 +192,11 @@ void MeasuringImpl::update_planes() void MeasuringImpl::extract_features() { - - - const double edge_threshold = 25. * (M_PI/180.); + auto N_to_angle = [](double N) -> double { return 2.*M_PI / N; }; + constexpr double polygon_upper_threshold = N_to_angle(4.5); + constexpr double polygon_lower_threshold = N_to_angle(8.5); std::vector angles; + std::vector lengths; for (int i=0; i M_PI) + angle = 2*M_PI - angle; + angles.push_back(angle); + lengths.push_back(v2.squaredNorm()); } assert(border.size() == angles.size()); + assert(border.size() == lengths.size()); bool circle = false; - std::vector> circles; + std::vector> circles; + std::vector> circles_idxs; for (int i=1; i( + new Circle(center, radius, plane.normal))); circle = false; } } } + // Some of the "circles" may actually be polygons. We want them detected as + // edges, but also to remember the center and save it into those edges. + // We will add all such edges manually and delete the detected circles, + // leaving it in circles_idxs so they are not picked again: + assert(circles.size() == circles_idxs.size()); + for (int i=circles.size()-1; i>=0; --i) { + assert(circles_idxs[i].first + 1 < angles.size() - 1); // Check that this is internal point of the circle, not the first, not the last. + double angle = angles[circles_idxs[i].first + 1]; + if (angle > polygon_lower_threshold) { + if (angle < polygon_upper_threshold) { + const Vec3d center = static_cast(circles[i].get())->get_center(); + for (int j=circles_idxs[i].first + 1; j<=circles_idxs[i].second; ++j) + plane.surface_features.emplace_back(std::unique_ptr( + new Edge(border[j-1], border[j], center))); + } else { + // This will be handled just like a regular edge. + circles_idxs.erase(circles_idxs.begin() + i); + } + circles.erase(circles.begin() + i); + } + } + + + + + + // We have the circles. Now go around again and pick edges. int cidx = 0; // index of next circle in the way for (int i=1; i circles[cidx].first) - i = circles[cidx++].second; + if (cidx < circles_idxs.size() && i > circles_idxs[cidx].first) + i = circles_idxs[cidx++].second; else plane.surface_features.emplace_back(std::unique_ptr( new Edge(border[i-1], border[i]))); } @@ -258,13 +297,10 @@ void MeasuringImpl::extract_features() // FIXME Check and merge first and last circle if needed. - // Now create the circle-typed surface features. - for (const auto& [start_idx, end_idx] : circles) { - std::pair center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); - plane.surface_features.emplace_back(std::unique_ptr( - new Circle(center_and_radius.first, center_and_radius.second, plane.normal))); - } - + // Now move the circles into the feature list. + assert(std::all_of(circles.begin(), circles.end(), [](const std::unique_ptr& f) { return f->get_type() == SurfaceFeatureType::Circle; })); + plane.surface_features.insert(plane.surface_features.end(), std::make_move_iterator(circles.begin()), + std::make_move_iterator(circles.end())); } // The last surface feature is the plane itself. diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 0455291bfc..1db35d9fcd 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -32,6 +32,7 @@ public: m_pin{std::unique_ptr(new Vec3d(pin))} {} SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Edge; } std::pair get_edge() const { return std::make_pair(m_start, m_end); } + const Vec3d* get_point_of_interest() const { return m_pin.get(); } private: Vec3d m_start; Vec3d m_end; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 44dd44cc55..1869983015 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -207,6 +207,14 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_feature_matrix); m_vbo_cylinder.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); m_vbo_cylinder.render(); + if (edge->get_point_of_interest()) { + Vec3d pin = *edge->get_point_of_interest(); + view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(pin)); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_sphere.render(); + } } else if (feature->get_type() == Measure::SurfaceFeatureType::Plane) { const auto* plane = static_cast(feature); From 72d7cdd5f8d73119ef04319c39a58249196bf7a7 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 18 Aug 2022 13:42:26 +0200 Subject: [PATCH 014/103] Measuring: refactoring --- src/libslic3r/Measure.cpp | 166 ++++++++++++++--------- src/libslic3r/Measure.hpp | 114 ++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 45 +++--- 3 files changed, 180 insertions(+), 145 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 056178bc4e..e59e3c04d4 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -34,24 +34,21 @@ public: struct PlaneData { std::vector facets; std::vector> borders; // FIXME: should be in fact local in update_planes() - std::vector> surface_features; + std::vector surface_features; Vec3d normal; float area; }; - const std::vector& get_features() const; - const SurfaceFeature* get_feature(size_t face_idx, const Vec3d& point) const; - const std::vector> get_planes_triangle_indices() const; + std::vector get_all_features() const; + std::optional get_feature(size_t face_idx, const Vec3d& point) const; + std::vector> get_planes_triangle_indices() const; private: void update_planes(); void extract_features(); - void save_features(); - - + std::vector m_planes; std::vector m_face_to_plane; - std::vector m_features; const indexed_triangle_set& m_its; }; @@ -65,7 +62,6 @@ MeasuringImpl::MeasuringImpl(const indexed_triangle_set& its) { update_planes(); extract_features(); - save_features(); } @@ -233,7 +229,7 @@ void MeasuringImpl::extract_features() bool circle = false; - std::vector> circles; + std::vector circles; std::vector> circles_idxs; for (int i=1; i( - new Circle(center, radius, plane.normal))); + circles.emplace_back(SurfaceFeature(SurfaceFeatureType::Circle, center, plane.normal, std::optional(), radius)); circle = false; } } @@ -266,10 +261,10 @@ void MeasuringImpl::extract_features() double angle = angles[circles_idxs[i].first + 1]; if (angle > polygon_lower_threshold) { if (angle < polygon_upper_threshold) { - const Vec3d center = static_cast(circles[i].get())->get_center(); + const Vec3d center = std::get<0>(circles[i].get_circle()); for (int j=circles_idxs[i].first + 1; j<=circles_idxs[i].second; ++j) - plane.surface_features.emplace_back(std::unique_ptr( - new Edge(border[j-1], border[j], center))); + plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, + border[j-1], border[j], std::make_optional(center), 0.)); } else { // This will be handled just like a regular edge. circles_idxs.erase(circles_idxs.begin() + i); @@ -288,8 +283,8 @@ void MeasuringImpl::extract_features() for (int i=1; i circles_idxs[cidx].first) i = circles_idxs[cidx++].second; - else plane.surface_features.emplace_back(std::unique_ptr( - new Edge(border[i-1], border[i]))); + else plane.surface_features.emplace_back(SurfaceFeature( + SurfaceFeatureType::Edge, border[i-1], border[i], std::optional(), 0.)); } // FIXME Throw away / do not create edges which are parts of circles or @@ -298,14 +293,16 @@ void MeasuringImpl::extract_features() // FIXME Check and merge first and last circle if needed. // Now move the circles into the feature list. - assert(std::all_of(circles.begin(), circles.end(), [](const std::unique_ptr& f) { return f->get_type() == SurfaceFeatureType::Circle; })); + assert(std::all_of(circles.begin(), circles.end(), [](const SurfaceFeature& f) { + return f.get_type() == SurfaceFeatureType::Circle; + })); plane.surface_features.insert(plane.surface_features.end(), std::make_move_iterator(circles.begin()), std::make_move_iterator(circles.end())); } // The last surface feature is the plane itself. - plane.surface_features.emplace_back(std::unique_ptr( - new Plane(i))); + plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Plane, + Vec3d::Zero(), Vec3d::Zero(), std::optional(), i + 0.0001)); plane.borders.clear(); plane.borders.shrink_to_fit(); @@ -314,56 +311,58 @@ void MeasuringImpl::extract_features() -void MeasuringImpl::save_features() +std::vector MeasuringImpl::get_all_features() const { - m_features.clear(); - for (PlaneData& plane : m_planes) + std::vector features; //PlaneData& plane = m_planes[0]; - { - for (const std::unique_ptr& feature : plane.surface_features) { - m_features.emplace_back(feature.get()); - } - } + for (const PlaneData& plane : m_planes) + for (const SurfaceFeature& feature : plane.surface_features) + features.emplace_back(feature); + return features; } -const SurfaceFeature* MeasuringImpl::get_feature(size_t face_idx, const Vec3d& point) const + + + +std::optional MeasuringImpl::get_feature(size_t face_idx, const Vec3d& point) const { if (face_idx >= m_face_to_plane.size()) - return nullptr; + return std::optional(); const PlaneData& plane = m_planes[m_face_to_plane[face_idx]]; const SurfaceFeature* closest_feature = nullptr; double min_dist = std::numeric_limits::max(); - for (const std::unique_ptr& feature : plane.surface_features) { - double dist = Measuring::get_distance(feature.get(), &point); - if (dist < 0.5 && dist < min_dist) { - min_dist = std::min(dist, min_dist); - closest_feature = feature.get(); + MeasurementResult res; + SurfaceFeature point_sf(point); + + for (const SurfaceFeature& feature : plane.surface_features) { + res = get_measurement(feature, point_sf); + if (res.distance_strict) { // TODO: this should become an assert after all combinations are implemented. + double dist = *res.distance_strict; + if (dist < 0.5 && dist < min_dist) { + min_dist = std::min(dist, min_dist); + closest_feature = &feature; + } } } if (closest_feature) - return closest_feature; + return std::make_optional(*closest_feature); // Nothing detected, return the plane as a whole. - assert(plane.surface_features.back().get()->get_type() == SurfaceFeatureType::Plane); - return plane.surface_features.back().get(); + assert(plane.surface_features.back().get_type() == SurfaceFeatureType::Plane); + return std::make_optional(plane.surface_features.back()); } -const std::vector& MeasuringImpl::get_features() const -{ - return m_features; -} - -const std::vector> MeasuringImpl::get_planes_triangle_indices() const +std::vector> MeasuringImpl::get_planes_triangle_indices() const { std::vector> out; for (const PlaneData& plane : m_planes) @@ -390,45 +389,81 @@ Measuring::Measuring(const indexed_triangle_set& its) Measuring::~Measuring() {} -const std::vector& Measuring::get_features() const +std::vector Measuring::get_all_features() const { - return priv->get_features(); + return priv->get_all_features(); } -const SurfaceFeature* Measuring::get_feature(size_t face_idx, const Vec3d& point) const +std::optional Measuring::get_feature(size_t face_idx, const Vec3d& point) const { return priv->get_feature(face_idx, point); } -const std::vector> Measuring::get_planes_triangle_indices() const +std::vector> Measuring::get_planes_triangle_indices() const { return priv->get_planes_triangle_indices(); } -double Measuring::get_distance(const SurfaceFeature* feature, const Vec3d* pt) + + + + + + + + + + + +MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b) { - if (feature->get_type() == SurfaceFeatureType::Edge) { - const Edge* edge = static_cast(feature); - const auto& [s,e] = edge->get_edge(); - Eigen::ParametrizedLine line(s, (e-s).normalized()); - return line.distance(*pt); - } - else if (feature->get_type() == SurfaceFeatureType::Circle) { - const Circle* circle = static_cast(feature); - // Find a plane containing normal, center and the point. - const Vec3d& c = circle->get_center(); - const Vec3d& n = circle->get_normal(); - Eigen::Hyperplane circle_plane(n, c); - Vec3d proj = circle_plane.projection(*pt); - return std::sqrt( std::pow((proj - c).norm() - circle->get_radius(), 2.) + (*pt - proj).squaredNorm()); + assert(a.get_type() != SurfaceFeatureType::Undef && b.get_type() != SurfaceFeatureType::Undef); + + const bool swap = int(a.get_type()) > int(b.get_type()); + const SurfaceFeature& f1 = swap ? b : a; + const SurfaceFeature& f2 = swap ? a : b; + + MeasurementResult result; + if (f1.get_type() == SurfaceFeatureType::Point) { + if (f2.get_type() == SurfaceFeatureType::Point) { + Vec3d diff = (f2.get_point() - f1.get_point()); + result.distance_strict = diff.norm(); + result.distance_xyz = diff; + } else if (f2.get_type() == SurfaceFeatureType::Edge) { + const auto& [s,e] = f2.get_edge(); + Eigen::ParametrizedLine line(s, (e-s).normalized()); + result.distance_strict = std::make_optional(line.distance(f1.get_point())); // TODO: this is really infinite dist + } else if (f2.get_type() == SurfaceFeatureType::Circle) { + // Find a plane containing normal, center and the point. + const auto& [c, radius, n] = f2.get_circle(); + Eigen::Hyperplane circle_plane(n, c); + Vec3d proj = circle_plane.projection(f1.get_point()); + result.distance_strict = std::make_optional(std::sqrt( + std::pow((proj - c).norm() - radius, 2.) + (f1.get_point() - proj).squaredNorm())); + } else if (f2.get_type() == SurfaceFeatureType::Plane) { + } + } else if (f1.get_type() == SurfaceFeatureType::Edge) { + if (f2.get_type() == SurfaceFeatureType::Edge) { + } else if (f2.get_type() == SurfaceFeatureType::Circle) { + } else if (f2.get_type() == SurfaceFeatureType::Plane) { + } + } else if (f1.get_type() == SurfaceFeatureType::Circle) { + if (f2.get_type() == SurfaceFeatureType::Circle) { + } else if (f2.get_type() == SurfaceFeatureType::Plane) { + } + } else if (f1.get_type() == SurfaceFeatureType::Plane) { + assert(f2.get_type() == SurfaceFeatureType::Plane); + } - return std::numeric_limits::max(); + + + return result; } @@ -436,5 +471,8 @@ double Measuring::get_distance(const SurfaceFeature* feature, const Vec3d* pt) + + + } // namespace Measure } // namespace Slic3r diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 1db35d9fcd..87bed1cf17 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -1,6 +1,7 @@ #ifndef Slic3r_Measure_hpp_ #define Slic3r_Measure_hpp_ +#include #include #include "Point.hpp" @@ -15,55 +16,50 @@ namespace Measure { enum class SurfaceFeatureType { - Edge = 1 << 0, - Circle = 1 << 1, - Plane = 1 << 2 - }; + Undef, + Point, + Edge, + Circle, + Plane +}; class SurfaceFeature { -public: - virtual SurfaceFeatureType get_type() const = 0; -}; - -class Edge : public SurfaceFeature { public: - Edge(const Vec3d& start, const Vec3d& end) : m_start{start}, m_end{end} {} - Edge(const Vec3d& start, const Vec3d& end, const Vec3d& pin) : m_start{start}, m_end{end}, - m_pin{std::unique_ptr(new Vec3d(pin))} {} - SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Edge; } - std::pair get_edge() const { return std::make_pair(m_start, m_end); } - const Vec3d* get_point_of_interest() const { return m_pin.get(); } -private: - Vec3d m_start; - Vec3d m_end; - std::unique_ptr m_pin; -}; + SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional pt3, double value) + : m_type{type}, m_pt1{pt1}, m_pt2{pt2}, m_pt3{pt3}, m_value{value} {} -class Circle : public SurfaceFeature { -public: - Circle(const Vec3d& center, double radius, const Vec3d& normal) - : m_center{center}, m_radius{radius}, m_normal{normal} {} - SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Circle; } - Vec3d get_center() const { return m_center; } - double get_radius() const { return m_radius; } - Vec3d get_normal() const { return m_normal; } -private: - Vec3d m_center; - double m_radius; - Vec3d m_normal; -}; + explicit SurfaceFeature(const Vec3d& pt) + : m_type{SurfaceFeatureType::Point}, m_pt1{pt} {} -class Plane : public SurfaceFeature { -public: - Plane(int idx) : m_idx(idx) {} - SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Plane; } - int get_plane_idx() const { return m_idx; } // index into vector provided by Measuring::get_plane_triangle_indices + + // Get type of this feature. + SurfaceFeatureType get_type() const { return m_type; } + + // For points, return the point. + Vec3d get_point() const { assert(m_type == SurfaceFeatureType::Point); return m_pt1; } + + // For edges, return start and end. + std::pair get_edge() const { assert(m_type == SurfaceFeatureType::Edge); return std::make_pair(m_pt1, m_pt2); } + + // For circles, return center, radius and normal. + std::tuple get_circle() const { assert(m_type == SurfaceFeatureType::Circle); return std::make_tuple(m_pt1, m_value, m_pt2); } + + // For planes, return index into vector provided by Measuring::get_plane_triangle_indices. + int get_plane_idx() const { return int(m_value); } + + // For anything, return an extra point that should also be considered a part of this. + std::optional get_extra_point() const { assert(m_type != SurfaceFeatureType::Undef); return m_pt3; } private: - int m_idx; + SurfaceFeatureType m_type = SurfaceFeatureType::Undef; + Vec3d m_pt1; + Vec3d m_pt2; + std::optional m_pt3; + double m_value; }; + class MeasuringImpl; @@ -75,34 +71,36 @@ public: ~Measuring(); // Return a reference to a list of all features identified on the its. - [[deprecated]]const std::vector& get_features() const; + // Use only for debugging. Expensive, do not call often. + [[deprecated]] std::vector get_all_features() const; // Given a face_idx where the mouse cursor points, return a feature that - // should be highlighted or nullptr. - const SurfaceFeature* get_feature(size_t face_idx, const Vec3d& point) const; + // should be highlighted (if any). + std::optional get_feature(size_t face_idx, const Vec3d& point) const; // Returns a list of triangle indices for each identified plane. Each - // Plane object contains an index into this vector. - const std::vector> get_planes_triangle_indices() const; - - - - // Returns distance between two SurfaceFeatures. - static double get_distance(const SurfaceFeature* a, const SurfaceFeature* b); - - // Returns distance between a SurfaceFeature and a point. - static double get_distance(const SurfaceFeature* a, const Vec3d* pt); - - // Returns true if measuring angles between features makes sense. - // If so, result contains the angle in radians. - static bool get_angle(const SurfaceFeature* a, const SurfaceFeature* b, double& result); - - + // Plane object contains an index into this vector. Expensive, do not + // call too often. + std::vector> get_planes_triangle_indices() const; + private: std::unique_ptr priv; }; + + +struct MeasurementResult { + std::optional angle; + std::optional distance_infinite; + std::optional distance_strict; + std::optional distance_xyz; +}; + +// Returns distance/angle between two SurfaceFeatures. +static MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b); + + } // namespace Measure } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 3f12597987..6362e0053a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -158,24 +158,25 @@ void GLGizmoMeasure::on_render() - std::vector features = {m_measuring->get_feature(facet_idx, pos.cast())}; + std::vector features; if (m_show_all) { - features = m_measuring->get_features(); + features = m_measuring->get_all_features(); // EXPENSIVE - debugging only. features.erase(std::remove_if(features.begin(), features.end(), - [](const Measure::SurfaceFeature* f) { - return f->get_type() == Measure::SurfaceFeatureType::Plane; + [](const Measure::SurfaceFeature& f) { + return f.get_type() == Measure::SurfaceFeatureType::Plane; }), features.end()); + } else { + std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); + if (feat) + features.emplace_back(*feat); } - - - for (const Measure::SurfaceFeature* feature : features) { - if (! feature) - continue; - if (feature->get_type() == Measure::SurfaceFeatureType::Circle) { - const auto* circle = static_cast(feature); - const Vec3d& c = circle->get_center(); - const Vec3d& n = circle->get_normal(); + + + for (const Measure::SurfaceFeature& feature : features) { + + if (feature.get_type() == Measure::SurfaceFeatureType::Circle) { + const auto& [c, radius, n] = feature.get_circle(); Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(c)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); @@ -186,7 +187,7 @@ void GLGizmoMeasure::on_render() Vec3d rad = n.cross(Vec3d::UnitX()); if (rad.squaredNorm() < 0.1) rad = n.cross(Vec3d::UnitY()); - rad *= circle->get_radius() * rad.norm(); + rad *= radius * rad.norm(); const int N = 20; for (int i=0; iget_type() == Measure::SurfaceFeatureType::Edge) { - const auto* edge = static_cast(feature); - auto& [start, end] = edge->get_edge(); + else if (feature.get_type() == Measure::SurfaceFeatureType::Edge) { + const auto& [start, end] = feature.get_edge(); Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(start)); auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); view_feature_matrix *= q; @@ -207,8 +207,8 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_feature_matrix); m_vbo_cylinder.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); m_vbo_cylinder.render(); - if (edge->get_point_of_interest()) { - Vec3d pin = *edge->get_point_of_interest(); + if (feature.get_extra_point()) { + Vec3d pin = *feature.get_extra_point(); view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(pin)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); @@ -216,10 +216,9 @@ void GLGizmoMeasure::on_render() m_vbo_sphere.render(); } } - else if (feature->get_type() == Measure::SurfaceFeatureType::Plane) { - const auto* plane = static_cast(feature); - assert(plane->get_plane_idx() < m_plane_models.size()); - m_plane_models[plane->get_plane_idx()]->render(); + else if (feature.get_type() == Measure::SurfaceFeatureType::Plane) { + assert(feature.get_plane_idx() < m_plane_models.size()); + m_plane_models[feature.get_plane_idx()]->render(); } } shader->set_uniform("view_model_matrix", view_model_matrix); From aed6ddf8f392a49179f6bfbd91d813b26ca4c636 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 18 Aug 2022 18:50:49 +0200 Subject: [PATCH 015/103] Measuring: implemented edge endpoint detection --- src/libslic3r/Measure.cpp | 91 ++++++++++++++++++++---- src/libslic3r/Measure.hpp | 9 ++- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 14 +++- 3 files changed, 94 insertions(+), 20 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index e59e3c04d4..8c7254f85b 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -9,6 +9,10 @@ namespace Slic3r { namespace Measure { +constexpr double feature_hover_limit = 0.5; // how close to a feature the mouse must be to highlight it +constexpr double edge_endpoint_limit = 0.5; // how close to an edge endpoint the mouse ... + + static std::pair get_center_and_radius(const std::vector& border, int start_idx, int end_idx, const Transform3d& trafo) { @@ -302,7 +306,7 @@ void MeasuringImpl::extract_features() // The last surface feature is the plane itself. plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Plane, - Vec3d::Zero(), Vec3d::Zero(), std::optional(), i + 0.0001)); + plane.normal, plane.borders.front().front(), std::optional(), i + 0.0001)); plane.borders.clear(); plane.borders.shrink_to_fit(); @@ -333,25 +337,39 @@ std::optional MeasuringImpl::get_feature(size_t face_idx, const const PlaneData& plane = m_planes[m_face_to_plane[face_idx]]; - const SurfaceFeature* closest_feature = nullptr; + size_t closest_feature_idx = size_t(-1); double min_dist = std::numeric_limits::max(); MeasurementResult res; SurfaceFeature point_sf(point); - for (const SurfaceFeature& feature : plane.surface_features) { - res = get_measurement(feature, point_sf); + for (size_t i=0; i line(s, (e-s).normalized()); - result.distance_strict = std::make_optional(line.distance(f1.get_point())); // TODO: this is really infinite dist + double dist_inf = line.distance(f1.get_point()); + Vec3d proj = line.projection(f1.get_point()); + double len_sq = (e-s).squaredNorm(); + double dist_start_sq = (proj-s).squaredNorm(); + double dist_end_sq = (proj-e).squaredNorm(); + if (dist_start_sq < len_sq && dist_end_sq < len_sq) { + // projection falls on the line - the strict distance is the same as infinite + result.distance_strict = std::make_optional(dist_inf); + } else { // the result is the closer of the endpoints + result.distance_strict = std::make_optional(std::sqrt(std::min(dist_start_sq, dist_end_sq) + dist_inf)); + } + result.distance_infinite = std::make_optional(dist_inf); + /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { // Find a plane containing normal, center and the point. const auto& [c, radius, n] = f2.get_circle(); Eigen::Hyperplane circle_plane(n, c); Vec3d proj = circle_plane.projection(f1.get_point()); - result.distance_strict = std::make_optional(std::sqrt( - std::pow((proj - c).norm() - radius, 2.) + (f1.get_point() - proj).squaredNorm())); + double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) + + (f1.get_point() - proj).squaredNorm()); + + result.distance_strict = std::make_optional(dist); + /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { + const auto& [idx, normal, pt] = f2.get_plane(); + Eigen::Hyperplane plane(normal, pt); + result.distance_infinite = plane.absDistance(f1.get_point()); + // TODO: result.distance_strict = } + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Edge) { if (f2.get_type() == SurfaceFeatureType::Edge) { + /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { + /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { } + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Circle) { if (f2.get_type() == SurfaceFeatureType::Circle) { + /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { } + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Plane) { assert(f2.get_type() == SurfaceFeatureType::Plane); - + + const auto& [idx1, normal1, pt1] = f1.get_plane(); + const auto& [idx2, normal2, pt2] = f2.get_plane(); + double angle = 0.; + + if (! normal1.isApprox(normal2)) { + // The planes are parallel, calculate distance. + Eigen::Hyperplane plane(normal1, pt1); + result.distance_infinite = plane.absDistance(pt2); + } else { + // Planes are not parallel, calculate angle. + angle = std::acos(std::abs(normal1.dot(normal2))); + } + result.angle = angle; } diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 87bed1cf17..fe0b1687a1 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -31,7 +31,6 @@ public: explicit SurfaceFeature(const Vec3d& pt) : m_type{SurfaceFeatureType::Point}, m_pt1{pt} {} - // Get type of this feature. SurfaceFeatureType get_type() const { return m_type; } @@ -44,8 +43,8 @@ public: // For circles, return center, radius and normal. std::tuple get_circle() const { assert(m_type == SurfaceFeatureType::Circle); return std::make_tuple(m_pt1, m_value, m_pt2); } - // For planes, return index into vector provided by Measuring::get_plane_triangle_indices. - int get_plane_idx() const { return int(m_value); } + // For planes, return index into vector provided by Measuring::get_plane_triangle_indices, normal and point. + std::tuple get_plane() const { return std::make_tuple(int(m_value), m_pt1, m_pt2); } // For anything, return an extra point that should also be considered a part of this. std::optional get_extra_point() const { assert(m_type != SurfaceFeatureType::Undef); return m_pt3; } @@ -84,7 +83,7 @@ public: std::vector> get_planes_triangle_indices() const; private: - std::unique_ptr priv; + std::unique_ptr priv; }; @@ -98,7 +97,7 @@ struct MeasurementResult { }; // Returns distance/angle between two SurfaceFeatures. -static MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b); +MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b); } // namespace Measure diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 6362e0053a..26ecfb5813 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -175,7 +175,14 @@ void GLGizmoMeasure::on_render() for (const Measure::SurfaceFeature& feature : features) { - if (feature.get_type() == Measure::SurfaceFeatureType::Circle) { + if (feature.get_type() == Measure::SurfaceFeatureType::Point) { + Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.get_point())); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_sphere.render(); + } + else if (feature.get_type() == Measure::SurfaceFeatureType::Circle) { const auto& [c, radius, n] = feature.get_circle(); Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(c)); view_feature_matrix.scale(0.5); @@ -217,8 +224,9 @@ void GLGizmoMeasure::on_render() } } else if (feature.get_type() == Measure::SurfaceFeatureType::Plane) { - assert(feature.get_plane_idx() < m_plane_models.size()); - m_plane_models[feature.get_plane_idx()]->render(); + const auto& [idx, normal, pt] = feature.get_plane(); + assert(idx < m_plane_models.size()); + m_plane_models[idx]->render(); } } shader->set_uniform("view_model_matrix", view_model_matrix); From decbf315930f627b9ccedfad390e694fa4eb1f16 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 19 Aug 2022 09:05:44 +0200 Subject: [PATCH 016/103] Partial revert of 1e494e30 --- src/libslic3r/TriangleMesh.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 8dc22f1490..da6fcf28f6 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -877,20 +877,13 @@ Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Transfor indexed_triangle_set its_make_cube(double xd, double yd, double zd) { auto x = float(xd), y = float(yd), z = float(zd); - /*return { + return { { {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7}, {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2}, {2, 6, 5}, {2, 5, 3}, {4, 0, 3}, {4, 3, 5} }, { {x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0}, {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z} } - };*/ - return { - { {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7}, - {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2}, - {2, 5, 6}, {2, 5, 3}, {4, 0, 3}, /*{4, 3, 5}*/ }, - { {x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0}, - {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z} } - }; + }; } indexed_triangle_set its_make_prism(float width, float length, float height) From 4c8b78de726501f08a7c9762181cb360c8304c37 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 19 Aug 2022 09:19:10 +0200 Subject: [PATCH 017/103] Use unified color for hovering in GLGizmoMeasure --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 26ecfb5813..8fc2ff8ea0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -16,8 +16,7 @@ namespace Slic3r { namespace GUI { -static const Slic3r::ColorRGBA DEFAULT_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.9f }; -static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.2f, 0.2f, 1.f }; +static const Slic3r::ColorRGBA HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) @@ -126,7 +125,10 @@ void GLGizmoMeasure::on_render() glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_BLEND)); - glsafe(::glLineWidth(2.f)); +#if ENABLE_GL_CORE_PROFILE + if (!OpenGLManager::get_gl_info().is_core_profile()) +#endif // ENABLE_GL_CORE_PROFILE + glsafe(::glLineWidth(2.f)); if (selection.is_single_full_instance()) { const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); @@ -179,7 +181,7 @@ void GLGizmoMeasure::on_render() Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.get_point())); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_sphere.set_color(HOVER_COLOR); m_vbo_sphere.render(); } else if (feature.get_type() == Measure::SurfaceFeatureType::Circle) { @@ -187,7 +189,7 @@ void GLGizmoMeasure::on_render() Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(c)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_sphere.set_color(HOVER_COLOR); m_vbo_sphere.render(); // Now draw the circle itself - let's take a funny shortcut: @@ -212,20 +214,21 @@ void GLGizmoMeasure::on_render() view_feature_matrix *= q; view_feature_matrix.scale(Vec3d(0.075, 0.075, (end - start).norm())); shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_cylinder.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_cylinder.set_color(HOVER_COLOR); m_vbo_cylinder.render(); if (feature.get_extra_point()) { Vec3d pin = *feature.get_extra_point(); view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(pin)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_sphere.set_color(HOVER_COLOR); m_vbo_sphere.render(); } } else if (feature.get_type() == Measure::SurfaceFeatureType::Plane) { const auto& [idx, normal, pt] = feature.get_plane(); assert(idx < m_plane_models.size()); + m_plane_models[idx]->set_color(HOVER_COLOR); m_plane_models[idx]->render(); } } From a14c27b42ed11be75a1cf8916b7aeaa31e7e1f1e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 19 Aug 2022 10:36:28 +0200 Subject: [PATCH 018/103] New icon for Measure Gizmo --- resources/icons/measure.svg | 106 +++++++++++++++++++++++++++++++----- 1 file changed, 93 insertions(+), 13 deletions(-) diff --git a/resources/icons/measure.svg b/resources/icons/measure.svg index 275c522251..3ea137a1ed 100644 --- a/resources/icons/measure.svg +++ b/resources/icons/measure.svg @@ -1,13 +1,93 @@ - - - Layer 1 - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + From 4d9c564b9a7ca1a577ad645b1a9f095922e607ee Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 19 Aug 2022 10:48:32 +0200 Subject: [PATCH 019/103] Set Measure Gizmo to be activable for single volume selections only --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 11 ++--------- src/slic3r/GUI/Selection.hpp | 1 + 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 8fc2ff8ea0..712631be63 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -100,17 +100,12 @@ std::string GLGizmoMeasure::on_get_name() const return _u8L("Measure"); } - - bool GLGizmoMeasure::on_is_activable() const { - // This is assumed in GLCanvas3D::do_rotate, do not change this - // without updating that function too. - return m_parent.get_selection().is_single_full_instance(); + const Selection& selection = m_parent.get_selection(); + return selection.is_single_volume() || selection.is_single_volume_instance(); } - - void GLGizmoMeasure::on_render() { const Selection& selection = m_parent.get_selection(); @@ -139,9 +134,7 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - update_if_needed(); - m_imgui->begin(std::string("DEBUG")); diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index cebea29e05..e118f6bca5 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -328,6 +328,7 @@ public: #if ENABLE_WORLD_COORDINATE bool is_single_volume_or_modifier() const { return is_single_volume() || is_single_modifier(); } #endif // ENABLE_WORLD_COORDINATE + bool is_single_volume_instance() const { return is_single_full_instance() && m_list.size() == 1; } bool contains_volume(unsigned int volume_idx) const { return m_list.find(volume_idx) != m_list.end(); } // returns true if the selection contains all the given indices From 474624f89e9bc678950bc3ea400885a48526bdef Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 19 Aug 2022 11:02:13 +0200 Subject: [PATCH 020/103] Removed method set_flattening_data() from GLGizmoMeasure --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 9 ++------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 712631be63..9b3d30a0be 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -69,7 +69,8 @@ void GLGizmoMeasure::data_changed() selection.is_from_single_object() ) { model_object = selection.get_model()->objects[selection.get_object_idx()]; } - set_flattening_data(model_object); + if (model_object != m_old_model_object) + update_if_needed(); } @@ -247,12 +248,6 @@ void GLGizmoMeasure::on_render() #error NOT IMPLEMENTED #endif -void GLGizmoMeasure::set_flattening_data(const ModelObject* model_object) -{ - if (model_object != m_old_model_object) - update_if_needed(); -} - void GLGizmoMeasure::update_if_needed() { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 9094007b5d..e1460c2146 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -50,7 +50,6 @@ private: std::vector> m_plane_models; void update_if_needed(); - void set_flattening_data(const ModelObject* model_object); public: GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); @@ -63,6 +62,7 @@ public: bool on_mouse(const wxMouseEvent &mouse_event) override; void data_changed() override; + protected: bool on_init() override; std::string on_get_name() const override; From 9d81568bf956f6152d0c984c8d1f88d850d7c1b0 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 22 Aug 2022 08:52:02 +0200 Subject: [PATCH 021/103] Measuring: allow to select single parts of a multipart object while Gizmo Measure is active --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 136 +++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 7 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 26 +++-- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 6 +- 4 files changed, 105 insertions(+), 70 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 9b3d30a0be..92fb8691ef 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -64,12 +64,14 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) void GLGizmoMeasure::data_changed() { const Selection & selection = m_parent.get_selection(); - const ModelObject *model_object = nullptr; + const ModelObject* model_object = nullptr; + const ModelVolume* model_volume = nullptr; if (selection.is_single_full_instance() || selection.is_from_single_object() ) { model_object = selection.get_model()->objects[selection.get_object_idx()]; - } - if (model_object != m_old_model_object) + model_volume = model_object->volumes[selection.get_first_volume()->volume_idx()]; + } + if (model_object != m_old_model_object || model_volume != m_old_model_volume) update_if_needed(); } @@ -109,6 +111,10 @@ bool GLGizmoMeasure::on_is_activable() const void GLGizmoMeasure::on_render() { + // do not render if the user is panning/rotating the 3d scene + if (m_parent.is_mouse_dragging()) + return; + const Selection& selection = m_parent.get_selection(); GLShaderProgram* shader = wxGetApp().get_shader("flat"); @@ -126,33 +132,34 @@ void GLGizmoMeasure::on_render() #endif // ENABLE_GL_CORE_PROFILE glsafe(::glLineWidth(2.f)); - if (selection.is_single_full_instance()) { - const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); + if (selection.is_single_volume() || selection.is_single_volume_instance()) { + const Transform3d& model_matrix = selection.get_first_volume()->world_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); const Transform3d view_model_matrix = camera.get_view_matrix() * - Geometry::assemble_transform(selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z() * Vec3d::UnitZ()) * m; + Geometry::assemble_transform(selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * model_matrix; shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); update_if_needed(); - m_imgui->begin(std::string("DEBUG")); - - m_imgui->checkbox(wxString("Show all features"), m_show_all); - m_imgui->checkbox(wxString("Show all planes"), m_show_planes); - Vec3f pos; Vec3f normal; size_t facet_idx; - m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), m, camera, pos, normal, nullptr, &facet_idx); + m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), model_matrix, camera, pos, normal, nullptr, &facet_idx); + +#define ENABLE_DEBUG_DIALOG 0 +#if ENABLE_DEBUG_DIALOG + m_imgui->begin(std::string("DEBUG")); + m_imgui->checkbox(wxString("Show all features"), m_show_all); + m_imgui->checkbox(wxString("Show all planes"), m_show_planes); ImGui::Separator(); m_imgui->text(std::string("face_idx: ") + std::to_string(facet_idx)); m_imgui->text(std::string("pos_x: ") + std::to_string(pos.x())); m_imgui->text(std::string("pos_y: ") + std::to_string(pos.y())); m_imgui->text(std::string("pos_z: ") + std::to_string(pos.z())); - - + m_imgui->end(); +#endif // ENABLE_DEBUG_DIALOG std::vector features; if (m_show_all) { @@ -230,8 +237,6 @@ void GLGizmoMeasure::on_render() if (m_show_planes) for (const auto& glmodel : m_plane_models) glmodel->render(); - - m_imgui->end(); } glsafe(::glEnable(GL_CULL_FACE)); @@ -251,57 +256,70 @@ void GLGizmoMeasure::on_render() void GLGizmoMeasure::update_if_needed() { + auto do_update = [this](const ModelObject* object, const ModelVolume* volume) { + const indexed_triangle_set& its = (volume != nullptr) ? volume->mesh().its : object->volumes.front()->mesh().its; + m_measuring.reset(new Measure::Measuring(its)); + m_plane_models.clear(); + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + for (const std::vector& triangle_indices : planes_triangles) { + m_plane_models.emplace_back(std::unique_ptr(new GLModel())); + GUI::GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA(0.9f, 0.9f, 0.9f, 0.5f); + int i = 0; + for (int idx : triangle_indices) { + init_data.add_vertex(its.vertices[its.indices[idx][0]]); + init_data.add_vertex(its.vertices[its.indices[idx][1]]); + init_data.add_vertex(its.vertices[its.indices[idx][2]]); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; + } + m_plane_models.back()->init_from(std::move(init_data)); + } + + // Let's save what we calculated it from: + m_volumes_matrices.clear(); + m_volumes_types.clear(); + m_first_instance_scale = Vec3d::Ones(); + m_first_instance_mirror = Vec3d::Ones(); + if (object != nullptr) { + for (const ModelVolume* vol : object->volumes) { + m_volumes_matrices.push_back(vol->get_matrix()); + m_volumes_types.push_back(vol->type()); + } + m_first_instance_scale = object->instances.front()->get_scaling_factor(); + m_first_instance_mirror = object->instances.front()->get_mirror(); + } + m_old_model_object = object; + m_old_model_volume = volume; + }; + const ModelObject* mo = m_c->selection_info()->model_object(); - if (m_state != On || ! mo || mo->instances.empty()) + const ModelVolume* mv = m_c->selection_info()->model_volume(); + if (m_state != On || (mo == nullptr && mv == nullptr)) return; - if (! m_measuring || mo != m_old_model_object - || mo->volumes.size() != m_volumes_matrices.size()) - goto UPDATE; + if (mo == nullptr) + mo = mv->get_object(); + + if (mo->instances.empty()) + return; + + if (!m_measuring || mo != m_old_model_object || mv != m_old_model_volume || mo->volumes.size() != m_volumes_matrices.size()) + do_update(mo, mv); // We want to recalculate when the scale changes - some planes could (dis)appear. - if (! mo->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale) - || ! mo->instances.front()->get_mirror().isApprox(m_first_instance_mirror)) - goto UPDATE; + if (!mo->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale) || + !mo->instances.front()->get_mirror().isApprox(m_first_instance_mirror)) + do_update(mo, mv); - for (unsigned int i=0; i < mo->volumes.size(); ++i) - if (! mo->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i]) - || mo->volumes[i]->type() != m_volumes_types[i]) - goto UPDATE; - - return; - -UPDATE: - const indexed_triangle_set& its = mo->volumes.front()->mesh().its; - m_measuring.reset(new Measure::Measuring(its)); - m_plane_models.clear(); - const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); - for (const std::vector& triangle_indices : planes_triangles) { - m_plane_models.emplace_back(std::unique_ptr(new GLModel())); - GUI::GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3 }; - init_data.color = ColorRGBA(0.9f, 0.9f, 0.9f, 0.5f); - int i = 0; - for (int idx : triangle_indices) { - init_data.add_vertex(its.vertices[its.indices[idx][0]]); - init_data.add_vertex(its.vertices[its.indices[idx][1]]); - init_data.add_vertex(its.vertices[its.indices[idx][2]]); - init_data.add_triangle(i, i+1, i+2); - i+=3; + for (unsigned int i = 0; i < mo->volumes.size(); ++i) { + if (!mo->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i]) || + mo->volumes[i]->type() != m_volumes_types[i]) { + do_update(mo, mv); + break; } - m_plane_models.back()->init_from(std::move(init_data)); } - - // Let's save what we calculated it from: - m_volumes_matrices.clear(); - m_volumes_types.clear(); - for (const ModelVolume* vol : mo->volumes) { - m_volumes_matrices.push_back(vol->get_matrix()); - m_volumes_types.push_back(vol->type()); - } - m_first_instance_scale = mo->instances.front()->get_scaling_factor(); - m_first_instance_mirror = mo->instances.front()->get_mirror(); - m_old_model_object = mo; } } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index e1460c2146..cfdc760608 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -14,6 +14,8 @@ namespace Slic3r { +class ModelVolume; + enum class ModelVolumeType : int; namespace Measure { class Measuring; } @@ -35,12 +37,13 @@ private: // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; std::vector m_volumes_types; - Vec3d m_first_instance_scale; - Vec3d m_first_instance_mirror; + Vec3d m_first_instance_scale{ Vec3d::Ones() }; + Vec3d m_first_instance_mirror{ Vec3d::Ones() }; bool m_mouse_left_down = false; // for detection left_up of this gizmo bool m_planes_valid = false; const ModelObject* m_old_model_object = nullptr; + const ModelVolume* m_old_model_volume = nullptr; std::vector instances_matrices; int m_mouse_pos_x; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index ce3ba1d4ab..1eef6aced5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -119,21 +119,25 @@ void SelectionInfo::on_update() const Selection& selection = get_pool()->get_canvas()->get_selection(); if (selection.is_single_full_instance()) { m_model_object = selection.get_model()->objects[selection.get_object_idx()]; + m_model_volume = nullptr; m_z_shift = selection.get_first_volume()->get_sla_shift_z(); } - else + else { m_model_object = nullptr; + if (selection.is_single_volume()) + m_model_volume = selection.get_model()->objects[selection.get_object_idx()]->volumes[selection.get_first_volume()->volume_idx()]; + } } void SelectionInfo::on_release() { m_model_object = nullptr; + m_model_volume = nullptr; } int SelectionInfo::get_active_instance() const { - const Selection& selection = get_pool()->get_canvas()->get_selection(); - return selection.get_instance_idx(); + return get_pool()->get_canvas()->get_selection().get_instance_idx(); } @@ -329,12 +333,18 @@ void Raycaster::on_update() { wxBusyCursor wait; const ModelObject* mo = get_pool()->selection_info()->model_object(); + const ModelVolume* mv = get_pool()->selection_info()->model_volume(); - if (! mo) + if (mo == nullptr && mv == nullptr) return; + std::vector mvs; + if (mv != nullptr) + mvs.push_back(const_cast(mv)); + else + mvs = mo->volumes; + std::vector meshes; - const std::vector& mvs = mo->volumes; if (mvs.size() == 1) { assert(mvs.front()->is_model_part()); const HollowedMesh* hollowed_mesh_tracker = get_pool()->hollowed_mesh(); @@ -342,9 +352,9 @@ void Raycaster::on_update() meshes.push_back(hollowed_mesh_tracker->get_hollowed_mesh()); } if (meshes.empty()) { - for (const ModelVolume* mv : mvs) { - if (mv->is_model_part()) - meshes.push_back(&mv->mesh()); + for (const ModelVolume* v : mvs) { + if (v->is_model_part()) + meshes.push_back(&v->mesh()); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 7dd2c110ea..c8b29f761e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -10,7 +10,7 @@ namespace Slic3r { class ModelObject; - +class ModelVolume; namespace GUI { @@ -153,7 +153,10 @@ public: explicit SelectionInfo(CommonGizmosDataPool* cgdp) : CommonGizmosDataBase(cgdp) {} + // Returns a non-null pointer if the selection is a single full instance ModelObject* model_object() const { return m_model_object; } + // Returns a non-null pointer if the selection is a single volume + ModelVolume* model_volume() const { return m_model_volume; } int get_active_instance() const; float get_sla_shift() const { return m_z_shift; } @@ -163,6 +166,7 @@ protected: private: ModelObject* m_model_object = nullptr; + ModelVolume* m_model_volume = nullptr; // int m_active_inst = -1; float m_z_shift = 0.f; }; From a7d1c9b5e99132e51d84fcd5155ede7da1ecadd0 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 23 Aug 2022 13:17:09 +0200 Subject: [PATCH 022/103] Measuring: reworked rendering of hovered features --- src/slic3r/GUI/GLModel.cpp | 104 +++++++++++++++ src/slic3r/GUI/GLModel.hpp | 7 ++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 154 ++++++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 3 + 4 files changed, 211 insertions(+), 57 deletions(-) diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 5ae7f9fbf5..e05ae7619b 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -2274,6 +2274,110 @@ GLModel::Geometry smooth_sphere(unsigned int resolution, float radius) return data; } + +GLModel::Geometry smooth_cylinder(Axis axis, unsigned int resolution, float radius, float height) +{ + resolution = std::max(4, resolution); + + const unsigned int sectorCount = resolution; + const float sectorStep = 2.0f * float(M_PI) / float(sectorCount); + + GLModel::Geometry data; + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + data.reserve_vertices(sectorCount * 4 + 2); + data.reserve_indices(sectorCount * 4 * 3); + + auto generate_vertices_on_circle = [sectorCount, sectorStep](Axis axis, float radius) { + std::vector ret; + ret.reserve(sectorCount); + for (unsigned int i = 0; i < sectorCount; ++i) { + // from 0 to 2pi + const float sectorAngle = (i != sectorCount) ? sectorStep * i : 0.0f; + const float x1 = radius * std::cos(sectorAngle); + const float x2 = radius * std::sin(sectorAngle); + + Vec3f v; + switch (axis) + { + case X: { v = Vec3f(0.0f, x1, x2); break; } + case Y: { v = Vec3f(-x1, 0.0f, x2); break; } + case Z: { v = Vec3f(x1, x2, 0.0f); break; } + default: { assert(false); break; } + } + + ret.emplace_back(v); + } + return ret; + }; + + const std::vector base_vertices = generate_vertices_on_circle(axis, radius); + + Vec3f h; + switch (axis) + { + case X: { h = height * Vec3f::UnitX(); break; } + case Y: { h = height * Vec3f::UnitY(); break; } + case Z: { h = height * Vec3f::UnitZ(); break; } + default: { assert(false); break; } + } + + // stem vertices + for (unsigned int i = 0; i < sectorCount; ++i) { + const Vec3f& v = base_vertices[i]; + const Vec3f n = v.normalized(); + data.add_vertex(v, n); + data.add_vertex(v + h, n); + } + + // stem triangles + for (unsigned int i = 0; i < sectorCount; ++i) { + unsigned int v1 = i * 2; + unsigned int v2 = (i < sectorCount - 1) ? v1 + 2 : 0; + unsigned int v3 = v2 + 1; + unsigned int v4 = v1 + 1; + data.add_triangle(v1, v2, v3); + data.add_triangle(v1, v3, v4); + } + + // bottom cap vertices + Vec3f cap_center = Vec3f::Zero(); + unsigned int cap_center_id = data.vertices_count(); + Vec3f normal; + switch (axis) + { + case X: { normal = -Vec3f::UnitX(); break; } + case Y: { normal = -Vec3f::UnitY(); break; } + case Z: { normal = -Vec3f::UnitZ(); break; } + default: { assert(false); break; } + } + + data.add_vertex(cap_center, normal); + for (unsigned int i = 0; i < sectorCount; ++i) { + data.add_vertex(base_vertices[i], normal); + } + + // bottom cap triangles + for (unsigned int i = 0; i < sectorCount; ++i) { + data.add_triangle(cap_center_id, (i < sectorCount - 1) ? cap_center_id + i + 2 : cap_center_id + 1, cap_center_id + i + 1); + } + + // top cap vertices + cap_center += h; + cap_center_id = data.vertices_count(); + normal = -normal; + + data.add_vertex(cap_center, normal); + for (unsigned int i = 0; i < sectorCount; ++i) { + data.add_vertex(base_vertices[i] + h, normal); + } + + // top cap triangles + for (unsigned int i = 0; i < sectorCount; ++i) { + data.add_triangle(cap_center_id, cap_center_id + i + 1, (i < sectorCount - 1) ? cap_center_id + i + 2 : cap_center_id + 1); + } + + return data; +} #endif // ENABLE_LEGACY_OPENGL_REMOVAL } // namespace GUI diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index f3c62d786c..7d2f785573 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -365,7 +365,14 @@ namespace GUI { #if ENABLE_LEGACY_OPENGL_REMOVAL // create a sphere with the given resolution and smooth normals // the origin of the sphere is in its center + // the radius of the sphere is the given value GLModel::Geometry smooth_sphere(unsigned int resolution, float radius); + // create a cylinder with the given resolution and smooth normals + // the axis of the cylinder is the given value + // the radius of the cylinder is the given value + // the height of the cylinder is the given value + // the origin of the cylinder is in the center of the cap face having axis == 0 + GLModel::Geometry smooth_cylinder(Axis axis, unsigned int resolution, float radius, float height); #endif // ENABLE_LEGACY_OPENGL_REMOVAL } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 92fb8691ef..8b39582822 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -8,6 +8,7 @@ #include "libslic3r/Model.hpp" #include "libslic3r/Measure.hpp" +#include "libslic3r/PresetBundle.hpp" #include @@ -21,8 +22,8 @@ static const Slic3r::ColorRGBA HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { - m_vbo_sphere.init_from(its_make_sphere(1., M_PI/32.)); - m_vbo_cylinder.init_from(its_make_cylinder(1., 1.)); + m_vbo_sphere.init_from(smooth_sphere(16, 7.5f)); + m_vbo_cylinder.init_from(smooth_cylinder(Z, 16, 5.0f, 1.0f)); } bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) @@ -106,7 +107,9 @@ std::string GLGizmoMeasure::on_get_name() const bool GLGizmoMeasure::on_is_activable() const { const Selection& selection = m_parent.get_selection(); - return selection.is_single_volume() || selection.is_single_volume_instance(); + return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) ? + selection.is_single_full_instance() : + selection.is_single_volume() || selection.is_single_volume_instance(); } void GLGizmoMeasure::on_render() @@ -117,30 +120,27 @@ void GLGizmoMeasure::on_render() const Selection& selection = m_parent.get_selection(); - GLShaderProgram* shader = wxGetApp().get_shader("flat"); + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; shader->start_using(); + shader->set_uniform("emission_factor", 0.25f); glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_BLEND)); -#if ENABLE_GL_CORE_PROFILE - if (!OpenGLManager::get_gl_info().is_core_profile()) -#endif // ENABLE_GL_CORE_PROFILE - glsafe(::glLineWidth(2.f)); - if (selection.is_single_volume() || selection.is_single_volume_instance()) { + if ((wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA && selection.is_single_full_instance()) || + (selection.is_single_volume() || selection.is_single_volume_instance())) { const Transform3d& model_matrix = selection.get_first_volume()->world_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d view_model_matrix = camera.get_view_matrix() * - Geometry::assemble_transform(selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * model_matrix; + const Transform3d& view_matrix = camera.get_view_matrix(); + const float inv_zoom = camera.get_inv_zoom(); - shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - + update_if_needed(); Vec3f pos; @@ -148,7 +148,6 @@ void GLGizmoMeasure::on_render() size_t facet_idx; m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), model_matrix, camera, pos, normal, nullptr, &facet_idx); -#define ENABLE_DEBUG_DIALOG 0 #if ENABLE_DEBUG_DIALOG m_imgui->begin(std::string("DEBUG")); m_imgui->checkbox(wxString("Show all features"), m_show_all); @@ -162,34 +161,46 @@ void GLGizmoMeasure::on_render() #endif // ENABLE_DEBUG_DIALOG std::vector features; - if (m_show_all) { +#if ENABLE_DEBUG_DIALOG + if (m_show_all) { features = m_measuring->get_all_features(); // EXPENSIVE - debugging only. features.erase(std::remove_if(features.begin(), features.end(), [](const Measure::SurfaceFeature& f) { return f.get_type() == Measure::SurfaceFeatureType::Plane; }), features.end()); - } else { + } + else { +#endif // ENABLE_DEBUG_DIALOG std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); - if (feat) + if (feat.has_value()) features.emplace_back(*feat); - } - - +#if ENABLE_DEBUG_DIALOG + } +#endif // ENABLE_DEBUG_DIALOG for (const Measure::SurfaceFeature& feature : features) { - - if (feature.get_type() == Measure::SurfaceFeatureType::Point) { - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.get_point())); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); + switch (feature.get_type()) { + case Measure::SurfaceFeatureType::Point: + { + const Vec3d& position = feature.get_point(); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_vbo_sphere.set_color(HOVER_COLOR); m_vbo_sphere.render(); + break; } - else if (feature.get_type() == Measure::SurfaceFeatureType::Circle) { - const auto& [c, radius, n] = feature.get_circle(); - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(c)); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, n] = feature.get_circle(); + // render center + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_vbo_sphere.set_color(HOVER_COLOR); m_vbo_sphere.render(); @@ -198,45 +209,69 @@ void GLGizmoMeasure::on_render() if (rad.squaredNorm() < 0.1) rad = n.cross(Vec3d::UnitY()); rad *= radius * rad.norm(); - const int N = 20; - for (int i=0; iset_uniform("view_model_matrix", view_feature_matrix); + const int N = 32; + m_vbo_sphere.set_color(HOVER_COLOR); + for (int i = 0; i < N; ++i) { + rad = Eigen::AngleAxisd(2.0 * M_PI / N, n) * rad; + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(center + rad) * Geometry::scale_transform(N / 100.0 * inv_zoom); + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_vbo_sphere.render(); } + break; } - else if (feature.get_type() == Measure::SurfaceFeatureType::Edge) { + case Measure::SurfaceFeatureType::Edge: + { const auto& [start, end] = feature.get_edge(); - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(start)); - auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); - view_feature_matrix *= q; - view_feature_matrix.scale(Vec3d(0.075, 0.075, (end - start).norm())); - shader->set_uniform("view_model_matrix", view_feature_matrix); + auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * + Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_vbo_cylinder.set_color(HOVER_COLOR); m_vbo_cylinder.render(); - if (feature.get_extra_point()) { - Vec3d pin = *feature.get_extra_point(); - view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(pin)); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); + +/* + std::optional extra_point = feature.get_extra_point(); + if (extra_point.has_value()) { + const Vec3d pin = *extra_point; + const Transform3d feature_matrix = Geometry::translation_transform(pin + selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * + Geometry::scale_transform(inv_zoom) * model_matrix; + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_vbo_sphere.set_color(HOVER_COLOR); m_vbo_sphere.render(); } +*/ + break; } - else if (feature.get_type() == Measure::SurfaceFeatureType::Plane) { + case Measure::SurfaceFeatureType::Plane: + { const auto& [idx, normal, pt] = feature.get_plane(); assert(idx < m_plane_models.size()); + const Transform3d view_model_matrix = view_matrix * model_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_plane_models[idx]->set_color(HOVER_COLOR); m_plane_models[idx]->render(); - } + break; + } + } } - shader->set_uniform("view_model_matrix", view_model_matrix); +#if ENABLE_DEBUG_DIALOG if (m_show_planes) - for (const auto& glmodel : m_plane_models) + for (const auto& glmodel : m_plane_models) { + glmodel->set_color(HOVER_COLOR); glmodel->render(); + } +#endif // ENABLE_DEBUG_DIALOG } glsafe(::glEnable(GL_CULL_FACE)); @@ -263,14 +298,19 @@ void GLGizmoMeasure::update_if_needed() const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); for (const std::vector& triangle_indices : planes_triangles) { m_plane_models.emplace_back(std::unique_ptr(new GLModel())); - GUI::GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3 }; + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; init_data.color = ColorRGBA(0.9f, 0.9f, 0.9f, 0.5f); int i = 0; for (int idx : triangle_indices) { - init_data.add_vertex(its.vertices[its.indices[idx][0]]); - init_data.add_vertex(its.vertices[its.indices[idx][1]]); - init_data.add_vertex(its.vertices[its.indices[idx][2]]); + const Vec3f& v0 = its.vertices[its.indices[idx][0]]; + const Vec3f& v1 = its.vertices[its.indices[idx][1]]; + const Vec3f& v2 = its.vertices[its.indices[idx][2]]; + + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); init_data.add_triangle(i, i + 1, i + 2); i += 3; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index cfdc760608..72454f475d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -11,6 +11,7 @@ #include +#define ENABLE_DEBUG_DIALOG 0 namespace Slic3r { @@ -48,8 +49,10 @@ private: int m_mouse_pos_x; int m_mouse_pos_y; +#if ENABLE_DEBUG_DIALOG bool m_show_all = false; bool m_show_planes = false; +#endif // ENABLE_DEBUG_DIALOG std::vector> m_plane_models; void update_if_needed(); From 8f06086679eb695d3e79f36044255e0fa6ac4d21 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 23 Aug 2022 13:53:55 +0200 Subject: [PATCH 023/103] Follow-up of a7d1c9b5e99132e51d84fcd5155ede7da1ecadd0 - Simplified code to generate a smooth cylinder --- src/slic3r/GUI/GLModel.cpp | 39 ++++-------------------- src/slic3r/GUI/GLModel.hpp | 13 +++----- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 2 +- 3 files changed, 12 insertions(+), 42 deletions(-) diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index e05ae7619b..249a5cce7e 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -2275,7 +2275,7 @@ GLModel::Geometry smooth_sphere(unsigned int resolution, float radius) return data; } -GLModel::Geometry smooth_cylinder(Axis axis, unsigned int resolution, float radius, float height) +GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float height) { resolution = std::max(4, resolution); @@ -2287,39 +2287,19 @@ GLModel::Geometry smooth_cylinder(Axis axis, unsigned int resolution, float radi data.reserve_vertices(sectorCount * 4 + 2); data.reserve_indices(sectorCount * 4 * 3); - auto generate_vertices_on_circle = [sectorCount, sectorStep](Axis axis, float radius) { + auto generate_vertices_on_circle = [sectorCount, sectorStep](float radius) { std::vector ret; ret.reserve(sectorCount); for (unsigned int i = 0; i < sectorCount; ++i) { // from 0 to 2pi const float sectorAngle = (i != sectorCount) ? sectorStep * i : 0.0f; - const float x1 = radius * std::cos(sectorAngle); - const float x2 = radius * std::sin(sectorAngle); - - Vec3f v; - switch (axis) - { - case X: { v = Vec3f(0.0f, x1, x2); break; } - case Y: { v = Vec3f(-x1, 0.0f, x2); break; } - case Z: { v = Vec3f(x1, x2, 0.0f); break; } - default: { assert(false); break; } - } - - ret.emplace_back(v); + ret.emplace_back(radius * std::cos(sectorAngle), radius * std::sin(sectorAngle), 0.0f); } return ret; }; - const std::vector base_vertices = generate_vertices_on_circle(axis, radius); - - Vec3f h; - switch (axis) - { - case X: { h = height * Vec3f::UnitX(); break; } - case Y: { h = height * Vec3f::UnitY(); break; } - case Z: { h = height * Vec3f::UnitZ(); break; } - default: { assert(false); break; } - } + const std::vector base_vertices = generate_vertices_on_circle(radius); + const Vec3f h = height * Vec3f::UnitZ(); // stem vertices for (unsigned int i = 0; i < sectorCount; ++i) { @@ -2342,14 +2322,7 @@ GLModel::Geometry smooth_cylinder(Axis axis, unsigned int resolution, float radi // bottom cap vertices Vec3f cap_center = Vec3f::Zero(); unsigned int cap_center_id = data.vertices_count(); - Vec3f normal; - switch (axis) - { - case X: { normal = -Vec3f::UnitX(); break; } - case Y: { normal = -Vec3f::UnitY(); break; } - case Z: { normal = -Vec3f::UnitZ(); break; } - default: { assert(false); break; } - } + Vec3f normal = -Vec3f::UnitZ(); data.add_vertex(cap_center, normal); for (unsigned int i = 0; i < sectorCount; ++i) { diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index 7d2f785573..c5b9f5e095 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -363,16 +363,13 @@ namespace GUI { GLModel::Geometry diamond(unsigned int resolution); #if ENABLE_LEGACY_OPENGL_REMOVAL - // create a sphere with the given resolution and smooth normals + // create a sphere with smooth normals // the origin of the sphere is in its center - // the radius of the sphere is the given value GLModel::Geometry smooth_sphere(unsigned int resolution, float radius); - // create a cylinder with the given resolution and smooth normals - // the axis of the cylinder is the given value - // the radius of the cylinder is the given value - // the height of the cylinder is the given value - // the origin of the cylinder is in the center of the cap face having axis == 0 - GLModel::Geometry smooth_cylinder(Axis axis, unsigned int resolution, float radius, float height); + // create a cylinder with smooth normals + // the axis of the cylinder is the Z axis + // the origin of the cylinder is the center of its bottom cap face + GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float height); #endif // ENABLE_LEGACY_OPENGL_REMOVAL } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 8b39582822..32f110d9fd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -23,7 +23,7 @@ GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filen : GLGizmoBase(parent, icon_filename, sprite_id) { m_vbo_sphere.init_from(smooth_sphere(16, 7.5f)); - m_vbo_cylinder.init_from(smooth_cylinder(Z, 16, 5.0f, 1.0f)); + m_vbo_cylinder.init_from(smooth_cylinder(16, 5.0f, 1.0f)); } bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) From 26650a26588c2039a1743ef7e982de9fd6d58c0e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 24 Aug 2022 11:25:15 +0200 Subject: [PATCH 024/103] Measuring: circle feature rendered using a torus --- src/slic3r/GUI/GLModel.cpp | 47 ++++++++++++++++- src/slic3r/GUI/GLModel.hpp | 4 ++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 67 ++++++++---------------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 5 +- 4 files changed, 76 insertions(+), 47 deletions(-) diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 249a5cce7e..b36db83ad1 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -2292,7 +2292,7 @@ GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float h ret.reserve(sectorCount); for (unsigned int i = 0; i < sectorCount; ++i) { // from 0 to 2pi - const float sectorAngle = (i != sectorCount) ? sectorStep * i : 0.0f; + const float sectorAngle = sectorStep * i; ret.emplace_back(radius * std::cos(sectorAngle), radius * std::sin(sectorAngle), 0.0f); } return ret; @@ -2351,6 +2351,51 @@ GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float h return data; } + +GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness) +{ + primary_resolution = std::max(4, primary_resolution); + secondary_resolution = std::max(4, secondary_resolution); + const unsigned int torusSectorCount = primary_resolution; + const float torusSectorStep = 2.0f * float(M_PI) / float(torusSectorCount); + const unsigned int sectionSectorCount = secondary_resolution; + const float sectionSectorStep = 2.0f * float(M_PI) / float(sectionSectorCount); + + GLModel::Geometry data; + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + data.reserve_vertices(torusSectorCount * sectionSectorCount); + data.reserve_indices(torusSectorCount * sectionSectorCount * 2 * 3); + + // vertices + for (unsigned int i = 0; i < torusSectorCount; ++i) { + const float sectionAngle = torusSectorStep * i; + const Vec3f sectionCenter(radius * std::cos(sectionAngle), radius * std::sin(sectionAngle), 0.0f); + for (unsigned int j = 0; j < sectionSectorCount; ++j) { + const float circleAngle = sectionSectorStep * j; + const float thickness_xy = thickness * std::cos(circleAngle); + const float thickness_z = thickness * std::sin(circleAngle); + const Vec3f v(thickness_xy * std::cos(sectionAngle), thickness_xy * std::sin(sectionAngle), thickness_z); + data.add_vertex(sectionCenter + v, (Vec3f)v.normalized()); + } + } + + // triangles + for (unsigned int i = 0; i < torusSectorCount; ++i) { + const unsigned int ii = i * sectionSectorCount; + const unsigned int ii_next = ((i + 1) % torusSectorCount) * sectionSectorCount; + for (unsigned int j = 0; j < sectionSectorCount; ++j) { + const unsigned int j_next = (j + 1) % sectionSectorCount; + const unsigned int i0 = ii + j; + const unsigned int i1 = ii_next + j; + const unsigned int i2 = ii_next + j_next; + const unsigned int i3 = ii + j_next; + data.add_triangle(i0, i1, i2); + data.add_triangle(i0, i2, i3); + } + } + + return data; +} #endif // ENABLE_LEGACY_OPENGL_REMOVAL } // namespace GUI diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index c5b9f5e095..8843d39eeb 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -370,6 +370,10 @@ namespace GUI { // the axis of the cylinder is the Z axis // the origin of the cylinder is the center of its bottom cap face GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float height); + // create a torus with smooth normals + // the axis of the torus is the Z axis + // the origin of the torus is in its center + GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness); #endif // ENABLE_LEGACY_OPENGL_REMOVAL } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 32f110d9fd..6e1ba9991e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -22,8 +22,8 @@ static const Slic3r::ColorRGBA HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { - m_vbo_sphere.init_from(smooth_sphere(16, 7.5f)); - m_vbo_cylinder.init_from(smooth_cylinder(16, 5.0f, 1.0f)); + m_sphere.init_from(smooth_sphere(16, 7.5f)); + m_cylinder.init_from(smooth_cylinder(16, 5.0f, 1.0f)); } bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) @@ -188,38 +188,32 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_vbo_sphere.set_color(HOVER_COLOR); - m_vbo_sphere.render(); + m_sphere.set_color(HOVER_COLOR); + m_sphere.render(); break; } case Measure::SurfaceFeatureType::Circle: { const auto& [center, radius, n] = feature.get_circle(); // render center - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_vbo_sphere.set_color(HOVER_COLOR); - m_vbo_sphere.render(); + const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); + const Transform3d center_view_model_matrix = view_matrix * center_matrix; + shader->set_uniform("view_model_matrix", center_view_model_matrix); + const Matrix3d center_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * center_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", center_view_normal_matrix); + m_sphere.set_color(HOVER_COLOR); + m_sphere.render(); - // Now draw the circle itself - let's take a funny shortcut: - Vec3d rad = n.cross(Vec3d::UnitX()); - if (rad.squaredNorm() < 0.1) - rad = n.cross(Vec3d::UnitY()); - rad *= radius * rad.norm(); - const int N = 32; - m_vbo_sphere.set_color(HOVER_COLOR); - for (int i = 0; i < N; ++i) { - rad = Eigen::AngleAxisd(2.0 * M_PI / N, n) * rad; - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(center + rad) * Geometry::scale_transform(N / 100.0 * inv_zoom); - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_vbo_sphere.render(); - } + m_circle.reset(); + m_circle.init_from(smooth_torus(64, 16, float(radius), 5.0f * inv_zoom)); + + const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); + const Transform3d circle_view_model_matrix = view_matrix * circle_matrix; + shader->set_uniform("view_model_matrix", circle_view_model_matrix); + const Matrix3d circle_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * circle_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", circle_view_normal_matrix); + m_circle.set_color(HOVER_COLOR); + m_circle.render(); break; } case Measure::SurfaceFeatureType::Edge: @@ -232,23 +226,8 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_vbo_cylinder.set_color(HOVER_COLOR); - m_vbo_cylinder.render(); - -/* - std::optional extra_point = feature.get_extra_point(); - if (extra_point.has_value()) { - const Vec3d pin = *extra_point; - const Transform3d feature_matrix = Geometry::translation_transform(pin + selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * - Geometry::scale_transform(inv_zoom) * model_matrix; - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_vbo_sphere.set_color(HOVER_COLOR); - m_vbo_sphere.render(); - } -*/ + m_cylinder.set_color(HOVER_COLOR); + m_cylinder.render(); break; } case Measure::SurfaceFeatureType::Plane: diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 72454f475d..79bc934cbb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -32,8 +32,9 @@ class GLGizmoMeasure : public GLGizmoBase private: std::unique_ptr m_measuring; - GLModel m_vbo_sphere; - GLModel m_vbo_cylinder; + GLModel m_sphere; + GLModel m_cylinder; + GLModel m_circle; // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; From 2abb52ed4c88f9b5b3d52297d356955c1f4039e2 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 24 Aug 2022 12:00:04 +0200 Subject: [PATCH 025/103] Measuring: refactoring related to plane models cache --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 41 +++++++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 2 +- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 6e1ba9991e..d6bd5e67be 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -233,23 +233,28 @@ void GLGizmoMeasure::on_render() case Measure::SurfaceFeatureType::Plane: { const auto& [idx, normal, pt] = feature.get_plane(); - assert(idx < m_plane_models.size()); + assert(idx < m_plane_models_cache.size()); const Transform3d view_model_matrix = view_matrix * model_matrix; shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_plane_models[idx]->set_color(HOVER_COLOR); - m_plane_models[idx]->render(); + m_plane_models_cache[idx].set_color(HOVER_COLOR); + m_plane_models_cache[idx].render(); break; } } } #if ENABLE_DEBUG_DIALOG - if (m_show_planes) - for (const auto& glmodel : m_plane_models) { - glmodel->set_color(HOVER_COLOR); - glmodel->render(); + if (m_show_planes) { + const Transform3d view_model_matrix = view_matrix * model_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + for (GLModel& glmodel : m_plane_models_cache) { + glmodel.set_color(HOVER_COLOR); + glmodel.render(); } + } #endif // ENABLE_DEBUG_DIALOG } @@ -270,17 +275,14 @@ void GLGizmoMeasure::on_render() void GLGizmoMeasure::update_if_needed() { - auto do_update = [this](const ModelObject* object, const ModelVolume* volume) { - const indexed_triangle_set& its = (volume != nullptr) ? volume->mesh().its : object->volumes.front()->mesh().its; - m_measuring.reset(new Measure::Measuring(its)); - m_plane_models.clear(); + auto update_plane_models_cache = [this](const indexed_triangle_set& its) { + m_plane_models_cache.clear(); const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); for (const std::vector& triangle_indices : planes_triangles) { - m_plane_models.emplace_back(std::unique_ptr(new GLModel())); + m_plane_models_cache.emplace_back(GLModel()); GLModel::Geometry init_data; init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - init_data.color = ColorRGBA(0.9f, 0.9f, 0.9f, 0.5f); - int i = 0; + unsigned int i = 0; for (int idx : triangle_indices) { const Vec3f& v0 = its.vertices[its.indices[idx][0]]; const Vec3f& v1 = its.vertices[its.indices[idx][1]]; @@ -293,8 +295,15 @@ void GLGizmoMeasure::update_if_needed() init_data.add_triangle(i, i + 1, i + 2); i += 3; } - m_plane_models.back()->init_from(std::move(init_data)); + m_plane_models_cache.back().init_from(std::move(init_data)); } + }; + + auto do_update = [this, update_plane_models_cache](const ModelObject* object, const ModelVolume* volume) { + const indexed_triangle_set& its = (volume != nullptr) ? volume->mesh().its : object->volumes.front()->mesh().its; + m_measuring.reset(new Measure::Measuring(its)); + + update_plane_models_cache(its); // Let's save what we calculated it from: m_volumes_matrices.clear(); @@ -306,7 +315,7 @@ void GLGizmoMeasure::update_if_needed() m_volumes_matrices.push_back(vol->get_matrix()); m_volumes_types.push_back(vol->type()); } - m_first_instance_scale = object->instances.front()->get_scaling_factor(); + m_first_instance_scale = object->instances.front()->get_scaling_factor(); m_first_instance_mirror = object->instances.front()->get_mirror(); } m_old_model_object = object; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 79bc934cbb..8197ea6863 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -35,6 +35,7 @@ private: GLModel m_sphere; GLModel m_cylinder; GLModel m_circle; + std::vector m_plane_models_cache; // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; @@ -54,7 +55,6 @@ private: bool m_show_all = false; bool m_show_planes = false; #endif // ENABLE_DEBUG_DIALOG - std::vector> m_plane_models; void update_if_needed(); From d0bf05d1e9760e3d452f612c70bee282b6486c5e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 24 Aug 2022 14:15:20 +0200 Subject: [PATCH 026/103] Refactoring into GLGizmoMeasure::on_render() --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 67 +++++++++++++----------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index d6bd5e67be..844ed8876f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -114,34 +114,20 @@ bool GLGizmoMeasure::on_is_activable() const void GLGizmoMeasure::on_render() { +#if !ENABLE_DEBUG_DIALOG // do not render if the user is panning/rotating the 3d scene if (m_parent.is_mouse_dragging()) return; +#endif // !ENABLE_DEBUG_DIALOG const Selection& selection = m_parent.get_selection(); - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); - if (shader == nullptr) - return; - - shader->start_using(); - shader->set_uniform("emission_factor", 0.25f); - - glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - - glsafe(::glEnable(GL_DEPTH_TEST)); - glsafe(::glEnable(GL_BLEND)); - if ((wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA && selection.is_single_full_instance()) || (selection.is_single_volume() || selection.is_single_volume_instance())) { + update_if_needed(); + const Transform3d& model_matrix = selection.get_first_volume()->world_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d& view_matrix = camera.get_view_matrix(); - const float inv_zoom = camera.get_inv_zoom(); - - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - - update_if_needed(); Vec3f pos; Vec3f normal; @@ -165,19 +151,42 @@ void GLGizmoMeasure::on_render() if (m_show_all) { features = m_measuring->get_all_features(); // EXPENSIVE - debugging only. features.erase(std::remove_if(features.begin(), features.end(), - [](const Measure::SurfaceFeature& f) { - return f.get_type() == Measure::SurfaceFeatureType::Plane; - }), features.end()); + [](const Measure::SurfaceFeature& f) { + return f.get_type() == Measure::SurfaceFeatureType::Plane; + }), features.end()); } else { + if (!m_parent.is_mouse_dragging()) { #endif // ENABLE_DEBUG_DIALOG - std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); - if (feat.has_value()) - features.emplace_back(*feat); + std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); + if (feat.has_value()) + features.emplace_back(*feat); #if ENABLE_DEBUG_DIALOG + } } #endif // ENABLE_DEBUG_DIALOG - + +#if ENABLE_DEBUG_DIALOG + if (features.empty() && !m_show_planes) +#else + if (features.empty()) +#endif // ENABLE_DEBUG_DIALOG + return; + + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + if (shader == nullptr) + return; + + shader->start_using(); + shader->set_uniform("emission_factor", 0.25f); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); + glsafe(::glEnable(GL_DEPTH_TEST)); + + const Transform3d& view_matrix = camera.get_view_matrix(); + const float inv_zoom = camera.get_inv_zoom(); + for (const Measure::SurfaceFeature& feature : features) { switch (feature.get_type()) { case Measure::SurfaceFeatureType::Point: @@ -256,18 +265,12 @@ void GLGizmoMeasure::on_render() } } #endif // ENABLE_DEBUG_DIALOG + shader->stop_using(); } - - glsafe(::glEnable(GL_CULL_FACE)); - glsafe(::glDisable(GL_BLEND)); - - shader->stop_using(); } - - #if ! ENABLE_LEGACY_OPENGL_REMOVAL #error NOT IMPLEMENTED #endif From 38c52941d68723b887b935622c6dd1f50fc46da6 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 24 Aug 2022 15:02:46 +0200 Subject: [PATCH 027/103] Added tech ENABLE_MEASURE_GIZMO_DEBUG to embed debug code related to GLGizmoMeasure --- src/libslic3r/Measure.cpp | 3 ++ src/libslic3r/Measure.hpp | 4 +- src/libslic3r/Technologies.hpp | 2 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 50 ++++++++++-------------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 6 +-- 5 files changed, 30 insertions(+), 35 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 8c7254f85b..9cb46edfc1 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -1,3 +1,4 @@ +#include "libslic3r/libslic3r.h" #include "Measure.hpp" #include "libslic3r/Geometry/Circle.hpp" @@ -407,10 +408,12 @@ Measuring::Measuring(const indexed_triangle_set& its) Measuring::~Measuring() {} +#if ENABLE_MEASURE_GIZMO_DEBUG std::vector Measuring::get_all_features() const { return priv->get_all_features(); } +#endif // ENABLE_MEASURE_GIZMO_DEBUG std::optional Measuring::get_feature(size_t face_idx, const Vec3d& point) const diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index fe0b1687a1..98517b991b 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -69,9 +69,11 @@ public: explicit Measuring(const indexed_triangle_set& its); ~Measuring(); +#if ENABLE_MEASURE_GIZMO_DEBUG // Return a reference to a list of all features identified on the its. // Use only for debugging. Expensive, do not call often. - [[deprecated]] std::vector get_all_features() const; + std::vector get_all_features() const; +#endif // ENABLE_MEASURE_GIZMO_DEBUG // Given a face_idx where the mouse cursor points, return a feature that // should be highlighted (if any). diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index ee2c0cd804..58720801f8 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -78,6 +78,8 @@ // Enable picking using raytracing #define ENABLE_RAYCAST_PICKING (1 && ENABLE_LEGACY_OPENGL_REMOVAL) #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) +// Enable debug code for Measure Gizmo +#define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_2_5_0_ALPHA1) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 844ed8876f..7ca051c7d1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -114,11 +114,11 @@ bool GLGizmoMeasure::on_is_activable() const void GLGizmoMeasure::on_render() { -#if !ENABLE_DEBUG_DIALOG +#if !ENABLE_MEASURE_GIZMO_DEBUG // do not render if the user is panning/rotating the 3d scene if (m_parent.is_mouse_dragging()) return; -#endif // !ENABLE_DEBUG_DIALOG +#endif // !ENABLE_MEASURE_GIZMO_DEBUG const Selection& selection = m_parent.get_selection(); @@ -134,7 +134,7 @@ void GLGizmoMeasure::on_render() size_t facet_idx; m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), model_matrix, camera, pos, normal, nullptr, &facet_idx); -#if ENABLE_DEBUG_DIALOG +#if ENABLE_MEASURE_GIZMO_DEBUG m_imgui->begin(std::string("DEBUG")); m_imgui->checkbox(wxString("Show all features"), m_show_all); m_imgui->checkbox(wxString("Show all planes"), m_show_planes); @@ -144,33 +144,35 @@ void GLGizmoMeasure::on_render() m_imgui->text(std::string("pos_y: ") + std::to_string(pos.y())); m_imgui->text(std::string("pos_z: ") + std::to_string(pos.z())); m_imgui->end(); -#endif // ENABLE_DEBUG_DIALOG +#endif // ENABLE_MEASURE_GIZMO_DEBUG std::vector features; -#if ENABLE_DEBUG_DIALOG - if (m_show_all) { - features = m_measuring->get_all_features(); // EXPENSIVE - debugging only. - features.erase(std::remove_if(features.begin(), features.end(), - [](const Measure::SurfaceFeature& f) { - return f.get_type() == Measure::SurfaceFeatureType::Plane; - }), features.end()); +#if ENABLE_MEASURE_GIZMO_DEBUG + if (m_show_all || m_show_planes) { + features = m_measuring->get_all_features(); + if (!m_show_planes) + features.erase(std::remove_if(features.begin(), features.end(), + [](const Measure::SurfaceFeature& f) { + return f.get_type() == Measure::SurfaceFeatureType::Plane; + }), features.end()); + if (!m_show_all) + features.erase(std::remove_if(features.begin(), features.end(), + [](const Measure::SurfaceFeature& f) { + return f.get_type() != Measure::SurfaceFeatureType::Plane; + }), features.end()); } else { if (!m_parent.is_mouse_dragging()) { -#endif // ENABLE_DEBUG_DIALOG +#endif // ENABLE_MEASURE_GIZMO_DEBUG std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); if (feat.has_value()) features.emplace_back(*feat); -#if ENABLE_DEBUG_DIALOG +#if ENABLE_MEASURE_GIZMO_DEBUG } } -#endif // ENABLE_DEBUG_DIALOG +#endif // ENABLE_MEASURE_GIZMO_DEBUG -#if ENABLE_DEBUG_DIALOG - if (features.empty() && !m_show_planes) -#else if (features.empty()) -#endif // ENABLE_DEBUG_DIALOG return; GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); @@ -253,18 +255,6 @@ void GLGizmoMeasure::on_render() } } } -#if ENABLE_DEBUG_DIALOG - if (m_show_planes) { - const Transform3d view_model_matrix = view_matrix * model_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - for (GLModel& glmodel : m_plane_models_cache) { - glmodel.set_color(HOVER_COLOR); - glmodel.render(); - } - } -#endif // ENABLE_DEBUG_DIALOG shader->stop_using(); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 8197ea6863..49ab7fce8a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -11,8 +11,6 @@ #include -#define ENABLE_DEBUG_DIALOG 0 - namespace Slic3r { class ModelVolume; @@ -51,10 +49,10 @@ private: int m_mouse_pos_x; int m_mouse_pos_y; -#if ENABLE_DEBUG_DIALOG +#if ENABLE_MEASURE_GIZMO_DEBUG bool m_show_all = false; bool m_show_planes = false; -#endif // ENABLE_DEBUG_DIALOG +#endif // ENABLE_MEASURE_GIZMO_DEBUG void update_if_needed(); From 1787f780b60c02344f914087d280a6dd98877080 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 25 Aug 2022 13:01:26 +0200 Subject: [PATCH 028/103] Measuring: code for Measure gizmo embedded into new tech ENABLE_MEASURE_GIZMO --- src/libslic3r/Technologies.hpp | 4 +++- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 9 ++++----- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 11 ++++------- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 4 ++++ 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 58720801f8..5eadc8d098 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -78,8 +78,10 @@ // Enable picking using raytracing #define ENABLE_RAYCAST_PICKING (1 && ENABLE_LEGACY_OPENGL_REMOVAL) #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) +// Enable Measure Gizmo +#define ENABLE_MEASURE_GIZMO (1 && ENABLE_LEGACY_OPENGL_REMOVAL) // Enable debug code for Measure Gizmo -#define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_2_5_0_ALPHA1) +#define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_MEASURE_GIZMO) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 7ca051c7d1..73ee87840a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -14,6 +14,8 @@ #include +#if ENABLE_MEASURE_GIZMO + namespace Slic3r { namespace GUI { @@ -261,11 +263,6 @@ void GLGizmoMeasure::on_render() -#if ! ENABLE_LEGACY_OPENGL_REMOVAL - #error NOT IMPLEMENTED -#endif - - void GLGizmoMeasure::update_if_needed() { auto update_plane_models_cache = [this](const indexed_triangle_set& its) { @@ -345,3 +342,5 @@ void GLGizmoMeasure::update_if_needed() } // namespace GUI } // namespace Slic3r + +#endif // ENABLE_MEASURE_GIZMO diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 49ab7fce8a..742c791131 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -1,15 +1,10 @@ #ifndef slic3r_GLGizmoMeasure_hpp_ #define slic3r_GLGizmoMeasure_hpp_ +#if ENABLE_MEASURE_GIZMO + #include "GLGizmoBase.hpp" -#if ENABLE_LEGACY_OPENGL_REMOVAL #include "slic3r/GUI/GLModel.hpp" -#else -#include "slic3r/GUI/3DScene.hpp" -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - - -#include namespace Slic3r { @@ -80,4 +75,6 @@ protected: } // namespace GUI } // namespace Slic3r +#endif // ENABLE_MEASURE_GIZMO + #endif // slic3r_GLGizmoMeasure_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 6d25f84fbc..ff24dd84a3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -21,7 +21,9 @@ #include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp" #include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp" +#if ENABLE_MEASURE_GIZMO #include "slic3r/GUI/Gizmos/GLGizmoMeasure.hpp" +#endif // ENABLE_MEASURE_GIZMO #include "libslic3r/format.hpp" #include "libslic3r/Model.hpp" @@ -107,7 +109,9 @@ bool GLGizmosManager::init() m_gizmos.emplace_back(new GLGizmoSeam(m_parent, "seam.svg", 8)); m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "mmu_segmentation.svg", 9)); m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "cut.svg", 10)); +#if ENABLE_MEASURE_GIZMO m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, "measure.svg", 11)); +#endif // ENABLE_MEASURE_GIZMO m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent)); From 8833bc3138676461ed443f19f900e80e0402bac3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 29 Aug 2022 12:55:34 +0200 Subject: [PATCH 029/103] Measuring: Measure gizmo features registered for raycasted picking --- src/libslic3r/Measure.hpp | 10 ++ src/libslic3r/Technologies.hpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 4 +- src/slic3r/GUI/GLCanvas3D.hpp | 2 +- src/slic3r/GUI/GLModel.cpp | 15 +++ src/slic3r/GUI/GLModel.hpp | 2 + src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 132 ++++++++++++++++--- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 16 ++- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 4 +- 10 files changed, 160 insertions(+), 29 deletions(-) diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 98517b991b..2f27a72598 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -49,6 +49,16 @@ public: // For anything, return an extra point that should also be considered a part of this. std::optional get_extra_point() const { assert(m_type != SurfaceFeatureType::Undef); return m_pt3; } + bool operator == (const SurfaceFeature& other) const { + if (this->m_type != other.m_type) return false; + if (!this->m_pt1.isApprox(other.m_pt1)) return false; + if (!this->m_pt2.isApprox(other.m_pt2)) return false; + if (this->m_pt3.has_value() && !other.m_pt3.has_value()) return false; + if (!this->m_pt3.has_value() && other.m_pt3.has_value()) return false; + if (this->m_pt3.has_value() && other.m_pt3.has_value() && !(*this->m_pt3).isApprox(*other.m_pt3)) return false; + return this->m_value == other.m_value; + } + private: SurfaceFeatureType m_type = SurfaceFeatureType::Undef; Vec3d m_pt1; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 5eadc8d098..4e2011b3b4 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -79,7 +79,7 @@ #define ENABLE_RAYCAST_PICKING (1 && ENABLE_LEGACY_OPENGL_REMOVAL) #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) // Enable Measure Gizmo -#define ENABLE_MEASURE_GIZMO (1 && ENABLE_LEGACY_OPENGL_REMOVAL) +#define ENABLE_MEASURE_GIZMO (1 && ENABLE_RAYCAST_PICKING) // Enable debug code for Measure Gizmo #define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_MEASURE_GIZMO) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index dcc0d2db4f..9c98a20e7a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2234,7 +2234,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re #if ENABLE_LEGACY_OPENGL_REMOVAL volume.model.init_from(mesh); #if ENABLE_RAYCAST_PICKING - volume.mesh_raycaster = std::make_unique(std::make_shared(mesh)); + volume.mesh_raycaster = std::make_unique(std::make_shared(mesh)); #endif // ENABLE_RAYCAST_PICKING #else volume.indexed_vertex_array.load_mesh(mesh); @@ -2254,7 +2254,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re #if ENABLE_RAYCAST_PICKING const TriangleMesh& new_mesh = m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(); volume.model.init_from(new_mesh); - volume.mesh_raycaster = std::make_unique(std::make_shared(new_mesh)); + volume.mesh_raycaster = std::make_unique(std::make_shared(new_mesh)); #else volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); #endif // ENABLE_RAYCAST_PICKING diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 4b78b66d99..b058386f56 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -680,7 +680,7 @@ public: #if ENABLE_RAYCAST_PICKING std::shared_ptr add_raycaster_for_picking(SceneRaycaster::EType type, int id, const MeshRaycaster& raycaster, - const Transform3d& trafo, bool use_back_faces = false) { + const Transform3d& trafo = Transform3d::Identity(), bool use_back_faces = false) { return m_scene_raycaster.add_raycaster(type, id, raycaster, trafo, use_back_faces); } void remove_raycasters_for_picking(SceneRaycaster::EType type, int id) { diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index b36db83ad1..445711f885 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -278,6 +278,21 @@ void GLModel::Geometry::remove_vertex(size_t id) } } +indexed_triangle_set GLModel::Geometry::get_as_indexed_triangle_set() const +{ + indexed_triangle_set its; + its.vertices.reserve(vertices_count()); + for (size_t i = 0; i < vertices_count(); ++i) { + its.vertices.emplace_back(extract_position_3(i)); + } + its.indices.reserve(indices_count() / 3); + for (size_t i = 0; i < indices_count() / 3; ++i) { + const size_t tri_id = i * 3; + its.indices.emplace_back(extract_index(tri_id), extract_index(tri_id + 1), extract_index(tri_id + 2)); + } + return its; +} + size_t GLModel::Geometry::vertex_stride_floats(const Format& format) { switch (format.vertex_layout) diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index 8843d39eeb..d4d7307855 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -129,6 +129,8 @@ namespace GUI { size_t vertices_size_bytes() const { return vertices_size_floats() * sizeof(float); } size_t indices_size_bytes() const { return indices.size() * index_stride_bytes(*this); } + indexed_triangle_set get_as_indexed_triangle_set() const; + static size_t vertex_stride_floats(const Format& format); static size_t vertex_stride_bytes(const Format& format) { return vertex_stride_floats(format) * sizeof(float); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index bbae3f2429..99769e0061 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -114,7 +114,7 @@ void GLGizmoHollow::on_register_raycasters_for_picking() if (info != nullptr && !info->model_object()->sla_drain_holes.empty()) { const sla::DrainHoles& drain_holes = info->model_object()->sla_drain_holes; for (int i = 0; i < (int)drain_holes.size(); ++i) { - m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cylinder.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cylinder.mesh_raycaster)); } update_raycasters_for_picking_transform(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 73ee87840a..e132cf2ce1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -7,7 +7,6 @@ #include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" #include "libslic3r/Model.hpp" -#include "libslic3r/Measure.hpp" #include "libslic3r/PresetBundle.hpp" #include @@ -20,12 +19,22 @@ namespace Slic3r { namespace GUI { static const Slic3r::ColorRGBA HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; +static const int POINT_ID = 100; +static const int EDGE_ID = 200; +static const int CIRCLE_ID = 300; +static const int CIRCLE_CENTER_ID = 301; +static const int PLANE_ID = 400; GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { - m_sphere.init_from(smooth_sphere(16, 7.5f)); - m_cylinder.init_from(smooth_cylinder(16, 5.0f, 1.0f)); + GLModel::Geometry sphere_geometry = smooth_sphere(16, 7.5f); + m_sphere.mesh_raycaster = std::make_unique(std::make_shared(std::move(sphere_geometry.get_as_indexed_triangle_set()))); + m_sphere.model.init_from(std::move(sphere_geometry)); + + GLModel::Geometry cylinder_geometry = smooth_cylinder(16, 5.0f, 1.0f); + m_cylinder.mesh_raycaster = std::make_unique(std::make_shared(std::move(cylinder_geometry.get_as_indexed_triangle_set()))); + m_cylinder.model.init_from(std::move(cylinder_geometry)); } bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) @@ -130,6 +139,7 @@ void GLGizmoMeasure::on_render() const Transform3d& model_matrix = selection.get_first_volume()->world_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); + const float inv_zoom = (float)camera.get_inv_zoom(); Vec3f pos; Vec3f normal; @@ -174,8 +184,70 @@ void GLGizmoMeasure::on_render() } #endif // ENABLE_MEASURE_GIZMO_DEBUG - if (features.empty()) - return; + if (m_features != features) { + GLGizmoMeasure::on_unregister_raycasters_for_picking(); + m_features = features; + if (m_features.empty()) + return; + +#if !ENABLE_MEASURE_GIZMO_DEBUG + for (const Measure::SurfaceFeature& feature : m_features) { + switch (feature.get_type()) { + case Measure::SurfaceFeatureType::Point: + { + m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, n] = feature.get_circle(); + m_circle.reset(); + GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); + m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); + m_circle.model.init_from(std::move(circle_geometry)); + + m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); + m_raycasters.insert({ CIRCLE_CENTER_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_CENTER_ID, *m_sphere.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto& [idx, normal, pt] = feature.get_plane(); + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + const std::vector& triangle_indices = planes_triangles[idx]; + + const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + unsigned int i = 0; + for (int idx : triangle_indices) { + const Vec3f& v0 = its.vertices[its.indices[idx][0]]; + const Vec3f& v1 = its.vertices[its.indices[idx][1]]; + const Vec3f& v2 = its.vertices[its.indices[idx][2]]; + + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; + } + + m_plane.reset(); + m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); + m_plane.model.init_from(std::move(init_data)); + m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); + break; + } + } + } +#endif // !ENABLE_MEASURE_GIZMO_DEBUG + } GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) @@ -189,9 +261,8 @@ void GLGizmoMeasure::on_render() glsafe(::glEnable(GL_DEPTH_TEST)); const Transform3d& view_matrix = camera.get_view_matrix(); - const float inv_zoom = camera.get_inv_zoom(); - for (const Measure::SurfaceFeature& feature : features) { + for (const Measure::SurfaceFeature& feature : m_features) { switch (feature.get_type()) { case Measure::SurfaceFeatureType::Point: { @@ -201,8 +272,11 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_sphere.set_color(HOVER_COLOR); - m_sphere.render(); + m_sphere.model.set_color(HOVER_COLOR); + m_sphere.model.render(); + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); break; } case Measure::SurfaceFeatureType::Circle: @@ -214,19 +288,22 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", center_view_model_matrix); const Matrix3d center_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * center_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", center_view_normal_matrix); - m_sphere.set_color(HOVER_COLOR); - m_sphere.render(); - - m_circle.reset(); - m_circle.init_from(smooth_torus(64, 16, float(radius), 5.0f * inv_zoom)); + m_sphere.model.set_color(HOVER_COLOR); + m_sphere.model.render(); + auto it = m_raycasters.find(CIRCLE_CENTER_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(center_matrix); const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); const Transform3d circle_view_model_matrix = view_matrix * circle_matrix; shader->set_uniform("view_model_matrix", circle_view_model_matrix); const Matrix3d circle_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * circle_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", circle_view_normal_matrix); - m_circle.set_color(HOVER_COLOR); - m_circle.render(); + m_circle.model.set_color(HOVER_COLOR); + m_circle.model.render(); + it = m_raycasters.find(CIRCLE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(circle_matrix); break; } case Measure::SurfaceFeatureType::Edge: @@ -239,8 +316,11 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_cylinder.set_color(HOVER_COLOR); - m_cylinder.render(); + m_cylinder.model.set_color(HOVER_COLOR); + m_cylinder.model.render(); + auto it = m_raycasters.find(EDGE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); break; } case Measure::SurfaceFeatureType::Plane: @@ -253,6 +333,9 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_normal_matrix", view_normal_matrix); m_plane_models_cache[idx].set_color(HOVER_COLOR); m_plane_models_cache[idx].render(); + auto it = m_raycasters.find(PLANE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(model_matrix); break; } } @@ -340,6 +423,19 @@ void GLGizmoMeasure::update_if_needed() } } +void GLGizmoMeasure::on_register_raycasters_for_picking() +{ + // the features are rendered on top of the scene, so the raytraced picker should take it into account + m_parent.set_raycaster_gizmos_on_top(true); +} + +void GLGizmoMeasure::on_unregister_raycasters_for_picking() +{ + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); + m_parent.set_raycaster_gizmos_on_top(false); + m_raycasters.clear(); +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 742c791131..9decf82fdc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -5,6 +5,7 @@ #include "GLGizmoBase.hpp" #include "slic3r/GUI/GLModel.hpp" +#include "libslic3r/Measure.hpp" namespace Slic3r { @@ -25,10 +26,14 @@ class GLGizmoMeasure : public GLGizmoBase private: std::unique_ptr m_measuring; - GLModel m_sphere; - GLModel m_cylinder; - GLModel m_circle; + PickingModel m_sphere; + PickingModel m_cylinder; + PickingModel m_circle; + PickingModel m_plane; + std::vector m_plane_models_cache; + std::map> m_raycasters; + std::vector m_features; // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; @@ -53,7 +58,7 @@ private: public: GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - + /// /// Apply rotation on select plane /// @@ -70,6 +75,9 @@ protected: void on_render() override; void on_set_state() override; CommonGizmosDataID on_get_requirements() const override; + + virtual void on_register_raycasters_for_picking() override; + virtual void on_unregister_raycasters_for_picking() override; }; } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 026fae17b6..237b101617 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -148,8 +148,8 @@ void GLGizmoSlaSupports::on_register_raycasters_for_picking() if (m_editing_mode && !m_editing_cache.empty()) { for (size_t i = 0; i < m_editing_cache.size(); ++i) { - m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_sphere.mesh_raycaster, Transform3d::Identity()), - m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cone.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_sphere.mesh_raycaster), + m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cone.mesh_raycaster)); } update_raycasters_for_picking_transform(); } From 4675ae2173ff2a9477f77b5bc321c8ee85e504ac Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 30 Aug 2022 14:30:38 +0200 Subject: [PATCH 030/103] Measuring: Added Measure gizmo imgui dialog + removed tech ENABLE_MEASURE_GIZMO_DEBUG + locking of features by pressing CTRL key --- src/libslic3r/Measure.cpp | 2 - src/libslic3r/Measure.hpp | 6 +- src/libslic3r/Technologies.hpp | 2 - src/slic3r/GUI/GLCanvas3D.hpp | 5 + src/slic3r/GUI/GUI_Utils.hpp | 12 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 365 ++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 19 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 4 + src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 58 ++-- 9 files changed, 311 insertions(+), 162 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 9cb46edfc1..cdde276dda 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -408,12 +408,10 @@ Measuring::Measuring(const indexed_triangle_set& its) Measuring::~Measuring() {} -#if ENABLE_MEASURE_GIZMO_DEBUG std::vector Measuring::get_all_features() const { return priv->get_all_features(); } -#endif // ENABLE_MEASURE_GIZMO_DEBUG std::optional Measuring::get_feature(size_t face_idx, const Vec3d& point) const diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 2f27a72598..95d8f34cba 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -59,6 +59,10 @@ public: return this->m_value == other.m_value; } + bool operator != (const SurfaceFeature& other) const { + return !operator == (other); + } + private: SurfaceFeatureType m_type = SurfaceFeatureType::Undef; Vec3d m_pt1; @@ -79,11 +83,9 @@ public: explicit Measuring(const indexed_triangle_set& its); ~Measuring(); -#if ENABLE_MEASURE_GIZMO_DEBUG // Return a reference to a list of all features identified on the its. // Use only for debugging. Expensive, do not call often. std::vector get_all_features() const; -#endif // ENABLE_MEASURE_GIZMO_DEBUG // Given a face_idx where the mouse cursor points, return a feature that // should be highlighted (if any). diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 4e2011b3b4..a5125fba95 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -80,8 +80,6 @@ #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) // Enable Measure Gizmo #define ENABLE_MEASURE_GIZMO (1 && ENABLE_RAYCAST_PICKING) -// Enable debug code for Measure Gizmo -#define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_MEASURE_GIZMO) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index b058386f56..effd15d5cb 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -19,6 +19,9 @@ #if ENABLE_RAYCAST_PICKING #include "SceneRaycaster.hpp" #endif // ENABLE_RAYCAST_PICKING +#if ENABLE_MEASURE_GIZMO +#include "GUI_Utils.hpp" +#endif // ENABLE_MEASURE_GIZMO #include "libslic3r/Slicing.hpp" @@ -135,6 +138,7 @@ private: wxTimer* m_timer; }; +#if !ENABLE_MEASURE_GIZMO class KeyAutoRepeatFilter { size_t m_count{ 0 }; @@ -144,6 +148,7 @@ public: void reset_count() { m_count = 0; } bool is_first() const { return m_count == 0; } }; +#endif // !ENABLE_MEASURE_GIZMO wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index d7d3529fee..e2152edd6d 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -416,6 +416,18 @@ public: ~TaskTimer(); }; +#if ENABLE_MEASURE_GIZMO +class KeyAutoRepeatFilter +{ + size_t m_count{ 0 }; + +public: + void increase_count() { ++m_count; } + void reset_count() { m_count = 0; } + bool is_first() const { return m_count == 0; } +}; +#endif // ENABLE_MEASURE_GIZMO + }} #endif diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index e132cf2ce1..2a856fb830 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -15,10 +15,26 @@ #if ENABLE_MEASURE_GIZMO +std::string surface_feature_type_as_string(Slic3r::Measure::SurfaceFeatureType type) +{ + switch (type) + { + default: + case Slic3r::Measure::SurfaceFeatureType::Undef: { return L("Undefined"); } + case Slic3r::Measure::SurfaceFeatureType::Point: { return L("Point"); } + case Slic3r::Measure::SurfaceFeatureType::Edge: { return L("Edge"); } + case Slic3r::Measure::SurfaceFeatureType::Circle: { return L("Circle"); } + case Slic3r::Measure::SurfaceFeatureType::Plane: { return L("Plane"); } + } +} + namespace Slic3r { namespace GUI { -static const Slic3r::ColorRGBA HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; +static const Slic3r::ColorRGBA BASIC_HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; +static const Slic3r::ColorRGBA EXTENDED_HOVER_COLOR = { 0.2f, 0.8f, 0.2f, 1.0f }; +static const Slic3r::ColorRGBA LOCK_COLOR = { 0.5f, 0.5f, 0.5f, 1.0f }; + static const int POINT_ID = 100; static const int EDGE_ID = 200; static const int CIRCLE_ID = 300; @@ -42,40 +58,39 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) m_mouse_pos_x = mouse_event.GetX(); m_mouse_pos_y = mouse_event.GetY(); - if (mouse_event.Moving()) { // only for sure m_mouse_left_down = false; return false; } - if (mouse_event.LeftDown()) { + else if (mouse_event.LeftDown()) { if (m_hover_id != -1) { - m_mouse_left_down = true; - + m_mouse_left_down = true; return true; } // fix: prevent restart gizmo when reselect object // take responsibility for left up - if (m_parent.get_first_hover_volume_idx() >= 0) m_mouse_left_down = true; + if (m_parent.get_first_hover_volume_idx() >= 0) + m_mouse_left_down = true; - } else if (mouse_event.LeftUp()) { + } + else if (mouse_event.LeftUp()) { if (m_mouse_left_down) { // responsible for mouse left up after selecting plane m_mouse_left_down = false; return true; } - } else if (mouse_event.Leaving()) { - m_mouse_left_down = false; } + else if (mouse_event.Leaving()) + m_mouse_left_down = false; + return false; } - - void GLGizmoMeasure::data_changed() { - const Selection & selection = m_parent.get_selection(); + const Selection& selection = m_parent.get_selection(); const ModelObject* model_object = nullptr; const ModelVolume* model_volume = nullptr; if (selection.is_single_full_instance() || @@ -87,29 +102,43 @@ void GLGizmoMeasure::data_changed() update_if_needed(); } - - -bool GLGizmoMeasure::on_init() +bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) { - // FIXME m_shortcut_key = WXK_CONTROL_F; + if (action == SLAGizmoEventType::CtrlDown) { + if (m_ctrl_kar_filter.is_first()) { + if (!m_features.empty()) + m_mode = EMode::ExtendedSelection; + } + + m_ctrl_kar_filter.increase_count(); + } + else if (action == SLAGizmoEventType::CtrlUp) { + m_ctrl_kar_filter.reset_count(); + m_mode = EMode::BasicSelection; + } + return true; } - +bool GLGizmoMeasure::on_init() +{ + m_shortcut_key = WXK_CONTROL_U; + return true; +} void GLGizmoMeasure::on_set_state() { + if (m_state == Off) + m_ctrl_kar_filter.reset_count(); + else + m_mode = EMode::BasicSelection; } - - CommonGizmosDataID GLGizmoMeasure::on_get_requirements() const { return CommonGizmosDataID(int(CommonGizmosDataID::SelectionInfo) | int(CommonGizmosDataID::Raycaster)); } - - std::string GLGizmoMeasure::on_get_name() const { return _u8L("Measure"); @@ -125,11 +154,9 @@ bool GLGizmoMeasure::on_is_activable() const void GLGizmoMeasure::on_render() { -#if !ENABLE_MEASURE_GIZMO_DEBUG // do not render if the user is panning/rotating the 3d scene if (m_parent.is_mouse_dragging()) return; -#endif // !ENABLE_MEASURE_GIZMO_DEBUG const Selection& selection = m_parent.get_selection(); @@ -146,107 +173,76 @@ void GLGizmoMeasure::on_render() size_t facet_idx; m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), model_matrix, camera, pos, normal, nullptr, &facet_idx); -#if ENABLE_MEASURE_GIZMO_DEBUG - m_imgui->begin(std::string("DEBUG")); - m_imgui->checkbox(wxString("Show all features"), m_show_all); - m_imgui->checkbox(wxString("Show all planes"), m_show_planes); - ImGui::Separator(); - m_imgui->text(std::string("face_idx: ") + std::to_string(facet_idx)); - m_imgui->text(std::string("pos_x: ") + std::to_string(pos.x())); - m_imgui->text(std::string("pos_y: ") + std::to_string(pos.y())); - m_imgui->text(std::string("pos_z: ") + std::to_string(pos.z())); - m_imgui->end(); -#endif // ENABLE_MEASURE_GIZMO_DEBUG - std::vector features; -#if ENABLE_MEASURE_GIZMO_DEBUG - if (m_show_all || m_show_planes) { - features = m_measuring->get_all_features(); - if (!m_show_planes) - features.erase(std::remove_if(features.begin(), features.end(), - [](const Measure::SurfaceFeature& f) { - return f.get_type() == Measure::SurfaceFeatureType::Plane; - }), features.end()); - if (!m_show_all) - features.erase(std::remove_if(features.begin(), features.end(), - [](const Measure::SurfaceFeature& f) { - return f.get_type() != Measure::SurfaceFeatureType::Plane; - }), features.end()); + if (m_mode == EMode::BasicSelection) { + std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); + if (feat.has_value()) + features.emplace_back(*feat); } - else { - if (!m_parent.is_mouse_dragging()) { -#endif // ENABLE_MEASURE_GIZMO_DEBUG - std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); - if (feat.has_value()) - features.emplace_back(*feat); -#if ENABLE_MEASURE_GIZMO_DEBUG - } - } -#endif // ENABLE_MEASURE_GIZMO_DEBUG - if (m_features != features) { - GLGizmoMeasure::on_unregister_raycasters_for_picking(); - m_features = features; - if (m_features.empty()) - return; + if (m_mode == EMode::BasicSelection) { + if (m_features != features) { + GLGizmoMeasure::on_unregister_raycasters_for_picking(); + m_features = features; + if (m_features.empty()) + return; -#if !ENABLE_MEASURE_GIZMO_DEBUG - for (const Measure::SurfaceFeature& feature : m_features) { - switch (feature.get_type()) { - case Measure::SurfaceFeatureType::Point: - { - m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Edge: - { - m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - const auto& [center, radius, n] = feature.get_circle(); - m_circle.reset(); - GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); - m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); - m_circle.model.init_from(std::move(circle_geometry)); - - m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); - m_raycasters.insert({ CIRCLE_CENTER_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_CENTER_ID, *m_sphere.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - const auto& [idx, normal, pt] = feature.get_plane(); - const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); - const std::vector& triangle_indices = planes_triangles[idx]; - - const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; - GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - unsigned int i = 0; - for (int idx : triangle_indices) { - const Vec3f& v0 = its.vertices[its.indices[idx][0]]; - const Vec3f& v1 = its.vertices[its.indices[idx][1]]; - const Vec3f& v2 = its.vertices[its.indices[idx][2]]; - - const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); - init_data.add_vertex(v0, n); - init_data.add_vertex(v1, n); - init_data.add_vertex(v2, n); - init_data.add_triangle(i, i + 1, i + 2); - i += 3; + for (const Measure::SurfaceFeature& feature : m_features) { + switch (feature.get_type()) { + case Measure::SurfaceFeatureType::Point: + { + m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); + break; } + case Measure::SurfaceFeatureType::Edge: + { + m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, n] = feature.get_circle(); + m_circle.reset(); + GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); + m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); + m_circle.model.init_from(std::move(circle_geometry)); - m_plane.reset(); - m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); - m_plane.model.init_from(std::move(init_data)); - m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); - break; - } + m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); + m_raycasters.insert({ CIRCLE_CENTER_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_CENTER_ID, *m_sphere.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto& [idx, normal, pt] = feature.get_plane(); + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + const std::vector& triangle_indices = planes_triangles[idx]; + + const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + unsigned int i = 0; + for (int idx : triangle_indices) { + const Vec3f& v0 = its.vertices[its.indices[idx][0]]; + const Vec3f& v1 = its.vertices[its.indices[idx][1]]; + const Vec3f& v2 = its.vertices[its.indices[idx][2]]; + + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; + } + + m_plane.reset(); + m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); + m_plane.model.init_from(std::move(init_data)); + m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); + break; + } + } } } -#endif // !ENABLE_MEASURE_GIZMO_DEBUG } GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); @@ -262,6 +258,13 @@ void GLGizmoMeasure::on_render() const Transform3d& view_matrix = camera.get_view_matrix(); + ColorRGBA color; + switch (m_mode) + { + case EMode::BasicSelection: { color = BASIC_HOVER_COLOR; break; } + case EMode::ExtendedSelection: { color = LOCK_COLOR; break; } + } + for (const Measure::SurfaceFeature& feature : m_features) { switch (feature.get_type()) { case Measure::SurfaceFeatureType::Point: @@ -272,7 +275,7 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_sphere.model.set_color(HOVER_COLOR); + m_sphere.model.set_color(color); m_sphere.model.render(); auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -288,7 +291,7 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", center_view_model_matrix); const Matrix3d center_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * center_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", center_view_normal_matrix); - m_sphere.model.set_color(HOVER_COLOR); + m_sphere.model.set_color(color); m_sphere.model.render(); auto it = m_raycasters.find(CIRCLE_CENTER_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -299,7 +302,7 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", circle_view_model_matrix); const Matrix3d circle_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * circle_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", circle_view_normal_matrix); - m_circle.model.set_color(HOVER_COLOR); + m_circle.model.set_color(color); m_circle.model.render(); it = m_raycasters.find(CIRCLE_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -316,7 +319,7 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_cylinder.model.set_color(HOVER_COLOR); + m_cylinder.model.set_color(color); m_cylinder.model.render(); auto it = m_raycasters.find(EDGE_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -331,7 +334,7 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_plane_models_cache[idx].set_color(HOVER_COLOR); + m_plane_models_cache[idx].set_color(color); m_plane_models_cache[idx].render(); auto it = m_raycasters.find(PLANE_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -344,8 +347,6 @@ void GLGizmoMeasure::on_render() } } - - void GLGizmoMeasure::update_if_needed() { auto update_plane_models_cache = [this](const indexed_triangle_set& its) { @@ -423,6 +424,124 @@ void GLGizmoMeasure::update_if_needed() } } +void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) +{ + static Measure::SurfaceFeature last_feature = Measure::SurfaceFeature(Measure::SurfaceFeatureType::Undef, Vec3d::Zero(), Vec3d::Zero(), std::nullopt, 0.0); + + static float last_y = 0.0f; + static float last_h = 0.0f; + + m_imgui->begin(_L("Measure tool"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + + // adjust window position to avoid overlap the view toolbar + const float win_h = ImGui::GetWindowHeight(); + y = std::min(y, bottom_limit - win_h); + ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always); + if (last_h != win_h || last_y != y) { + // ask canvas for another frame to render the window in the correct position + m_imgui->set_requires_extra_frame(); + if (last_h != win_h) + last_h = win_h; + if (last_y != y) + last_y = y; + } + + if (m_features.empty()) + m_imgui->text(_u8L("Select features to measure")); + + auto add_row_to_table = [this](const wxString& label, const std::string& value) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); + ImGui::TableSetColumnIndex(1); + m_imgui->text(value); + }; + + const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); + Measure::SurfaceFeature curr_feature = Measure::SurfaceFeature(Measure::SurfaceFeatureType::Undef, Vec3d::Zero(), Vec3d::Zero(), std::nullopt, 0.0); + for (size_t i = 0; i < m_features.size(); ++i) { + curr_feature = m_features[i]; + const Measure::SurfaceFeatureType type = curr_feature.get_type(); + if (type != Measure::SurfaceFeatureType::Undef) { + const std::string header = surface_feature_type_as_string(type) + std::string("##") + std::to_string(i); + if (ImGui::CollapsingHeader(header.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("Data", 2)) { + switch (type) + { + case Measure::SurfaceFeatureType::Point: + { + const Vec3d position = volume_matrix * curr_feature.get_point(); + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", position.x(), position.y(), position.z()); + add_row_to_table(_u8L("Position") + ":", std::string(buf)); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + auto [from, to] = curr_feature.get_edge(); + from = volume_matrix * from; + to = volume_matrix * to; + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", from.x(), from.y(), from.z()); + add_row_to_table(_u8L("From") + ":", std::string(buf)); + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", to.x(), to.y(), to.z()); + add_row_to_table(_u8L("To") + ":", std::string(buf)); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + auto [center, radius, normal] = curr_feature.get_circle(); + center = volume_matrix * center; + normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", center.x(), center.y(), center.z()); + add_row_to_table(_u8L("Center") + ":", std::string(buf)); + sprintf(buf, "%.3f", radius); + add_row_to_table(_u8L("Radius") + ":", std::string(buf)); + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", normal.x(), normal.y(), normal.z()); + add_row_to_table(_u8L("Normal") + ":", std::string(buf)); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + auto [idx, normal, origin] = curr_feature.get_plane(); + origin = volume_matrix * origin; + normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", origin.x(), origin.y(), origin.z()); + add_row_to_table(_u8L("Origin") + ":", std::string(buf)); + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", normal.x(), normal.y(), normal.z()); + add_row_to_table(_u8L("Normal") + ":", std::string(buf)); + break; + } + } + ImGui::EndTable(); + } + } + } + } + + if (last_feature != curr_feature) { + // the dialog may have changed its size, ask for an extra frame to render it properly + last_feature = curr_feature; + m_imgui->set_requires_extra_frame(); + } + + if (!m_features.empty()) { + static const std::string ctrl = +#ifdef __APPLE__ + "⌘" +#else + "Ctrl" +#endif //__APPLE__ + ; + + ImGui::Separator(); + m_imgui->text(_u8L("Press") + " " + ctrl + " " + _u8L("to enable extended selection")); + } + m_imgui->end(); +} + void GLGizmoMeasure::on_register_raycasters_for_picking() { // the features are rendered on top of the scene, so the raytraced picker should take it into account diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 9decf82fdc..13c46a48d9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -5,6 +5,7 @@ #include "GLGizmoBase.hpp" #include "slic3r/GUI/GLModel.hpp" +#include "slic3r/GUI/GUI_Utils.hpp" #include "libslic3r/Measure.hpp" namespace Slic3r { @@ -18,12 +19,19 @@ namespace Measure { class Measuring; } namespace GUI { +enum class SLAGizmoEventType : unsigned char; class GLGizmoMeasure : public GLGizmoBase { // This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself. -private: + enum class EMode : unsigned char + { + BasicSelection, + ExtendedSelection + }; + + EMode m_mode{ EMode::BasicSelection }; std::unique_ptr m_measuring; PickingModel m_sphere; @@ -49,10 +57,8 @@ private: int m_mouse_pos_x; int m_mouse_pos_y; -#if ENABLE_MEASURE_GIZMO_DEBUG - bool m_show_all = false; - bool m_show_planes = false; -#endif // ENABLE_MEASURE_GIZMO_DEBUG + + KeyAutoRepeatFilter m_ctrl_kar_filter; void update_if_needed(); @@ -68,6 +74,8 @@ public: void data_changed() override; + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + protected: bool on_init() override; std::string on_get_name() const override; @@ -76,6 +84,7 @@ protected: void on_set_state() override; CommonGizmosDataID on_get_requirements() const override; + virtual void on_render_input_window(float x, float y, float bottom_limit) override; virtual void on_register_raycasters_for_picking() override; virtual void on_unregister_raycasters_for_picking() override; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index c8b29f761e..c3aa1691f1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -24,6 +24,10 @@ enum class SLAGizmoEventType : unsigned char { Dragging, Delete, SelectAll, +#if ENABLE_MEASURE_GIZMO + CtrlDown, + CtrlUp, +#endif // ENABLE_MEASURE_GIZMO ShiftUp, AltUp, ApplyChanges, diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index ff24dd84a3..fbf0afe13a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -294,6 +294,10 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p return dynamic_cast(m_gizmos[Seam].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == MmuSegmentation) return dynamic_cast(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); +#if ENABLE_MEASURE_GIZMO + else if (m_current == Measure) + return dynamic_cast(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); +#endif // ENABLE_MEASURE_GIZMO else return false; } @@ -499,8 +503,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) bool processed = false; - if ((evt.GetModifiers() & ctrlMask) != 0) - { + if ((evt.GetModifiers() & ctrlMask) != 0) { switch (keyCode) { #ifdef __APPLE__ @@ -518,15 +521,13 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) } } } - else if (!evt.HasModifiers()) - { + else if (!evt.HasModifiers()) { switch (keyCode) { // key ESC case WXK_ESCAPE: { - if (m_current != Undefined) - { + if (m_current != Undefined) { if ((m_current != SlaSupports) || !gizmo_event(SLAGizmoEventType::DiscardChanges)) reset_all_states(); @@ -563,8 +564,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) case 'A': case 'a': { - if (m_current == SlaSupports) - { + if (m_current == SlaSupports) { gizmo_event(SLAGizmoEventType::AutomaticGeneration); // set as processed no matter what's returned by gizmo_event() to avoid the calling canvas to process 'A' as arrange processed = true; @@ -582,8 +582,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) case 'F': case 'f': { - if (m_current == Scale) - { + if (m_current == Scale) { if (!is_dragging()) wxGetApp().plater()->scale_selection_to_fit_print_volume(); @@ -595,8 +594,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) } } - if (!processed && !evt.HasModifiers()) - { + if (!processed && !evt.HasModifiers()) { if (handle_shortcut(keyCode)) processed = true; } @@ -612,10 +610,8 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) const int keyCode = evt.GetKeyCode(); bool processed = false; - if (evt.GetEventType() == wxEVT_KEY_UP) - { - if (m_current == SlaSupports || m_current == Hollow) - { + if (evt.GetEventType() == wxEVT_KEY_UP) { + if (m_current == SlaSupports || m_current == Hollow) { bool is_editing = true; bool is_rectangle_dragging = false; @@ -629,33 +625,33 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) is_rectangle_dragging = gizmo->is_selection_rectangle_dragging(); } - if (keyCode == WXK_SHIFT) - { + if (keyCode == WXK_SHIFT) { // shift has been just released - SLA gizmo might want to close rectangular selection. if (gizmo_event(SLAGizmoEventType::ShiftUp) || (is_editing && is_rectangle_dragging)) processed = true; } - else if (keyCode == WXK_ALT) - { + else if (keyCode == WXK_ALT) { // alt has been just released - SLA gizmo might want to close rectangular selection. if (gizmo_event(SLAGizmoEventType::AltUp) || (is_editing && is_rectangle_dragging)) processed = true; } } +#if ENABLE_MEASURE_GIZMO + else if (m_current == Measure && keyCode == WXK_CONTROL) { + gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), false); + } +#endif // ENABLE_MEASURE_GIZMO // if (processed) // m_parent.set_cursor(GLCanvas3D::Standard); } - else if (evt.GetEventType() == wxEVT_KEY_DOWN) - { - if ((m_current == SlaSupports) && ((keyCode == WXK_SHIFT) || (keyCode == WXK_ALT)) - && dynamic_cast(get_current())->is_in_editing_mode()) - { + else if (evt.GetEventType() == wxEVT_KEY_DOWN) { + if (m_current == SlaSupports && (keyCode == WXK_SHIFT || keyCode == WXK_ALT) + && dynamic_cast(get_current())->is_in_editing_mode()) { // m_parent.set_cursor(GLCanvas3D::Cross); processed = true; } - else if (m_current == Cut) - { + else if (m_current == Cut) { auto do_move = [this, &processed](double delta_z) { GLGizmoCut* cut = dynamic_cast(get_current()); cut->set_cut_z(delta_z + cut->get_cut_z()); @@ -668,11 +664,17 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) case WXK_NUMPAD_DOWN: case WXK_DOWN: { do_move(-1.0); break; } default: { break; } } - } else if (m_current == Simplify && keyCode == WXK_ESCAPE) { + } + else if (m_current == Simplify && keyCode == WXK_ESCAPE) { GLGizmoSimplify *simplify = dynamic_cast(get_current()); if (simplify != nullptr) processed = simplify->on_esc_key_down(); } +#if ENABLE_MEASURE_GIZMO + else if (m_current == Measure && keyCode == WXK_CONTROL) { + gizmo_event(SLAGizmoEventType::CtrlDown, Vec2d::Zero(), true); + } +#endif // ENABLE_MEASURE_GIZMO } if (processed) From db1b2fbfc111f98ae2f9f380e53cead6b7b5b433 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 30 Aug 2022 15:38:29 +0200 Subject: [PATCH 031/103] Refactoring of GLGizmoMeasure to simplify code --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 329 ++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 7 +- 2 files changed, 167 insertions(+), 169 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 2a856fb830..2c040c9c3b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -55,8 +55,7 @@ GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filen bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) { - m_mouse_pos_x = mouse_event.GetX(); - m_mouse_pos_y = mouse_event.GetY(); + m_mouse_pos = { double(mouse_event.GetX()), double(mouse_event.GetY()) }; if (mouse_event.Moving()) { // only for sure @@ -106,7 +105,7 @@ bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_po { if (action == SLAGizmoEventType::CtrlDown) { if (m_ctrl_kar_filter.is_first()) { - if (!m_features.empty()) + if (m_curr_feature.has_value()) m_mode = EMode::ExtendedSelection; } @@ -171,80 +170,84 @@ void GLGizmoMeasure::on_render() Vec3f pos; Vec3f normal; size_t facet_idx; - m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), model_matrix, camera, pos, normal, nullptr, &facet_idx); + m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, pos, normal, nullptr, &facet_idx); - std::vector features; - if (m_mode == EMode::BasicSelection) { - std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); - if (feat.has_value()) - features.emplace_back(*feat); - } + std::optional curr_feature; + if (m_mode == EMode::BasicSelection) + curr_feature = m_measuring->get_feature(facet_idx, pos.cast()); if (m_mode == EMode::BasicSelection) { - if (m_features != features) { + if (m_curr_feature != curr_feature) { GLGizmoMeasure::on_unregister_raycasters_for_picking(); - m_features = features; - if (m_features.empty()) + m_curr_feature = curr_feature; + if (!m_curr_feature.has_value()) return; - for (const Measure::SurfaceFeature& feature : m_features) { - switch (feature.get_type()) { - case Measure::SurfaceFeatureType::Point: - { - m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Edge: - { - m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - const auto& [center, radius, n] = feature.get_circle(); - m_circle.reset(); - GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); - m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); - m_circle.model.init_from(std::move(circle_geometry)); + switch (m_curr_feature->get_type()) { + case Measure::SurfaceFeatureType::Point: + { + m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, n] = m_curr_feature->get_circle(); - m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); - m_raycasters.insert({ CIRCLE_CENTER_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_CENTER_ID, *m_sphere.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - const auto& [idx, normal, pt] = feature.get_plane(); - const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); - const std::vector& triangle_indices = planes_triangles[idx]; + // TODO: check for changed inv_zoom - const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; - GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - unsigned int i = 0; - for (int idx : triangle_indices) { - const Vec3f& v0 = its.vertices[its.indices[idx][0]]; - const Vec3f& v1 = its.vertices[its.indices[idx][1]]; - const Vec3f& v2 = its.vertices[its.indices[idx][2]]; + m_circle.reset(); + GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); + m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); + m_circle.model.init_from(std::move(circle_geometry)); - const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); - init_data.add_vertex(v0, n); - init_data.add_vertex(v1, n); - init_data.add_vertex(v2, n); - init_data.add_triangle(i, i + 1, i + 2); - i += 3; - } + m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); + m_raycasters.insert({ CIRCLE_CENTER_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_CENTER_ID, *m_sphere.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto& [idx, normal, pt] = m_curr_feature->get_plane(); - m_plane.reset(); - m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); - m_plane.model.init_from(std::move(init_data)); - m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); - break; - } + // TODO: check for changed idx + + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + const std::vector& triangle_indices = planes_triangles[idx]; + + const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + unsigned int i = 0; + for (int idx : triangle_indices) { + const Vec3f& v0 = its.vertices[its.indices[idx][0]]; + const Vec3f& v1 = its.vertices[its.indices[idx][1]]; + const Vec3f& v2 = its.vertices[its.indices[idx][2]]; + + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; } + + m_plane.reset(); + m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); + m_plane.model.init_from(std::move(init_data)); + m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); + break; + } } } } + if (!m_curr_feature.has_value()) + return; + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; @@ -265,84 +268,84 @@ void GLGizmoMeasure::on_render() case EMode::ExtendedSelection: { color = LOCK_COLOR; break; } } - for (const Measure::SurfaceFeature& feature : m_features) { - switch (feature.get_type()) { - case Measure::SurfaceFeatureType::Point: - { - const Vec3d& position = feature.get_point(); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_sphere.model.set_color(color); - m_sphere.model.render(); - auto it = m_raycasters.find(POINT_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - const auto& [center, radius, n] = feature.get_circle(); - // render center - const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); - const Transform3d center_view_model_matrix = view_matrix * center_matrix; - shader->set_uniform("view_model_matrix", center_view_model_matrix); - const Matrix3d center_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * center_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", center_view_normal_matrix); - m_sphere.model.set_color(color); - m_sphere.model.render(); - auto it = m_raycasters.find(CIRCLE_CENTER_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(center_matrix); - - const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); - const Transform3d circle_view_model_matrix = view_matrix * circle_matrix; - shader->set_uniform("view_model_matrix", circle_view_model_matrix); - const Matrix3d circle_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * circle_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", circle_view_normal_matrix); - m_circle.model.set_color(color); - m_circle.model.render(); - it = m_raycasters.find(CIRCLE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(circle_matrix); - break; - } - case Measure::SurfaceFeatureType::Edge: - { - const auto& [start, end] = feature.get_edge(); - auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * - Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_cylinder.model.set_color(color); - m_cylinder.model.render(); - auto it = m_raycasters.find(EDGE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - const auto& [idx, normal, pt] = feature.get_plane(); - assert(idx < m_plane_models_cache.size()); - const Transform3d view_model_matrix = view_matrix * model_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_plane_models_cache[idx].set_color(color); - m_plane_models_cache[idx].render(); - auto it = m_raycasters.find(PLANE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(model_matrix); - break; - } - } + switch (m_curr_feature->get_type()) { + case Measure::SurfaceFeatureType::Point: + { + const Vec3d& position = m_curr_feature->get_point(); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + m_sphere.model.set_color(color); + m_sphere.model.render(); + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); + break; } + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, n] = m_curr_feature->get_circle(); + // render center + const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); + const Transform3d center_view_model_matrix = view_matrix * center_matrix; + shader->set_uniform("view_model_matrix", center_view_model_matrix); + const Matrix3d center_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * center_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", center_view_normal_matrix); + m_sphere.model.set_color(color); + m_sphere.model.render(); + auto it = m_raycasters.find(CIRCLE_CENTER_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(center_matrix); + + // render circle + const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); + const Transform3d circle_view_model_matrix = view_matrix * circle_matrix; + shader->set_uniform("view_model_matrix", circle_view_model_matrix); + const Matrix3d circle_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * circle_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", circle_view_normal_matrix); + m_circle.model.set_color(color); + m_circle.model.render(); + it = m_raycasters.find(CIRCLE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(circle_matrix); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + const auto& [start, end] = m_curr_feature->get_edge(); + auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * + Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + m_cylinder.model.set_color(color); + m_cylinder.model.render(); + auto it = m_raycasters.find(EDGE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto& [idx, normal, pt] = m_curr_feature->get_plane(); + assert(idx < m_plane_models_cache.size()); + const Transform3d view_model_matrix = view_matrix * model_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + m_plane_models_cache[idx].set_color(color); + m_plane_models_cache[idx].render(); + auto it = m_raycasters.find(PLANE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(model_matrix); + break; + } + } + shader->stop_using(); } } @@ -426,7 +429,8 @@ void GLGizmoMeasure::update_if_needed() void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) { - static Measure::SurfaceFeature last_feature = Measure::SurfaceFeature(Measure::SurfaceFeatureType::Undef, Vec3d::Zero(), Vec3d::Zero(), std::nullopt, 0.0); + static std::optional last_feature; + static EMode last_mode = EMode::BasicSelection; static float last_y = 0.0f; static float last_h = 0.0f; @@ -446,31 +450,27 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit last_y = y; } - if (m_features.empty()) + if (!m_curr_feature.has_value()) m_imgui->text(_u8L("Select features to measure")); + else { + auto add_row_to_table = [this](const wxString& label, const std::string& value) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); + ImGui::TableSetColumnIndex(1); + m_imgui->text(value); + }; - auto add_row_to_table = [this](const wxString& label, const std::string& value) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); - ImGui::TableSetColumnIndex(1); - m_imgui->text(value); - }; - - const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); - Measure::SurfaceFeature curr_feature = Measure::SurfaceFeature(Measure::SurfaceFeatureType::Undef, Vec3d::Zero(), Vec3d::Zero(), std::nullopt, 0.0); - for (size_t i = 0; i < m_features.size(); ++i) { - curr_feature = m_features[i]; - const Measure::SurfaceFeatureType type = curr_feature.get_type(); + const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); + const Measure::SurfaceFeatureType type = m_curr_feature->get_type(); if (type != Measure::SurfaceFeatureType::Undef) { - const std::string header = surface_feature_type_as_string(type) + std::string("##") + std::to_string(i); - if (ImGui::CollapsingHeader(header.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::CollapsingHeader(surface_feature_type_as_string(type).c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { if (ImGui::BeginTable("Data", 2)) { switch (type) { case Measure::SurfaceFeatureType::Point: { - const Vec3d position = volume_matrix * curr_feature.get_point(); + const Vec3d position = volume_matrix * m_curr_feature->get_point(); char buf[1024]; sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", position.x(), position.y(), position.z()); add_row_to_table(_u8L("Position") + ":", std::string(buf)); @@ -478,7 +478,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } case Measure::SurfaceFeatureType::Edge: { - auto [from, to] = curr_feature.get_edge(); + auto [from, to] = m_curr_feature->get_edge(); from = volume_matrix * from; to = volume_matrix * to; char buf[1024]; @@ -490,7 +490,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } case Measure::SurfaceFeatureType::Circle: { - auto [center, radius, normal] = curr_feature.get_circle(); + auto [center, radius, normal] = m_curr_feature->get_circle(); center = volume_matrix * center; normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; char buf[1024]; @@ -504,7 +504,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } case Measure::SurfaceFeatureType::Plane: { - auto [idx, normal, origin] = curr_feature.get_plane(); + auto [idx, normal, origin] = m_curr_feature->get_plane(); origin = volume_matrix * origin; normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; char buf[1024]; @@ -521,14 +521,15 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } } - if (last_feature != curr_feature) { + if (last_feature != m_curr_feature || last_mode != m_mode) { // the dialog may have changed its size, ask for an extra frame to render it properly - last_feature = curr_feature; + last_feature = m_curr_feature; + last_mode = m_mode; m_imgui->set_requires_extra_frame(); } - if (!m_features.empty()) { - static const std::string ctrl = + if (m_curr_feature.has_value() && m_mode == EMode::BasicSelection) { + static const std::string ctrl = #ifdef __APPLE__ "⌘" #else diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 13c46a48d9..093584c7f4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -41,7 +41,7 @@ class GLGizmoMeasure : public GLGizmoBase std::vector m_plane_models_cache; std::map> m_raycasters; - std::vector m_features; + std::optional m_curr_feature; // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; @@ -50,13 +50,10 @@ class GLGizmoMeasure : public GLGizmoBase Vec3d m_first_instance_mirror{ Vec3d::Ones() }; bool m_mouse_left_down = false; // for detection left_up of this gizmo - bool m_planes_valid = false; const ModelObject* m_old_model_object = nullptr; const ModelVolume* m_old_model_volume = nullptr; - std::vector instances_matrices; - int m_mouse_pos_x; - int m_mouse_pos_y; + Vec2d m_mouse_pos{ Vec2d::Zero() }; KeyAutoRepeatFilter m_ctrl_kar_filter; From 6694817cd6a893c5ce34f6628a36cdeb5c71119b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 30 Aug 2022 15:50:11 +0200 Subject: [PATCH 032/103] Refactoring of GLGizmoMeasure::on_render_input_window to simplify code --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 38 +++++++++++------------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 6 ++-- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 2c040c9c3b..3e803fa603 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -460,6 +460,16 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::TableSetColumnIndex(1); m_imgui->text(value); }; + auto format_double = [](double value) { + char buf[1024]; + sprintf(buf, "%.3f", value); + return std::string(buf); + }; + auto format_vec3 = [](const Vec3d& v) { + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); + return std::string(buf); + }; const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); const Measure::SurfaceFeatureType type = m_curr_feature->get_type(); @@ -471,9 +481,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit case Measure::SurfaceFeatureType::Point: { const Vec3d position = volume_matrix * m_curr_feature->get_point(); - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", position.x(), position.y(), position.z()); - add_row_to_table(_u8L("Position") + ":", std::string(buf)); + add_row_to_table(_u8L("Position") + ":", format_vec3(position)); break; } case Measure::SurfaceFeatureType::Edge: @@ -481,11 +489,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit auto [from, to] = m_curr_feature->get_edge(); from = volume_matrix * from; to = volume_matrix * to; - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", from.x(), from.y(), from.z()); - add_row_to_table(_u8L("From") + ":", std::string(buf)); - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", to.x(), to.y(), to.z()); - add_row_to_table(_u8L("To") + ":", std::string(buf)); + add_row_to_table(_u8L("From") + ":", format_vec3(from)); + add_row_to_table(_u8L("To") + ":", format_vec3(to)); break; } case Measure::SurfaceFeatureType::Circle: @@ -493,13 +498,9 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit auto [center, radius, normal] = m_curr_feature->get_circle(); center = volume_matrix * center; normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", center.x(), center.y(), center.z()); - add_row_to_table(_u8L("Center") + ":", std::string(buf)); - sprintf(buf, "%.3f", radius); - add_row_to_table(_u8L("Radius") + ":", std::string(buf)); - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", normal.x(), normal.y(), normal.z()); - add_row_to_table(_u8L("Normal") + ":", std::string(buf)); + add_row_to_table(_u8L("Center") + ":", format_vec3(center)); + add_row_to_table(_u8L("Radius") + ":", format_double(radius)); + add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); break; } case Measure::SurfaceFeatureType::Plane: @@ -507,11 +508,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit auto [idx, normal, origin] = m_curr_feature->get_plane(); origin = volume_matrix * origin; normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", origin.x(), origin.y(), origin.z()); - add_row_to_table(_u8L("Origin") + ":", std::string(buf)); - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", normal.x(), normal.y(), normal.z()); - add_row_to_table(_u8L("Normal") + ":", std::string(buf)); + add_row_to_table(_u8L("Origin") + ":", format_vec3(origin)); + add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); break; } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 093584c7f4..8a8cdac755 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -49,9 +49,9 @@ class GLGizmoMeasure : public GLGizmoBase Vec3d m_first_instance_scale{ Vec3d::Ones() }; Vec3d m_first_instance_mirror{ Vec3d::Ones() }; - bool m_mouse_left_down = false; // for detection left_up of this gizmo - const ModelObject* m_old_model_object = nullptr; - const ModelVolume* m_old_model_volume = nullptr; + bool m_mouse_left_down{ false }; // for detection left_up of this gizmo + const ModelObject* m_old_model_object{ nullptr }; + const ModelVolume* m_old_model_volume{ nullptr }; Vec2d m_mouse_pos{ Vec2d::Zero() }; From 83990562c5089248427056685b976c92e4c20fab Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 31 Aug 2022 12:42:55 +0200 Subject: [PATCH 033/103] Measuring: Measure gizmo - added visualization of point for extended selection and updates to imgui dialog --- src/slic3r/GUI/GLCanvas3D.cpp | 9 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 255 +++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + 3 files changed, 151 insertions(+), 114 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 9c98a20e7a..9adef7b472 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5465,8 +5465,13 @@ void GLCanvas3D::_picking_pass() // do not add the volume id if any gizmo is active and CTRL is pressed if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined || !wxGetKeyState(WXK_CONTROL)) { m_hover_volume_idxs.emplace_back(hit.raycaster_id); +#if !ENABLE_MEASURE_GIZMO m_gizmos.set_hover_id(-1); +#endif // !ENABLE_MEASURE_GIZMO } +#if ENABLE_MEASURE_GIZMO + m_gizmos.set_hover_id(-1); +#endif // ENABLE_MEASURE_GIZMO } } else @@ -5477,8 +5482,8 @@ void GLCanvas3D::_picking_pass() case SceneRaycaster::EType::Gizmo: { const Size& cnv_size = get_canvas_size(); - bool inside = 0 <= m_mouse.position.x() && m_mouse.position.x() < cnv_size.get_width() && - 0 <= m_mouse.position.y() && m_mouse.position.y() < cnv_size.get_height(); + const bool inside = 0 <= m_mouse.position.x() && m_mouse.position.x() < cnv_size.get_width() && + 0 <= m_mouse.position.y() && m_mouse.position.y() < cnv_size.get_height(); m_gizmos.set_hover_id(inside ? hit.raycaster_id : -1); break; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 3e803fa603..932f9113f1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -21,7 +21,7 @@ std::string surface_feature_type_as_string(Slic3r::Measure::SurfaceFeatureType t { default: case Slic3r::Measure::SurfaceFeatureType::Undef: { return L("Undefined"); } - case Slic3r::Measure::SurfaceFeatureType::Point: { return L("Point"); } + case Slic3r::Measure::SurfaceFeatureType::Point: { return L("Vertex"); } case Slic3r::Measure::SurfaceFeatureType::Edge: { return L("Edge"); } case Slic3r::Measure::SurfaceFeatureType::Circle: { return L("Circle"); } case Slic3r::Measure::SurfaceFeatureType::Plane: { return L("Plane"); } @@ -32,15 +32,22 @@ namespace Slic3r { namespace GUI { static const Slic3r::ColorRGBA BASIC_HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; -static const Slic3r::ColorRGBA EXTENDED_HOVER_COLOR = { 0.2f, 0.8f, 0.2f, 1.0f }; +static const Slic3r::ColorRGBA EXTENDED_HOVER_COLOR = { 0.8f, 0.8f, 0.2f, 1.0f }; static const Slic3r::ColorRGBA LOCK_COLOR = { 0.5f, 0.5f, 0.5f, 1.0f }; static const int POINT_ID = 100; static const int EDGE_ID = 200; static const int CIRCLE_ID = 300; -static const int CIRCLE_CENTER_ID = 301; static const int PLANE_ID = 400; +static const std::string CTRL_STR = +#ifdef __APPLE__ +"⌘" +#else +"Ctrl" +#endif //__APPLE__ +; + GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { @@ -127,8 +134,11 @@ bool GLGizmoMeasure::on_init() void GLGizmoMeasure::on_set_state() { - if (m_state == Off) + if (m_state == Off) { m_ctrl_kar_filter.reset_count(); + m_curr_feature.reset(); + m_curr_ex_feature_position.reset(); + } else m_mode = EMode::BasicSelection; } @@ -167,16 +177,19 @@ void GLGizmoMeasure::on_render() const Camera& camera = wxGetApp().plater()->get_camera(); const float inv_zoom = (float)camera.get_inv_zoom(); - Vec3f pos; - Vec3f normal; + Vec3f position_on_model; + Vec3f normal_on_model; size_t facet_idx; - m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, pos, normal, nullptr, &facet_idx); + m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, position_on_model, normal_on_model, nullptr, &facet_idx); + + const bool is_hovering_on_extended_selection = m_mode == EMode::ExtendedSelection && m_hover_id != -1; std::optional curr_feature; if (m_mode == EMode::BasicSelection) - curr_feature = m_measuring->get_feature(facet_idx, pos.cast()); + curr_feature = m_measuring->get_feature(facet_idx, position_on_model.cast()); if (m_mode == EMode::BasicSelection) { + m_curr_ex_feature_position.reset(); if (m_curr_feature != curr_feature) { GLGizmoMeasure::on_unregister_raycasters_for_picking(); m_curr_feature = curr_feature; @@ -196,7 +209,7 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Circle: { - const auto& [center, radius, n] = m_curr_feature->get_circle(); + const auto& [center, radius, normal] = m_curr_feature->get_circle(); // TODO: check for changed inv_zoom @@ -206,12 +219,12 @@ void GLGizmoMeasure::on_render() m_circle.model.init_from(std::move(circle_geometry)); m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); - m_raycasters.insert({ CIRCLE_CENTER_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_CENTER_ID, *m_sphere.mesh_raycaster) }); + m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); break; } case Measure::SurfaceFeatureType::Plane: { - const auto& [idx, normal, pt] = m_curr_feature->get_plane(); + const auto& [idx, normal, point] = m_curr_feature->get_plane(); // TODO: check for changed idx @@ -244,6 +257,8 @@ void GLGizmoMeasure::on_render() } } } + else if (m_mode == EMode::ExtendedSelection) + m_curr_ex_feature_position = position_on_model.cast(); if (!m_curr_feature.has_value()) return; @@ -261,23 +276,27 @@ void GLGizmoMeasure::on_render() const Transform3d& view_matrix = camera.get_view_matrix(); - ColorRGBA color; + ColorRGBA feature_color; switch (m_mode) { - case EMode::BasicSelection: { color = BASIC_HOVER_COLOR; break; } - case EMode::ExtendedSelection: { color = LOCK_COLOR; break; } + case EMode::BasicSelection: { feature_color = BASIC_HOVER_COLOR; break; } + case EMode::ExtendedSelection: { feature_color = LOCK_COLOR; break; } } + auto set_matrix_uniforms = [shader, &view_matrix](const Transform3d& model_matrix) { + const Transform3d view_model_matrix = view_matrix * model_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + }; + switch (m_curr_feature->get_type()) { case Measure::SurfaceFeatureType::Point: { const Vec3d& position = m_curr_feature->get_point(); const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_sphere.model.set_color(color); + set_matrix_uniforms(feature_matrix); + m_sphere.model.set_color(is_hovering_on_extended_selection ? EXTENDED_HOVER_COLOR : feature_color); m_sphere.model.render(); auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -289,23 +308,17 @@ void GLGizmoMeasure::on_render() const auto& [center, radius, n] = m_curr_feature->get_circle(); // render center const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); - const Transform3d center_view_model_matrix = view_matrix * center_matrix; - shader->set_uniform("view_model_matrix", center_view_model_matrix); - const Matrix3d center_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * center_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", center_view_normal_matrix); - m_sphere.model.set_color(color); + set_matrix_uniforms(center_matrix); + m_sphere.model.set_color((is_hovering_on_extended_selection && m_hover_id == POINT_ID) ? EXTENDED_HOVER_COLOR : feature_color); m_sphere.model.render(); - auto it = m_raycasters.find(CIRCLE_CENTER_ID); + auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(center_matrix); // render circle const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); - const Transform3d circle_view_model_matrix = view_matrix * circle_matrix; - shader->set_uniform("view_model_matrix", circle_view_model_matrix); - const Matrix3d circle_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * circle_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", circle_view_normal_matrix); - m_circle.model.set_color(color); + set_matrix_uniforms(circle_matrix); + m_circle.model.set_color(feature_color); m_circle.model.render(); it = m_raycasters.find(CIRCLE_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -318,11 +331,8 @@ void GLGizmoMeasure::on_render() auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_cylinder.model.set_color(color); + set_matrix_uniforms(feature_matrix); + m_cylinder.model.set_color(feature_color); m_cylinder.model.render(); auto it = m_raycasters.find(EDGE_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -333,11 +343,8 @@ void GLGizmoMeasure::on_render() { const auto& [idx, normal, pt] = m_curr_feature->get_plane(); assert(idx < m_plane_models_cache.size()); - const Transform3d view_model_matrix = view_matrix * model_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_plane_models_cache[idx].set_color(color); + set_matrix_uniforms(model_matrix); + m_plane_models_cache[idx].set_color(feature_color); m_plane_models_cache[idx].render(); auto it = m_raycasters.find(PLANE_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -346,6 +353,15 @@ void GLGizmoMeasure::on_render() } } + if (is_hovering_on_extended_selection && m_curr_ex_feature_position.has_value()) { + if (m_hover_id != POINT_ID) { + Transform3d matrix = model_matrix * Geometry::translation_transform(*m_curr_ex_feature_position) * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(matrix); + m_sphere.model.set_color(EXTENDED_HOVER_COLOR); + m_sphere.model.render(); + } + } + shader->stop_using(); } } @@ -450,70 +466,97 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit last_y = y; } - if (!m_curr_feature.has_value()) - m_imgui->text(_u8L("Select features to measure")); - else { - auto add_row_to_table = [this](const wxString& label, const std::string& value) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); - ImGui::TableSetColumnIndex(1); - m_imgui->text(value); - }; - auto format_double = [](double value) { - char buf[1024]; - sprintf(buf, "%.3f", value); - return std::string(buf); - }; - auto format_vec3 = [](const Vec3d& v) { - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); - return std::string(buf); - }; + auto add_row_to_table = [this](const wxString& label, const std::string& value) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); + ImGui::TableSetColumnIndex(1); + m_imgui->text(value); + }; + auto format_double = [](double value) { + char buf[1024]; + sprintf(buf, "%.3f", value); + return std::string(buf); + }; + auto format_vec3 = [](const Vec3d& v) { + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); + return std::string(buf); + }; + if (ImGui::BeginTable("Commands", 2)) { + add_row_to_table(_u8L("Left mouse button"), (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point")); + if (m_mode == EMode::BasicSelection && m_hover_id != -1) + add_row_to_table(CTRL_STR, _u8L("Enable point selection")); + ImGui::EndTable(); + } + + if (m_curr_feature.has_value()) { const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); - const Measure::SurfaceFeatureType type = m_curr_feature->get_type(); - if (type != Measure::SurfaceFeatureType::Undef) { - if (ImGui::CollapsingHeader(surface_feature_type_as_string(type).c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { - if (ImGui::BeginTable("Data", 2)) { - switch (type) - { - case Measure::SurfaceFeatureType::Point: - { - const Vec3d position = volume_matrix * m_curr_feature->get_point(); + const Measure::SurfaceFeatureType feature_type = m_curr_feature->get_type(); + if (m_mode == EMode::BasicSelection) { + if (feature_type != Measure::SurfaceFeatureType::Undef) { + if (ImGui::CollapsingHeader(surface_feature_type_as_string(feature_type).c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("Data", 2)) { + switch (feature_type) + { + case Measure::SurfaceFeatureType::Point: + { + const Vec3d position = volume_matrix * m_curr_feature->get_point(); + add_row_to_table(_u8L("Position") + ":", format_vec3(position)); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + auto [from, to] = m_curr_feature->get_edge(); + from = volume_matrix * from; + to = volume_matrix * to; + add_row_to_table(_u8L("From") + ":", format_vec3(from)); + add_row_to_table(_u8L("To") + ":", format_vec3(to)); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + auto [center, radius, normal] = m_curr_feature->get_circle(); + center = volume_matrix * center; + normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + add_row_to_table(_u8L("Center") + ":", format_vec3(center)); + add_row_to_table(_u8L("Radius") + ":", format_double(radius)); + add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + auto [idx, normal, origin] = m_curr_feature->get_plane(); + origin = volume_matrix * origin; + normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + add_row_to_table(_u8L("Origin") + ":", format_vec3(origin)); + add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); + break; + } + } + ImGui::EndTable(); + } + } + } + } + else if (m_mode == EMode::ExtendedSelection) { + if (m_hover_id != -1) { + std::string header; + switch (feature_type) { + case Measure::SurfaceFeatureType::Point: { header = _u8L("Vertex"); break; } + case Measure::SurfaceFeatureType::Edge: { header = _u8L("Point on edge"); break; } + case Measure::SurfaceFeatureType::Circle: { header = (m_hover_id == POINT_ID) ? _u8L("Center of circle") : _u8L("Point on circle"); break; } + case Measure::SurfaceFeatureType::Plane: { header = _u8L("Point on plane"); break; } + default: { assert(false); break; } + } + + if (ImGui::CollapsingHeader(header.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { + const Vec3d position = volume_matrix * *m_curr_ex_feature_position; + if (ImGui::BeginTable("Data", 2)) { add_row_to_table(_u8L("Position") + ":", format_vec3(position)); - break; + ImGui::EndTable(); } - case Measure::SurfaceFeatureType::Edge: - { - auto [from, to] = m_curr_feature->get_edge(); - from = volume_matrix * from; - to = volume_matrix * to; - add_row_to_table(_u8L("From") + ":", format_vec3(from)); - add_row_to_table(_u8L("To") + ":", format_vec3(to)); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - auto [center, radius, normal] = m_curr_feature->get_circle(); - center = volume_matrix * center; - normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - add_row_to_table(_u8L("Center") + ":", format_vec3(center)); - add_row_to_table(_u8L("Radius") + ":", format_double(radius)); - add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - auto [idx, normal, origin] = m_curr_feature->get_plane(); - origin = volume_matrix * origin; - normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - add_row_to_table(_u8L("Origin") + ":", format_vec3(origin)); - add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); - break; - } - } - ImGui::EndTable(); } } } @@ -526,18 +569,6 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit m_imgui->set_requires_extra_frame(); } - if (m_curr_feature.has_value() && m_mode == EMode::BasicSelection) { - static const std::string ctrl = -#ifdef __APPLE__ - "⌘" -#else - "Ctrl" -#endif //__APPLE__ - ; - - ImGui::Separator(); - m_imgui->text(_u8L("Press") + " " + ctrl + " " + _u8L("to enable extended selection")); - } m_imgui->end(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 8a8cdac755..09d40b47ba 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -42,6 +42,7 @@ class GLGizmoMeasure : public GLGizmoBase std::vector m_plane_models_cache; std::map> m_raycasters; std::optional m_curr_feature; + std::optional m_curr_ex_feature_position; // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; From 7cf85a1565c790e885d886921d6cd165fb7bb45a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 31 Aug 2022 14:56:00 +0200 Subject: [PATCH 034/103] Measuring: Measure gizmo - Improved visualization of points for extended selection --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 53 +++++++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 932f9113f1..89ba8cd410 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -257,8 +257,50 @@ void GLGizmoMeasure::on_render() } } } - else if (m_mode == EMode::ExtendedSelection) - m_curr_ex_feature_position = position_on_model.cast(); + else if (is_hovering_on_extended_selection) { + switch (m_curr_feature->get_type()) + { + case Measure::SurfaceFeatureType::Point: + { + m_curr_ex_feature_position = model_matrix * m_curr_feature->get_point(); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + auto it = m_raycasters.find(EDGE_ID); + if (it != m_raycasters.end() && it->second != nullptr) { + Vec3f p; + Vec3f n; + const Transform3d& trafo = it->second->get_transform(); + it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); + m_curr_ex_feature_position = trafo * p.cast(); + } + break; + } + case Measure::SurfaceFeatureType::Plane: + { + m_curr_ex_feature_position = model_matrix * position_on_model.cast(); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, normal] = m_curr_feature->get_circle(); + if (m_hover_id == POINT_ID) + m_curr_ex_feature_position = model_matrix * center; + else { + auto it = m_raycasters.find(CIRCLE_ID); + if (it != m_raycasters.end() && it->second != nullptr) { + Vec3f p; + Vec3f n; + const Transform3d& trafo = it->second->get_transform(); + it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); + m_curr_ex_feature_position = trafo * p.cast(); + } + } + break; + } + } + } if (!m_curr_feature.has_value()) return; @@ -355,7 +397,7 @@ void GLGizmoMeasure::on_render() if (is_hovering_on_extended_selection && m_curr_ex_feature_position.has_value()) { if (m_hover_id != POINT_ID) { - Transform3d matrix = model_matrix * Geometry::translation_transform(*m_curr_ex_feature_position) * Geometry::scale_transform(inv_zoom); + const Transform3d matrix = Geometry::translation_transform(*m_curr_ex_feature_position) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); m_sphere.model.set_color(EXTENDED_HOVER_COLOR); m_sphere.model.render(); @@ -541,7 +583,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } } else if (m_mode == EMode::ExtendedSelection) { - if (m_hover_id != -1) { + if (m_hover_id != -1 && m_curr_ex_feature_position.has_value()) { std::string header; switch (feature_type) { case Measure::SurfaceFeatureType::Point: { header = _u8L("Vertex"); break; } @@ -552,9 +594,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } if (ImGui::CollapsingHeader(header.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { - const Vec3d position = volume_matrix * *m_curr_ex_feature_position; if (ImGui::BeginTable("Data", 2)) { - add_row_to_table(_u8L("Position") + ":", format_vec3(position)); + add_row_to_table(_u8L("Position") + ":", format_vec3(*m_curr_ex_feature_position)); ImGui::EndTable(); } } From 58da6e994cd4c9eea2750760681f33c86c9279ea Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 31 Aug 2022 15:31:39 +0200 Subject: [PATCH 035/103] Measuring: Measure gizmo - Further improvements in visualization of points for extended selection --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 89ba8cd410..a9c2e18257 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -273,6 +273,7 @@ void GLGizmoMeasure::on_render() Vec3f n; const Transform3d& trafo = it->second->get_transform(); it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); + p = { 0.0f, 0.0f, p.z() }; m_curr_ex_feature_position = trafo * p.cast(); } break; @@ -294,6 +295,10 @@ void GLGizmoMeasure::on_render() Vec3f n; const Transform3d& trafo = it->second->get_transform(); it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); + float angle = std::atan2(p.y(), p.x()); + if (angle < 0.0f) + angle += 2.0f * float(M_PI); + p = float(radius) * Vec3f(std::cos(angle), std::sin(angle), 0.0f); m_curr_ex_feature_position = trafo * p.cast(); } } From ec9001c57e9eb4f0847ca0c58f7cd19b07b41707 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 1 Sep 2022 09:44:40 +0200 Subject: [PATCH 036/103] Measuring: Optimization into GLGizmoMeasure::on_render() --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 59 +++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 4 +- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index a9c2e18257..13415e70ac 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -106,6 +106,9 @@ void GLGizmoMeasure::data_changed() } if (model_object != m_old_model_object || model_volume != m_old_model_volume) update_if_needed(); + + m_last_inv_zoom = 0.0f; + m_last_plane_idx = -1; } bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) @@ -211,12 +214,13 @@ void GLGizmoMeasure::on_render() { const auto& [center, radius, normal] = m_curr_feature->get_circle(); - // TODO: check for changed inv_zoom - - m_circle.reset(); - GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); - m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); - m_circle.model.init_from(std::move(circle_geometry)); + if (m_last_inv_zoom != inv_zoom) { + m_last_inv_zoom = inv_zoom; + m_circle.reset(); + GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); + m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); + m_circle.model.init_from(std::move(circle_geometry)); + } m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); @@ -225,32 +229,33 @@ void GLGizmoMeasure::on_render() case Measure::SurfaceFeatureType::Plane: { const auto& [idx, normal, point] = m_curr_feature->get_plane(); + if (m_last_plane_idx != idx) { + m_last_plane_idx = idx; + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + const std::vector& triangle_indices = planes_triangles[idx]; - // TODO: check for changed idx + const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + unsigned int i = 0; + for (int idx : triangle_indices) { + const Vec3f& v0 = its.vertices[its.indices[idx][0]]; + const Vec3f& v1 = its.vertices[its.indices[idx][1]]; + const Vec3f& v2 = its.vertices[its.indices[idx][2]]; - const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); - const std::vector& triangle_indices = planes_triangles[idx]; + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; + } - const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; - GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - unsigned int i = 0; - for (int idx : triangle_indices) { - const Vec3f& v0 = its.vertices[its.indices[idx][0]]; - const Vec3f& v1 = its.vertices[its.indices[idx][1]]; - const Vec3f& v2 = its.vertices[its.indices[idx][2]]; - - const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); - init_data.add_vertex(v0, n); - init_data.add_vertex(v1, n); - init_data.add_vertex(v2, n); - init_data.add_triangle(i, i + 1, i + 2); - i += 3; + m_plane.reset(); + m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); + m_plane.model.init_from(std::move(init_data)); } - m_plane.reset(); - m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); - m_plane.model.init_from(std::move(init_data)); m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); break; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 09d40b47ba..46b37737ae 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -44,11 +44,13 @@ class GLGizmoMeasure : public GLGizmoBase std::optional m_curr_feature; std::optional m_curr_ex_feature_position; - // This holds information to decide whether recalculation is necessary: + // These hold information to decide whether recalculation is necessary: std::vector m_volumes_matrices; std::vector m_volumes_types; Vec3d m_first_instance_scale{ Vec3d::Ones() }; Vec3d m_first_instance_mirror{ Vec3d::Ones() }; + float m_last_inv_zoom{ 0.0f }; + int m_last_plane_idx{ -1 }; bool m_mouse_left_down{ false }; // for detection left_up of this gizmo const ModelObject* m_old_model_object{ nullptr }; From fe7982cb08428aec915d3ab7262e7618e2cde2c8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 1 Sep 2022 09:54:16 +0200 Subject: [PATCH 037/103] Fixed warnings --- src/libslic3r/Measure.cpp | 18 +++++++++--------- src/libslic3r/Measure.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 7 ++++++- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index cdde276dda..1a79369a3e 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -106,7 +106,7 @@ void MeasuringImpl::update_planes() int facet_idx = facet_queue[-- facet_queue_cnt]; const stl_normal& this_normal = face_normals[facet_idx]; if (is_same_normal(this_normal, *normal_ptr)) { - const Vec3i& face = m_its.indices[facet_idx]; +// const Vec3i& face = m_its.indices[facet_idx]; m_face_to_plane[facet_idx] = m_planes.size() - 1; m_planes.back().facets.emplace_back(facet_idx); @@ -132,7 +132,7 @@ void MeasuringImpl::update_planes() for (int face_id=0; face_id lengths; - for (int i=0; i M_PI) @@ -236,10 +236,10 @@ void MeasuringImpl::extract_features() bool circle = false; std::vector circles; std::vector> circles_idxs; - for (int i=1; i polygon_lower_threshold) { if (angle < polygon_upper_threshold) { const Vec3d center = std::get<0>(circles[i].get_circle()); - for (int j=circles_idxs[i].first + 1; j<=circles_idxs[i].second; ++j) + for (int j=(int)circles_idxs[i].first + 1; j<=(int)circles_idxs[i].second; ++j) plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, border[j-1], border[j], std::make_optional(center), 0.)); } else { @@ -286,7 +286,7 @@ void MeasuringImpl::extract_features() // We have the circles. Now go around again and pick edges. int cidx = 0; // index of next circle in the way for (int i=1; i circles_idxs[cidx].first) + if (cidx < (int)circles_idxs.size() && i > (int)circles_idxs[cidx].first) i = circles_idxs[cidx++].second; else plane.surface_features.emplace_back(SurfaceFeature( SurfaceFeatureType::Edge, border[i-1], border[i], std::optional(), 0.)); diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 95d8f34cba..9e3629589b 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -68,7 +68,7 @@ private: Vec3d m_pt1; Vec3d m_pt2; std::optional m_pt3; - double m_value; + double m_value{ 0.0 }; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 13415e70ac..54c944590b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -200,6 +200,7 @@ void GLGizmoMeasure::on_render() return; switch (m_curr_feature->get_type()) { + default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); @@ -265,6 +266,7 @@ void GLGizmoMeasure::on_render() else if (is_hovering_on_extended_selection) { switch (m_curr_feature->get_type()) { + default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { m_curr_ex_feature_position = model_matrix * m_curr_feature->get_point(); @@ -342,7 +344,9 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_normal_matrix", view_normal_matrix); }; - switch (m_curr_feature->get_type()) { + switch (m_curr_feature->get_type()) + { + default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { const Vec3d& position = m_curr_feature->get_point(); @@ -552,6 +556,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (ImGui::BeginTable("Data", 2)) { switch (feature_type) { + default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { const Vec3d position = volume_matrix * m_curr_feature->get_point(); From c6e6c86627f2889253425787fedaea08c1d2f850 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 1 Sep 2022 10:19:12 +0200 Subject: [PATCH 038/103] Refactoring into GLGizmoMeasure.cpp to remove duplicated code --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 63 +++++++++++------------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 54c944590b..3b257076a2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -48,6 +48,30 @@ static const std::string CTRL_STR = #endif //__APPLE__ ; +static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) +{ + assert(0 <= idx && idx < (int)planes_triangles.size()); + const std::vector& triangle_indices = planes_triangles[idx]; + + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + unsigned int i = 0; + for (int idx : triangle_indices) { + const Vec3f& v0 = its.vertices[its.indices[idx][0]]; + const Vec3f& v1 = its.vertices[its.indices[idx][1]]; + const Vec3f& v2 = its.vertices[its.indices[idx][2]]; + + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; + } + + return init_data; +} + GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { @@ -232,26 +256,9 @@ void GLGizmoMeasure::on_render() const auto& [idx, normal, point] = m_curr_feature->get_plane(); if (m_last_plane_idx != idx) { m_last_plane_idx = idx; - const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); - const std::vector& triangle_indices = planes_triangles[idx]; - const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; - GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - unsigned int i = 0; - for (int idx : triangle_indices) { - const Vec3f& v0 = its.vertices[its.indices[idx][0]]; - const Vec3f& v1 = its.vertices[its.indices[idx][1]]; - const Vec3f& v2 = its.vertices[its.indices[idx][2]]; - - const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); - init_data.add_vertex(v0, n); - init_data.add_vertex(v1, n); - init_data.add_vertex(v2, n); - init_data.add_triangle(i, i + 1, i + 2); - i += 3; - } - + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + GLModel::Geometry init_data = init_plane_data(its, planes_triangles, idx); m_plane.reset(); m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); m_plane.model.init_from(std::move(init_data)); @@ -427,23 +434,9 @@ void GLGizmoMeasure::update_if_needed() auto update_plane_models_cache = [this](const indexed_triangle_set& its) { m_plane_models_cache.clear(); const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); - for (const std::vector& triangle_indices : planes_triangles) { + for (int idx = 0; idx < (int)planes_triangles.size(); ++idx) { m_plane_models_cache.emplace_back(GLModel()); - GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - unsigned int i = 0; - for (int idx : triangle_indices) { - const Vec3f& v0 = its.vertices[its.indices[idx][0]]; - const Vec3f& v1 = its.vertices[its.indices[idx][1]]; - const Vec3f& v2 = its.vertices[its.indices[idx][2]]; - - const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); - init_data.add_vertex(v0, n); - init_data.add_vertex(v1, n); - init_data.add_vertex(v2, n); - init_data.add_triangle(i, i + 1, i + 2); - i += 3; - } + GLModel::Geometry init_data = init_plane_data(its, planes_triangles, idx); m_plane_models_cache.back().init_from(std::move(init_data)); } }; From c5532b175ed894428eec232b90d00bd3a21c59f8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 2 Sep 2022 11:24:06 +0200 Subject: [PATCH 039/103] Measuring: Added features selection in GLGizmoMeasure --- src/libslic3r/Measure.hpp | 6 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 382 +++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 39 ++- 3 files changed, 297 insertions(+), 130 deletions(-) diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 9e3629589b..3aefc0b54b 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -107,7 +107,11 @@ struct MeasurementResult { std::optional angle; std::optional distance_infinite; std::optional distance_strict; - std::optional distance_xyz; + std::optional distance_xyz; + + bool has_any_data() const { + return angle.has_value() || distance_infinite.has_value() || distance_strict.has_value() || distance_xyz.has_value(); + } }; // Returns distance/angle between two SurfaceFeatures. diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 3b257076a2..8c27678ce0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -31,9 +31,8 @@ std::string surface_feature_type_as_string(Slic3r::Measure::SurfaceFeatureType t namespace Slic3r { namespace GUI { -static const Slic3r::ColorRGBA BASIC_HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; -static const Slic3r::ColorRGBA EXTENDED_HOVER_COLOR = { 0.8f, 0.8f, 0.2f, 1.0f }; -static const Slic3r::ColorRGBA LOCK_COLOR = { 0.5f, 0.5f, 0.5f, 1.0f }; +static const Slic3r::ColorRGBA SELECTED_1ST_COLOR = { 0.25f, 0.75f, 0.75f, 1.0f }; +static const Slic3r::ColorRGBA SELECTED_2ND_COLOR = { 0.75f, 0.25f, 0.75f, 1.0f }; static const int POINT_ID = 100; static const int EDGE_ID = 200; @@ -95,7 +94,23 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) } else if (mouse_event.LeftDown()) { if (m_hover_id != -1) { - m_mouse_left_down = true; + m_mouse_left_down = true; + + auto set_item_from_feature = [this]() { + const SelectedFeatures::Item item = { m_mode, (m_mode == EMode::ExtendedSelection) ? + Measure::SurfaceFeature(Measure::SurfaceFeatureType::Point, *m_curr_ex_feature_position, Vec3d::Zero(), std::nullopt, 0.0) : + m_curr_feature }; + return item; + }; + + if (m_selected_features.first.feature.has_value()) { + const SelectedFeatures::Item item = set_item_from_feature(); + if (m_selected_features.first != item) + m_selected_features.second = item; + } + else + m_selected_features.first = set_item_from_feature(); + return true; } @@ -103,7 +118,6 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) // take responsibility for left up if (m_parent.get_first_hover_volume_idx() >= 0) m_mouse_left_down = true; - } else if (mouse_event.LeftUp()) { if (m_mouse_left_down) { @@ -133,6 +147,7 @@ void GLGizmoMeasure::data_changed() m_last_inv_zoom = 0.0f; m_last_plane_idx = -1; + m_selected_features.reset(); } bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) @@ -321,13 +336,13 @@ void GLGizmoMeasure::on_render() } } - if (!m_curr_feature.has_value()) + if (!m_curr_feature.has_value() && !m_selected_features.first.feature.has_value()) return; GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; - + shader->start_using(); shader->set_uniform("emission_factor", 0.25f); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); @@ -337,13 +352,6 @@ void GLGizmoMeasure::on_render() const Transform3d& view_matrix = camera.get_view_matrix(); - ColorRGBA feature_color; - switch (m_mode) - { - case EMode::BasicSelection: { feature_color = BASIC_HOVER_COLOR; break; } - case EMode::ExtendedSelection: { feature_color = LOCK_COLOR; break; } - } - auto set_matrix_uniforms = [shader, &view_matrix](const Transform3d& model_matrix) { const Transform3d view_model_matrix = view_matrix * model_matrix; shader->set_uniform("view_model_matrix", view_model_matrix); @@ -351,76 +359,143 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_normal_matrix", view_normal_matrix); }; - switch (m_curr_feature->get_type()) - { - default: { assert(false); break; } - case Measure::SurfaceFeatureType::Point: - { - const Vec3d& position = m_curr_feature->get_point(); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(feature_matrix); - m_sphere.model.set_color(is_hovering_on_extended_selection ? EXTENDED_HOVER_COLOR : feature_color); - m_sphere.model.render(); - auto it = m_raycasters.find(POINT_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - const auto& [center, radius, n] = m_curr_feature->get_circle(); - // render center - const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(center_matrix); - m_sphere.model.set_color((is_hovering_on_extended_selection && m_hover_id == POINT_ID) ? EXTENDED_HOVER_COLOR : feature_color); - m_sphere.model.render(); - auto it = m_raycasters.find(POINT_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(center_matrix); + auto render_feature = [this, set_matrix_uniforms](const Measure::SurfaceFeature& feature, const std::vector& colors, + const Transform3d& model_matrix, float inv_zoom, bool update_raycasters) { + switch (feature.get_type()) + { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + const Vec3d& position = feature.get_point(); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(feature_matrix); + m_sphere.model.set_color(colors.front()); + m_sphere.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); + } + break; + } + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, n] = feature.get_circle(); + // render center + const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(center_matrix); + m_sphere.model.set_color(colors.front()); + m_sphere.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(center_matrix); + } + // render circle + const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); + set_matrix_uniforms(circle_matrix); + m_circle.model.set_color(colors.back()); + m_circle.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(CIRCLE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(circle_matrix); + } + break; + } + case Measure::SurfaceFeatureType::Edge: + { + const auto& [start, end] = feature.get_edge(); + auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * + Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); + set_matrix_uniforms(feature_matrix); + m_cylinder.model.set_color(colors.front()); + m_cylinder.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(EDGE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); + } + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto& [idx, normal, pt] = feature.get_plane(); + assert(idx < m_plane_models_cache.size()); + set_matrix_uniforms(model_matrix); + m_plane_models_cache[idx].set_color(colors.front()); + m_plane_models_cache[idx].render(); + if (update_raycasters) { + auto it = m_raycasters.find(PLANE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(model_matrix); + } + break; + } + } + }; - // render circle - const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); - set_matrix_uniforms(circle_matrix); - m_circle.model.set_color(feature_color); - m_circle.model.render(); - it = m_raycasters.find(CIRCLE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(circle_matrix); - break; + auto hover_selection_color = [this]() { + return saturate(!m_selected_features.first.feature.has_value() ? SELECTED_1ST_COLOR : SELECTED_2ND_COLOR, 1.5f); + }; + + auto hovering_color = [this, hover_selection_color, &selection]() { + return (m_mode == EMode::ExtendedSelection) ? selection.get_first_volume()->render_color : hover_selection_color(); + }; + + if (m_curr_feature.has_value()) { + std::vector colors; + if (m_selected_features.first.feature.has_value() && *m_curr_feature == *m_selected_features.first.feature) + colors.emplace_back(SELECTED_1ST_COLOR); + else if (m_selected_features.second.feature.has_value() && *m_curr_feature == *m_selected_features.second.feature) + colors.emplace_back(SELECTED_2ND_COLOR); + else { + switch (m_curr_feature->get_type()) + { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + colors.emplace_back(hover_selection_color()); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + colors.emplace_back((m_hover_id == POINT_ID) ? hover_selection_color() : hovering_color()); + colors.emplace_back(hovering_color()); + break; + } + case Measure::SurfaceFeatureType::Edge: + case Measure::SurfaceFeatureType::Plane: + { + colors.emplace_back(hovering_color()); + break; + } + } + } + + render_feature(*m_curr_feature, colors, model_matrix, inv_zoom, true); } - case Measure::SurfaceFeatureType::Edge: - { - const auto& [start, end] = m_curr_feature->get_edge(); - auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * - Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); - set_matrix_uniforms(feature_matrix); - m_cylinder.model.set_color(feature_color); - m_cylinder.model.render(); - auto it = m_raycasters.find(EDGE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - const auto& [idx, normal, pt] = m_curr_feature->get_plane(); - assert(idx < m_plane_models_cache.size()); - set_matrix_uniforms(model_matrix); - m_plane_models_cache[idx].set_color(feature_color); - m_plane_models_cache[idx].render(); - auto it = m_raycasters.find(PLANE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(model_matrix); - break; + + if (m_selected_features.first.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.first.feature)) { + std::vector colors; + colors.emplace_back(SELECTED_1ST_COLOR); + render_feature(*m_selected_features.first.feature, colors, + (m_selected_features.first.mode == EMode::BasicSelection) ? model_matrix : Transform3d::Identity(), inv_zoom, false); } + if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) { + std::vector colors; + colors.emplace_back(SELECTED_2ND_COLOR); + render_feature(*m_selected_features.second.feature, colors, + (m_selected_features.second.mode == EMode::BasicSelection) ? model_matrix : Transform3d::Identity(), inv_zoom, false); } if (is_hovering_on_extended_selection && m_curr_ex_feature_position.has_value()) { if (m_hover_id != POINT_ID) { const Transform3d matrix = Geometry::translation_transform(*m_curr_ex_feature_position) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); - m_sphere.model.set_color(EXTENDED_HOVER_COLOR); + m_sphere.model.set_color(hover_selection_color()); +// m_sphere.model.set_color(EXTENDED_HOVER_COLOR); m_sphere.model.render(); } } @@ -496,6 +571,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit { static std::optional last_feature; static EMode last_mode = EMode::BasicSelection; + static SelectedFeatures last_selected_features; static float last_y = 0.0f; static float last_h = 0.0f; @@ -515,18 +591,20 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit last_y = y; } - auto add_row_to_table = [this](const wxString& label, const std::string& value) { + auto add_row_to_table = [this](const std::string& label, const ImVec4& label_color, const std::string& value, const ImVec4& value_color) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); + m_imgui->text_colored(label_color, label); ImGui::TableSetColumnIndex(1); - m_imgui->text(value); + m_imgui->text_colored(value_color, value); }; + auto format_double = [](double value) { char buf[1024]; sprintf(buf, "%.3f", value); return std::string(buf); }; + auto format_vec3 = [](const Vec3d& v) { char buf[1024]; sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); @@ -534,9 +612,10 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit }; if (ImGui::BeginTable("Commands", 2)) { - add_row_to_table(_u8L("Left mouse button"), (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point")); + add_row_to_table(_u8L("Left mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, + (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); if (m_mode == EMode::BasicSelection && m_hover_id != -1) - add_row_to_table(CTRL_STR, _u8L("Enable point selection")); + add_row_to_table(CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } @@ -545,48 +624,49 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit const Measure::SurfaceFeatureType feature_type = m_curr_feature->get_type(); if (m_mode == EMode::BasicSelection) { if (feature_type != Measure::SurfaceFeatureType::Undef) { - if (ImGui::CollapsingHeader(surface_feature_type_as_string(feature_type).c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { - if (ImGui::BeginTable("Data", 2)) { - switch (feature_type) - { - default: { assert(false); break; } - case Measure::SurfaceFeatureType::Point: - { - const Vec3d position = volume_matrix * m_curr_feature->get_point(); - add_row_to_table(_u8L("Position") + ":", format_vec3(position)); - break; - } - case Measure::SurfaceFeatureType::Edge: - { - auto [from, to] = m_curr_feature->get_edge(); - from = volume_matrix * from; - to = volume_matrix * to; - add_row_to_table(_u8L("From") + ":", format_vec3(from)); - add_row_to_table(_u8L("To") + ":", format_vec3(to)); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - auto [center, radius, normal] = m_curr_feature->get_circle(); - center = volume_matrix * center; - normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - add_row_to_table(_u8L("Center") + ":", format_vec3(center)); - add_row_to_table(_u8L("Radius") + ":", format_double(radius)); - add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - auto [idx, normal, origin] = m_curr_feature->get_plane(); - origin = volume_matrix * origin; - normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - add_row_to_table(_u8L("Origin") + ":", format_vec3(origin)); - add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); - break; - } - } - ImGui::EndTable(); + ImGui::Separator(); + m_imgui->text(surface_feature_type_as_string(feature_type) + ":"); + if (ImGui::BeginTable("Data", 2)) { + switch (feature_type) + { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + const Vec3d position = volume_matrix * m_curr_feature->get_point(); + add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; } + case Measure::SurfaceFeatureType::Edge: + { + auto [from, to] = m_curr_feature->get_edge(); + from = volume_matrix * from; + to = volume_matrix * to; + add_row_to_table(_u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("Length") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + auto [center, radius, normal] = m_curr_feature->get_circle(); + center = volume_matrix * center; + normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + add_row_to_table(_u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("Radius") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + auto [idx, normal, origin] = m_curr_feature->get_plane(); + origin = volume_matrix * origin; + normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + add_row_to_table(_u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + } + ImGui::EndTable(); } } } @@ -601,20 +681,68 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit default: { assert(false); break; } } - if (ImGui::CollapsingHeader(header.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { - if (ImGui::BeginTable("Data", 2)) { - add_row_to_table(_u8L("Position") + ":", format_vec3(*m_curr_ex_feature_position)); - ImGui::EndTable(); - } + ImGui::Separator(); + m_imgui->text(header + ":"); + if (ImGui::BeginTable("Data", 2)) { + add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*m_curr_ex_feature_position), + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::EndTable(); } } } } - if (last_feature != m_curr_feature || last_mode != m_mode) { + ImGui::Separator(); + m_imgui->text(_u8L("Selection")); + if (ImGui::BeginTable("Selection", 2)) { + add_row_to_table("1", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? + surface_feature_type_as_string(m_selected_features.first.feature->get_type()) : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR)); + add_row_to_table("2", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? + surface_feature_type_as_string(m_selected_features.second.feature->get_type()) : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR)); + ImGui::EndTable(); + } + + if (m_selected_features.first.feature.has_value()) { + if (m_imgui->button(_u8L("Restart"))) { + m_selected_features.reset(); + m_imgui->set_requires_extra_frame(); + } + } + + if (m_selected_features.second.feature.has_value()) { + const Measure::MeasurementResult measure = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); + ImGui::Separator(); + if (measure.has_any_data()) { + m_imgui->text(_u8L("Measure")); + if (ImGui::BeginTable("Measure", 2)) { + if (measure.angle.has_value()) { + add_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + if (measure.distance_infinite.has_value()) { + add_row_to_table(_u8L("Distance Infinite") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(*measure.distance_infinite), + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + if (measure.distance_strict.has_value()) { + add_row_to_table(_u8L("Distance Strict") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(*measure.distance_strict), + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + if (measure.distance_xyz.has_value()) { + add_row_to_table(_u8L("Distance XYZ") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*measure.distance_xyz), + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + ImGui::EndTable(); + } + } + else + m_imgui->text(_u8L("No measure available")); + } + + if (last_feature != m_curr_feature || last_mode != m_mode || last_selected_features != m_selected_features) { // the dialog may have changed its size, ask for an extra frame to render it properly last_feature = m_curr_feature; last_mode = m_mode; + last_selected_features = m_selected_features; m_imgui->set_requires_extra_frame(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 46b37737ae..9e0cbd946c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -23,14 +23,47 @@ enum class SLAGizmoEventType : unsigned char; class GLGizmoMeasure : public GLGizmoBase { -// This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself. - enum class EMode : unsigned char { BasicSelection, ExtendedSelection }; + struct SelectedFeatures + { + struct Item + { + EMode mode{ EMode::BasicSelection }; + std::optional feature; + + bool operator == (const Item& other) const { + if (this->mode != other.mode) return false; + return this->feature == other.feature; + } + + bool operator != (const Item& other) const { + return !operator == (other); + } + }; + + Item first; + Item second; + + void reset() { + first.feature.reset(); + second.feature.reset(); + } + + bool operator == (const SelectedFeatures& other) const { + if (this->first != other.first) return false; + return this->second == other.second; + } + + bool operator != (const SelectedFeatures& other) const { + return !operator == (other); + } + }; + EMode m_mode{ EMode::BasicSelection }; std::unique_ptr m_measuring; @@ -60,6 +93,8 @@ class GLGizmoMeasure : public GLGizmoBase KeyAutoRepeatFilter m_ctrl_kar_filter; + SelectedFeatures m_selected_features; + void update_if_needed(); public: From 5e271a18df789de7af53749b767a9587c726685b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 2 Sep 2022 12:26:56 +0200 Subject: [PATCH 040/103] Measuring: Changes in GLGizmoMeasure imgui dialog layout --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 97 ++++++++++++------------ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 14 +++- 2 files changed, 61 insertions(+), 50 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 8c27678ce0..6fe6c36ef3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -15,19 +15,6 @@ #if ENABLE_MEASURE_GIZMO -std::string surface_feature_type_as_string(Slic3r::Measure::SurfaceFeatureType type) -{ - switch (type) - { - default: - case Slic3r::Measure::SurfaceFeatureType::Undef: { return L("Undefined"); } - case Slic3r::Measure::SurfaceFeatureType::Point: { return L("Vertex"); } - case Slic3r::Measure::SurfaceFeatureType::Edge: { return L("Edge"); } - case Slic3r::Measure::SurfaceFeatureType::Circle: { return L("Circle"); } - case Slic3r::Measure::SurfaceFeatureType::Plane: { return L("Plane"); } - } -} - namespace Slic3r { namespace GUI { @@ -47,6 +34,32 @@ static const std::string CTRL_STR = #endif //__APPLE__ ; +static std::string surface_feature_type_as_string(Measure::SurfaceFeatureType type) +{ + switch (type) + { + default: + case Measure::SurfaceFeatureType::Undef: { return L("Undefined"); } + case Measure::SurfaceFeatureType::Point: { return L("Vertex"); } + case Measure::SurfaceFeatureType::Edge: { return L("Edge"); } + case Measure::SurfaceFeatureType::Circle: { return L("Circle"); } + case Measure::SurfaceFeatureType::Plane: { return L("Plane"); } + } +} + +static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType type, int hover_id) +{ + std::string ret; + switch (type) { + case Measure::SurfaceFeatureType::Point: { ret = _u8L("Vertex"); break; } + case Measure::SurfaceFeatureType::Edge: { ret = _u8L("Point on edge"); break; } + case Measure::SurfaceFeatureType::Circle: { ret = (hover_id == POINT_ID) ? _u8L("Center of circle") : _u8L("Point on circle"); break; } + case Measure::SurfaceFeatureType::Plane: { ret = _u8L("Point on plane"); break; } + default: { assert(false); break; } + } + return ret; +} + static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) { assert(0 <= idx && idx < (int)planes_triangles.size()); @@ -97,9 +110,9 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) m_mouse_left_down = true; auto set_item_from_feature = [this]() { - const SelectedFeatures::Item item = { m_mode, (m_mode == EMode::ExtendedSelection) ? - Measure::SurfaceFeature(Measure::SurfaceFeatureType::Point, *m_curr_ex_feature_position, Vec3d::Zero(), std::nullopt, 0.0) : - m_curr_feature }; + const SelectedFeatures::Item item = { m_mode, + (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), + (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(Measure::SurfaceFeatureType::Point, *m_curr_point_on_feature_position, Vec3d::Zero(), std::nullopt, 0.0) : m_curr_feature }; return item; }; @@ -179,7 +192,7 @@ void GLGizmoMeasure::on_set_state() if (m_state == Off) { m_ctrl_kar_filter.reset_count(); m_curr_feature.reset(); - m_curr_ex_feature_position.reset(); + m_curr_point_on_feature_position.reset(); } else m_mode = EMode::BasicSelection; @@ -231,7 +244,7 @@ void GLGizmoMeasure::on_render() curr_feature = m_measuring->get_feature(facet_idx, position_on_model.cast()); if (m_mode == EMode::BasicSelection) { - m_curr_ex_feature_position.reset(); + m_curr_point_on_feature_position.reset(); if (m_curr_feature != curr_feature) { GLGizmoMeasure::on_unregister_raycasters_for_picking(); m_curr_feature = curr_feature; @@ -291,7 +304,7 @@ void GLGizmoMeasure::on_render() default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { - m_curr_ex_feature_position = model_matrix * m_curr_feature->get_point(); + m_curr_point_on_feature_position = model_matrix * m_curr_feature->get_point(); break; } case Measure::SurfaceFeatureType::Edge: @@ -303,20 +316,20 @@ void GLGizmoMeasure::on_render() const Transform3d& trafo = it->second->get_transform(); it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); p = { 0.0f, 0.0f, p.z() }; - m_curr_ex_feature_position = trafo * p.cast(); + m_curr_point_on_feature_position = trafo * p.cast(); } break; } case Measure::SurfaceFeatureType::Plane: { - m_curr_ex_feature_position = model_matrix * position_on_model.cast(); + m_curr_point_on_feature_position = model_matrix * position_on_model.cast(); break; } case Measure::SurfaceFeatureType::Circle: { const auto& [center, radius, normal] = m_curr_feature->get_circle(); if (m_hover_id == POINT_ID) - m_curr_ex_feature_position = model_matrix * center; + m_curr_point_on_feature_position = model_matrix * center; else { auto it = m_raycasters.find(CIRCLE_ID); if (it != m_raycasters.end() && it->second != nullptr) { @@ -328,7 +341,7 @@ void GLGizmoMeasure::on_render() if (angle < 0.0f) angle += 2.0f * float(M_PI); p = float(radius) * Vec3f(std::cos(angle), std::sin(angle), 0.0f); - m_curr_ex_feature_position = trafo * p.cast(); + m_curr_point_on_feature_position = trafo * p.cast(); } } break; @@ -490,12 +503,11 @@ void GLGizmoMeasure::on_render() (m_selected_features.second.mode == EMode::BasicSelection) ? model_matrix : Transform3d::Identity(), inv_zoom, false); } - if (is_hovering_on_extended_selection && m_curr_ex_feature_position.has_value()) { + if (is_hovering_on_extended_selection && m_curr_point_on_feature_position.has_value()) { if (m_hover_id != POINT_ID) { - const Transform3d matrix = Geometry::translation_transform(*m_curr_ex_feature_position) * Geometry::scale_transform(inv_zoom); + const Transform3d matrix = Geometry::translation_transform(*m_curr_point_on_feature_position) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); m_sphere.model.set_color(hover_selection_color()); -// m_sphere.model.set_color(EXTENDED_HOVER_COLOR); m_sphere.model.render(); } } @@ -591,12 +603,12 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit last_y = y; } - auto add_row_to_table = [this](const std::string& label, const ImVec4& label_color, const std::string& value, const ImVec4& value_color) { + auto add_row_to_table = [this](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - m_imgui->text_colored(label_color, label); + m_imgui->text_colored(col_1_color, col_1); ImGui::TableSetColumnIndex(1); - m_imgui->text_colored(value_color, value); + m_imgui->text_colored(col_2_color, col_2); }; auto format_double = [](double value) { @@ -671,20 +683,11 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } } else if (m_mode == EMode::ExtendedSelection) { - if (m_hover_id != -1 && m_curr_ex_feature_position.has_value()) { - std::string header; - switch (feature_type) { - case Measure::SurfaceFeatureType::Point: { header = _u8L("Vertex"); break; } - case Measure::SurfaceFeatureType::Edge: { header = _u8L("Point on edge"); break; } - case Measure::SurfaceFeatureType::Circle: { header = (m_hover_id == POINT_ID) ? _u8L("Center of circle") : _u8L("Point on circle"); break; } - case Measure::SurfaceFeatureType::Plane: { header = _u8L("Point on plane"); break; } - default: { assert(false); break; } - } - + if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) { ImGui::Separator(); - m_imgui->text(header + ":"); + m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id) + ":"); if (ImGui::BeginTable("Data", 2)) { - add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*m_curr_ex_feature_position), + add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*m_curr_point_on_feature_position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } @@ -693,12 +696,12 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } ImGui::Separator(); - m_imgui->text(_u8L("Selection")); - if (ImGui::BeginTable("Selection", 2)) { - add_row_to_table("1", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? - surface_feature_type_as_string(m_selected_features.first.feature->get_type()) : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR)); - add_row_to_table("2", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? - surface_feature_type_as_string(m_selected_features.second.feature->get_type()) : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR)); + const ImGuiTableFlags flags = /*ImGuiTableFlags_SizingStretchSame | */ImGuiTableFlags_BordersOuter | /*ImGuiTableFlags_BordersV | */ImGuiTableFlags_BordersH; + if (ImGui::BeginTable("Selection", 2, flags)) { + add_row_to_table(_u8L("Selection") + " 1:", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? + m_selected_features.first.source : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR)); + add_row_to_table(_u8L("Selection") + " 2:", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? + m_selected_features.second.source : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR)); ImGui::EndTable(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 9e0cbd946c..5755d9a912 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -34,24 +34,32 @@ class GLGizmoMeasure : public GLGizmoBase struct Item { EMode mode{ EMode::BasicSelection }; + std::string source; std::optional feature; bool operator == (const Item& other) const { if (this->mode != other.mode) return false; + if (this->source != other.source) return false; return this->feature == other.feature; } bool operator != (const Item& other) const { return !operator == (other); } + + void reset() { + mode = EMode::BasicSelection; + source.clear(); + feature.reset(); + } }; Item first; Item second; void reset() { - first.feature.reset(); - second.feature.reset(); + first.reset(); + second.reset(); } bool operator == (const SelectedFeatures& other) const { @@ -75,7 +83,7 @@ class GLGizmoMeasure : public GLGizmoBase std::vector m_plane_models_cache; std::map> m_raycasters; std::optional m_curr_feature; - std::optional m_curr_ex_feature_position; + std::optional m_curr_point_on_feature_position; // These hold information to decide whether recalculation is necessary: std::vector m_volumes_matrices; From 91266e48158ed1c7e47a18eab0c7aaa4551838bd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 08:26:19 +0200 Subject: [PATCH 041/103] Measuring: Disable scene raycasters while GLGizmoMeasure is active --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 101 +++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 5 ++ 2 files changed, 71 insertions(+), 35 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 6fe6c36ef3..8fddf11adf 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -167,8 +167,10 @@ bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_po { if (action == SLAGizmoEventType::CtrlDown) { if (m_ctrl_kar_filter.is_first()) { - if (m_curr_feature.has_value()) + if (m_curr_feature.has_value()) { m_mode = EMode::ExtendedSelection; + disable_scene_raycasters(); + } } m_ctrl_kar_filter.increase_count(); @@ -176,6 +178,7 @@ bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_po else if (action == SLAGizmoEventType::CtrlUp) { m_ctrl_kar_filter.reset_count(); m_mode = EMode::BasicSelection; + restore_scene_raycasters_state(); } return true; @@ -193,9 +196,19 @@ void GLGizmoMeasure::on_set_state() m_ctrl_kar_filter.reset_count(); m_curr_feature.reset(); m_curr_point_on_feature_position.reset(); + restore_scene_raycasters_state(); } - else + else { m_mode = EMode::BasicSelection; + // store current state of scene raycaster for later use + m_scene_raycaster_state.clear(); + m_scene_raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); + if (m_scene_raycasters != nullptr) { + for (const auto r : *m_scene_raycasters) { + m_scene_raycaster_state.emplace_back(r->is_active()); + } + } + } } CommonGizmosDataID GLGizmoMeasure::on_get_requirements() const @@ -234,16 +247,13 @@ void GLGizmoMeasure::on_render() Vec3f position_on_model; Vec3f normal_on_model; - size_t facet_idx; - m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, position_on_model, normal_on_model, nullptr, &facet_idx); + size_t model_facet_idx; + m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); - const bool is_hovering_on_extended_selection = m_mode == EMode::ExtendedSelection && m_hover_id != -1; - - std::optional curr_feature; - if (m_mode == EMode::BasicSelection) - curr_feature = m_measuring->get_feature(facet_idx, position_on_model.cast()); + const bool is_hovering_on_locked_feature = m_mode == EMode::ExtendedSelection && m_hover_id != -1; if (m_mode == EMode::BasicSelection) { + std::optional curr_feature = m_measuring->get_feature(model_facet_idx, position_on_model.cast()); m_curr_point_on_feature_position.reset(); if (m_curr_feature != curr_feature) { GLGizmoMeasure::on_unregister_raycasters_for_picking(); @@ -265,8 +275,7 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Circle: { - const auto& [center, radius, normal] = m_curr_feature->get_circle(); - + const auto [center, radius, normal] = m_curr_feature->get_circle(); if (m_last_inv_zoom != inv_zoom) { m_last_inv_zoom = inv_zoom; m_circle.reset(); @@ -281,7 +290,7 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Plane: { - const auto& [idx, normal, point] = m_curr_feature->get_plane(); + const auto [idx, normal, point] = m_curr_feature->get_plane(); if (m_last_plane_idx != idx) { m_last_plane_idx = idx; const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; @@ -298,7 +307,23 @@ void GLGizmoMeasure::on_render() } } } - else if (is_hovering_on_extended_selection) { + else if (is_hovering_on_locked_feature) { + auto default_callback = [](const Vec3f& v) { return v; }; + auto position_on_feature = [this](int feature_type_id, const Camera& camera, std::function callback = nullptr) -> Vec3d { + auto it = m_raycasters.find(feature_type_id); + if (it != m_raycasters.end() && it->second != nullptr) { + Vec3f p; + Vec3f n; + const Transform3d& trafo = it->second->get_transform(); + bool res = it->second->get_raycaster()->closest_hit(m_mouse_pos, trafo, camera, p, n); + assert(res); + if (callback) + p = callback(p); + return trafo * p.cast(); + } + return Vec3d::Zero(); + }; + switch (m_curr_feature->get_type()) { default: { assert(false); break; } @@ -309,40 +334,27 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Edge: { - auto it = m_raycasters.find(EDGE_ID); - if (it != m_raycasters.end() && it->second != nullptr) { - Vec3f p; - Vec3f n; - const Transform3d& trafo = it->second->get_transform(); - it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); - p = { 0.0f, 0.0f, p.z() }; - m_curr_point_on_feature_position = trafo * p.cast(); - } + m_curr_point_on_feature_position = position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); break; } case Measure::SurfaceFeatureType::Plane: { - m_curr_point_on_feature_position = model_matrix * position_on_model.cast(); + m_curr_point_on_feature_position = position_on_feature(PLANE_ID, camera); break; } case Measure::SurfaceFeatureType::Circle: { - const auto& [center, radius, normal] = m_curr_feature->get_circle(); + const auto [center, radius, normal] = m_curr_feature->get_circle(); if (m_hover_id == POINT_ID) m_curr_point_on_feature_position = model_matrix * center; else { - auto it = m_raycasters.find(CIRCLE_ID); - if (it != m_raycasters.end() && it->second != nullptr) { - Vec3f p; - Vec3f n; - const Transform3d& trafo = it->second->get_transform(); - it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); - float angle = std::atan2(p.y(), p.x()); + const float r = radius; // needed for the following lambda + m_curr_point_on_feature_position = position_on_feature(CIRCLE_ID, camera, [r](const Vec3f& v) { + float angle = std::atan2(v.y(), v.x()); if (angle < 0.0f) angle += 2.0f * float(M_PI); - p = float(radius) * Vec3f(std::cos(angle), std::sin(angle), 0.0f); - m_curr_point_on_feature_position = trafo * p.cast(); - } + return float(r) * Vec3f(std::cos(angle), std::sin(angle), 0.0f); + }); } break; } @@ -503,7 +515,7 @@ void GLGizmoMeasure::on_render() (m_selected_features.second.mode == EMode::BasicSelection) ? model_matrix : Transform3d::Identity(), inv_zoom, false); } - if (is_hovering_on_extended_selection && m_curr_point_on_feature_position.has_value()) { + if (is_hovering_on_locked_feature && m_curr_point_on_feature_position.has_value()) { if (m_hover_id != POINT_ID) { const Transform3d matrix = Geometry::translation_transform(*m_curr_point_on_feature_position) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); @@ -579,6 +591,25 @@ void GLGizmoMeasure::update_if_needed() } } +void GLGizmoMeasure::disable_scene_raycasters() +{ + if (m_scene_raycasters != nullptr) { + for (auto r : *m_scene_raycasters) { + r->set_active(false); + } + } +} + +void GLGizmoMeasure::restore_scene_raycasters_state() +{ + if (m_scene_raycasters != nullptr) { + assert(m_scene_raycasters->size() == m_scene_raycaster_state.size()); + for (size_t i = 0; i < m_scene_raycasters->size(); ++i) { + (*m_scene_raycasters)[i]->set_active(m_scene_raycaster_state[i]); + } + } +} + void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) { static std::optional last_feature; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 5755d9a912..3bbeb92b44 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -84,6 +84,8 @@ class GLGizmoMeasure : public GLGizmoBase std::map> m_raycasters; std::optional m_curr_feature; std::optional m_curr_point_on_feature_position; + std::vector>* m_scene_raycasters{ nullptr }; + std::vector m_scene_raycaster_state; // These hold information to decide whether recalculation is necessary: std::vector m_volumes_matrices; @@ -105,6 +107,9 @@ class GLGizmoMeasure : public GLGizmoBase void update_if_needed(); + void disable_scene_raycasters(); + void restore_scene_raycasters_state(); + public: GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); From ae3beeb50a60631f4c6c67162d709d3ee34506a7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 08:57:49 +0200 Subject: [PATCH 042/103] Fixed warnings --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 8fddf11adf..74a5c93f7c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -308,7 +308,6 @@ void GLGizmoMeasure::on_render() } } else if (is_hovering_on_locked_feature) { - auto default_callback = [](const Vec3f& v) { return v; }; auto position_on_feature = [this](int feature_type_id, const Camera& camera, std::function callback = nullptr) -> Vec3d { auto it = m_raycasters.find(feature_type_id); if (it != m_raycasters.end() && it->second != nullptr) { @@ -317,9 +316,11 @@ void GLGizmoMeasure::on_render() const Transform3d& trafo = it->second->get_transform(); bool res = it->second->get_raycaster()->closest_hit(m_mouse_pos, trafo, camera, p, n); assert(res); - if (callback) - p = callback(p); - return trafo * p.cast(); + if (res) { + if (callback) + p = callback(p); + return trafo * p.cast(); + } } return Vec3d::Zero(); }; From 8050d457d38cd1250cb529a5ad17d845ef7fdc68 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 09:18:07 +0200 Subject: [PATCH 043/103] Measuring: GLGizmoMeasure - Fixed update of circle geometry --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 3 ++- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 74a5c93f7c..bf7fd04a7d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -276,8 +276,9 @@ void GLGizmoMeasure::on_render() case Measure::SurfaceFeatureType::Circle: { const auto [center, radius, normal] = m_curr_feature->get_circle(); - if (m_last_inv_zoom != inv_zoom) { + if (m_last_inv_zoom != inv_zoom || m_last_circle != m_curr_feature) { m_last_inv_zoom = inv_zoom; + m_last_circle = m_curr_feature; m_circle.reset(); GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 3bbeb92b44..d74fb9ed8e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -93,6 +93,7 @@ class GLGizmoMeasure : public GLGizmoBase Vec3d m_first_instance_scale{ Vec3d::Ones() }; Vec3d m_first_instance_mirror{ Vec3d::Ones() }; float m_last_inv_zoom{ 0.0f }; + std::optional m_last_circle; int m_last_plane_idx{ -1 }; bool m_mouse_left_down{ false }; // for detection left_up of this gizmo From bac3eebf51e5fdb5cd189aff06d2ad7678562b08 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 12:05:25 +0200 Subject: [PATCH 044/103] Measuring: GLGizmoMeasure - show data in inches into imgui dialog, when needed --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 42 ++++++++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index bf7fd04a7d..f8f84d7bc8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -3,6 +3,7 @@ #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/GUI_ObjectManipulation.hpp" #include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" @@ -664,6 +665,9 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::EndTable(); } + const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; + const std::string units = use_inches ? _u8L(" (in)") : _u8L(" (mm)"); + if (m_curr_feature.has_value()) { const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); const Measure::SurfaceFeatureType feature_type = m_curr_feature->get_type(); @@ -677,7 +681,9 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { - const Vec3d position = volume_matrix * m_curr_feature->get_point(); + Vec3d position = volume_matrix * m_curr_feature->get_point(); + if (use_inches) + position = ObjectManipulation::mm_to_in * position; add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } @@ -686,9 +692,13 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit auto [from, to] = m_curr_feature->get_edge(); from = volume_matrix * from; to = volume_matrix * to; + if (use_inches) { + from = ObjectManipulation::mm_to_in * from; + to = ObjectManipulation::mm_to_in * to; + } add_row_to_table(_u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_row_to_table(_u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("Length") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("Length") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Circle: @@ -696,8 +706,12 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit auto [center, radius, normal] = m_curr_feature->get_circle(); center = volume_matrix * center; normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + if (use_inches) { + center = ObjectManipulation::mm_to_in * center; + radius = ObjectManipulation::mm_to_in * radius; + } add_row_to_table(_u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("Radius") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("Radius") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } @@ -706,6 +720,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit auto [idx, normal, origin] = m_curr_feature->get_plane(); origin = volume_matrix * origin; normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + if (use_inches) + origin = ObjectManipulation::mm_to_in * origin; add_row_to_table(_u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; @@ -720,7 +736,10 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::Separator(); m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id) + ":"); if (ImGui::BeginTable("Data", 2)) { - add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*m_curr_point_on_feature_position), + Vec3d position = ObjectManipulation::mm_to_in * *m_curr_point_on_feature_position; + if (use_inches) + position = ObjectManipulation::mm_to_in * position; + add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } @@ -756,15 +775,24 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_infinite.has_value()) { - add_row_to_table(_u8L("Distance Infinite") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(*measure.distance_infinite), + double distance = ObjectManipulation::mm_to_in * *measure.distance_infinite; + if (use_inches) + distance = ObjectManipulation::mm_to_in * distance; + add_row_to_table(_u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_strict.has_value()) { - add_row_to_table(_u8L("Distance Strict") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(*measure.distance_strict), + double distance = ObjectManipulation::mm_to_in * *measure.distance_strict; + if (use_inches) + distance = ObjectManipulation::mm_to_in * distance; + add_row_to_table(_u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_xyz.has_value()) { - add_row_to_table(_u8L("Distance XYZ") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*measure.distance_xyz), + Vec3d distance = ObjectManipulation::mm_to_in * *measure.distance_xyz; + if (use_inches) + distance = ObjectManipulation::mm_to_in * distance; + add_row_to_table(_u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } ImGui::EndTable(); From 8a88b98f7f32fa9c276e45ce6edb7f599d3d3171 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 12:49:19 +0200 Subject: [PATCH 045/103] Measuring: GLGizmoMeasure - Fixed detection of current hovered feature --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index f8f84d7bc8..40ca6d3df3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -249,12 +249,11 @@ void GLGizmoMeasure::on_render() Vec3f position_on_model; Vec3f normal_on_model; size_t model_facet_idx; - m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); - + const bool mouse_on_object = m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); const bool is_hovering_on_locked_feature = m_mode == EMode::ExtendedSelection && m_hover_id != -1; if (m_mode == EMode::BasicSelection) { - std::optional curr_feature = m_measuring->get_feature(model_facet_idx, position_on_model.cast()); + std::optional curr_feature = mouse_on_object ? m_measuring->get_feature(model_facet_idx, position_on_model.cast()) : std::nullopt; m_curr_point_on_feature_position.reset(); if (m_curr_feature != curr_feature) { GLGizmoMeasure::on_unregister_raycasters_for_picking(); @@ -552,7 +551,7 @@ void GLGizmoMeasure::update_if_needed() // Let's save what we calculated it from: m_volumes_matrices.clear(); m_volumes_types.clear(); - m_first_instance_scale = Vec3d::Ones(); + m_first_instance_scale = Vec3d::Ones(); m_first_instance_mirror = Vec3d::Ones(); if (object != nullptr) { for (const ModelVolume* vol : object->volumes) { From b788628a18929bd20b00279de869b01520af7fe2 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 13:26:01 +0200 Subject: [PATCH 046/103] Fixed warnings on ARM64 --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 40ca6d3df3..c1e81ff21d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -89,11 +89,11 @@ GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filen : GLGizmoBase(parent, icon_filename, sprite_id) { GLModel::Geometry sphere_geometry = smooth_sphere(16, 7.5f); - m_sphere.mesh_raycaster = std::make_unique(std::make_shared(std::move(sphere_geometry.get_as_indexed_triangle_set()))); + m_sphere.mesh_raycaster = std::make_unique(std::make_shared(sphere_geometry.get_as_indexed_triangle_set())); m_sphere.model.init_from(std::move(sphere_geometry)); GLModel::Geometry cylinder_geometry = smooth_cylinder(16, 5.0f, 1.0f); - m_cylinder.mesh_raycaster = std::make_unique(std::make_shared(std::move(cylinder_geometry.get_as_indexed_triangle_set()))); + m_cylinder.mesh_raycaster = std::make_unique(std::make_shared(cylinder_geometry.get_as_indexed_triangle_set())); m_cylinder.model.init_from(std::move(cylinder_geometry)); } @@ -205,7 +205,7 @@ void GLGizmoMeasure::on_set_state() m_scene_raycaster_state.clear(); m_scene_raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); if (m_scene_raycasters != nullptr) { - for (const auto r : *m_scene_raycasters) { + for (const auto& r : *m_scene_raycasters) { m_scene_raycaster_state.emplace_back(r->is_active()); } } @@ -281,7 +281,7 @@ void GLGizmoMeasure::on_render() m_last_circle = m_curr_feature; m_circle.reset(); GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); - m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); + m_circle.mesh_raycaster = std::make_unique(std::make_shared(circle_geometry.get_as_indexed_triangle_set())); m_circle.model.init_from(std::move(circle_geometry)); } @@ -298,7 +298,7 @@ void GLGizmoMeasure::on_render() const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); GLModel::Geometry init_data = init_plane_data(its, planes_triangles, idx); m_plane.reset(); - m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); + m_plane.mesh_raycaster = std::make_unique(std::make_shared(init_data.get_as_indexed_triangle_set())); m_plane.model.init_from(std::move(init_data)); } From cb57b3c5cb4bd8e849f41dee04e52a82ed007288 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 15:16:31 +0200 Subject: [PATCH 047/103] Measuring: GLGizmoMeasure - Added colored icon into imgui dialog --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 68 +++++++++++++++--------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index c1e81ff21d..21e86532a1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -636,12 +636,20 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit last_y = y; } - auto add_row_to_table = [this](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { + auto add_row_to_table = [this](std::function col_1 = nullptr, std::function col_2 = nullptr) { + assert(col_1 != nullptr && col_2 != nullptr); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - m_imgui->text_colored(col_1_color, col_1); + col_1(); ImGui::TableSetColumnIndex(1); - m_imgui->text_colored(col_2_color, col_2); + col_2(); + }; + + auto add_strings_row_to_table = [this, add_row_to_table](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { + add_row_to_table( + [this, &col_1, &col_1_color]() { m_imgui->text_colored(col_1_color, col_1); }, + [this, &col_2, &col_2_color]() { m_imgui->text_colored(col_2_color, col_2); } + ); }; auto format_double = [](double value) { @@ -657,10 +665,22 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit }; if (ImGui::BeginTable("Commands", 2)) { - add_row_to_table(_u8L("Left mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, - (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table( + [this]() { + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Left mouse button")); + }, + [this]() { + m_imgui->text_colored(ImGui::GetStyleColorVec4(ImGuiCol_Text), (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point")); + ImGui::SameLine(); + const ImVec2 pos = ImGui::GetCursorScreenPos(); + const float rect_size = ImGui::GetTextLineHeight(); + ImGui::GetWindowDrawList()->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + rect_size, pos.y + rect_size }, + ImGuiWrapper::to_ImU32(m_selected_features.first.feature.has_value() ? SELECTED_2ND_COLOR : SELECTED_1ST_COLOR)); + ImGui::Dummy(ImVec2(rect_size, rect_size)); + } + ); if (m_mode == EMode::BasicSelection && m_hover_id != -1) - add_row_to_table(CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } @@ -683,7 +703,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit Vec3d position = volume_matrix * m_curr_feature->get_point(); if (use_inches) position = ObjectManipulation::mm_to_in * position; - add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Edge: @@ -695,9 +715,9 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit from = ObjectManipulation::mm_to_in * from; to = ObjectManipulation::mm_to_in * to; } - add_row_to_table(_u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("Length") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Length") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Circle: @@ -709,9 +729,9 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit center = ObjectManipulation::mm_to_in * center; radius = ObjectManipulation::mm_to_in * radius; } - add_row_to_table(_u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("Radius") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Radius") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Plane: @@ -721,8 +741,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; if (use_inches) origin = ObjectManipulation::mm_to_in * origin; - add_row_to_table(_u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } } @@ -738,7 +758,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit Vec3d position = ObjectManipulation::mm_to_in * *m_curr_point_on_feature_position; if (use_inches) position = ObjectManipulation::mm_to_in * position; - add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), + add_strings_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } @@ -747,11 +767,11 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } ImGui::Separator(); - const ImGuiTableFlags flags = /*ImGuiTableFlags_SizingStretchSame | */ImGuiTableFlags_BordersOuter | /*ImGuiTableFlags_BordersV | */ImGuiTableFlags_BordersH; + const ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersH; if (ImGui::BeginTable("Selection", 2, flags)) { - add_row_to_table(_u8L("Selection") + " 1:", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? + add_strings_row_to_table(_u8L("Selection") + " 1:", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? m_selected_features.first.source : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR)); - add_row_to_table(_u8L("Selection") + " 2:", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? + add_strings_row_to_table(_u8L("Selection") + " 2:", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? m_selected_features.second.source : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR)); ImGui::EndTable(); } @@ -767,31 +787,31 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit const Measure::MeasurementResult measure = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); ImGui::Separator(); if (measure.has_any_data()) { - m_imgui->text(_u8L("Measure")); + m_imgui->text(_u8L("Measure") + ":"); if (ImGui::BeginTable("Measure", 2)) { if (measure.angle.has_value()) { - add_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), + add_strings_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_infinite.has_value()) { double distance = ObjectManipulation::mm_to_in * *measure.distance_infinite; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_row_to_table(_u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + add_strings_row_to_table(_u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_strict.has_value()) { double distance = ObjectManipulation::mm_to_in * *measure.distance_strict; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_row_to_table(_u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + add_strings_row_to_table(_u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_xyz.has_value()) { Vec3d distance = ObjectManipulation::mm_to_in * *measure.distance_xyz; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_row_to_table(_u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), + add_strings_row_to_table(_u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } ImGui::EndTable(); From 49885b9c6f8948c723ff8c6f93dc2b8388f6a3ae Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 6 Sep 2022 10:17:53 +0200 Subject: [PATCH 048/103] Measuring: Added missing default values to SurfaceFeature member variables --- src/libslic3r/Measure.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 3aefc0b54b..3b811db0c5 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -64,9 +64,9 @@ public: } private: - SurfaceFeatureType m_type = SurfaceFeatureType::Undef; - Vec3d m_pt1; - Vec3d m_pt2; + SurfaceFeatureType m_type{ SurfaceFeatureType::Undef }; + Vec3d m_pt1{ Vec3d::Zero() }; + Vec3d m_pt2{ Vec3d::Zero() }; std::optional m_pt3; double m_value{ 0.0 }; }; From 80e1d8298b0feb335aa2d5752ca74df4076484e5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 6 Sep 2022 10:54:56 +0200 Subject: [PATCH 049/103] Measuring: Rewritten method SurfaceFeature::operator ==() --- src/libslic3r/Measure.hpp | 23 ++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 4 ++-- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 3b811db0c5..5d71983f0b 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -44,19 +44,28 @@ public: std::tuple get_circle() const { assert(m_type == SurfaceFeatureType::Circle); return std::make_tuple(m_pt1, m_value, m_pt2); } // For planes, return index into vector provided by Measuring::get_plane_triangle_indices, normal and point. - std::tuple get_plane() const { return std::make_tuple(int(m_value), m_pt1, m_pt2); } + std::tuple get_plane() const { assert(m_type == SurfaceFeatureType::Plane); return std::make_tuple(int(m_value), m_pt1, m_pt2); } // For anything, return an extra point that should also be considered a part of this. std::optional get_extra_point() const { assert(m_type != SurfaceFeatureType::Undef); return m_pt3; } bool operator == (const SurfaceFeature& other) const { if (this->m_type != other.m_type) return false; - if (!this->m_pt1.isApprox(other.m_pt1)) return false; - if (!this->m_pt2.isApprox(other.m_pt2)) return false; - if (this->m_pt3.has_value() && !other.m_pt3.has_value()) return false; - if (!this->m_pt3.has_value() && other.m_pt3.has_value()) return false; - if (this->m_pt3.has_value() && other.m_pt3.has_value() && !(*this->m_pt3).isApprox(*other.m_pt3)) return false; - return this->m_value == other.m_value; + switch (this->m_type) + { + case SurfaceFeatureType::Undef: { break; } + case SurfaceFeatureType::Point: { return (this->m_pt1.isApprox(other.m_pt1)); } + case SurfaceFeatureType::Edge: { + return (this->m_pt1.isApprox(other.m_pt1) && this->m_pt2.isApprox(other.m_pt2)) || + (this->m_pt1.isApprox(other.m_pt2) && this->m_pt2.isApprox(other.m_pt1)); + } + case SurfaceFeatureType::Plane: + case SurfaceFeatureType::Circle: { + return (this->m_pt1.isApprox(other.m_pt1) && this->m_pt2.isApprox(other.m_pt2) && std::abs(this->m_value - other.m_value) < EPSILON); + } + } + + return false; } bool operator != (const SurfaceFeature& other) const { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 21e86532a1..942a8c93eb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -113,7 +113,7 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) auto set_item_from_feature = [this]() { const SelectedFeatures::Item item = { m_mode, (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), - (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(Measure::SurfaceFeatureType::Point, *m_curr_point_on_feature_position, Vec3d::Zero(), std::nullopt, 0.0) : m_curr_feature }; + (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; return item; }; @@ -331,7 +331,7 @@ void GLGizmoMeasure::on_render() default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { - m_curr_point_on_feature_position = model_matrix * m_curr_feature->get_point(); + m_curr_point_on_feature_position = position_on_feature(POINT_ID, camera); break; } case Measure::SurfaceFeatureType::Edge: From 2b7520dc9eee8063c7d376811603c2d09c4a75e4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 6 Sep 2022 12:02:44 +0200 Subject: [PATCH 050/103] Measuring: GLGizmoMeasure - Use mouse right click to restart selection --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 49 ++++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 942a8c93eb..ebf3139868 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -110,20 +110,13 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) if (m_hover_id != -1) { m_mouse_left_down = true; - auto set_item_from_feature = [this]() { - const SelectedFeatures::Item item = { m_mode, - (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), - (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; - return item; - }; - if (m_selected_features.first.feature.has_value()) { - const SelectedFeatures::Item item = set_item_from_feature(); + const SelectedFeatures::Item item = item_from_feature(); if (m_selected_features.first != item) m_selected_features.second = item; } else - m_selected_features.first = set_item_from_feature(); + m_selected_features.first = item_from_feature(); return true; } @@ -140,6 +133,16 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) return true; } } + else if (mouse_event.RightDown()) { + if (m_hover_id != -1) { + if (m_selected_features.first.feature.has_value()) { + if (m_selected_features.first == item_from_feature()) { + m_selected_features.reset(); + m_imgui->set_requires_extra_frame(); + } + } + } + } else if (mouse_event.Leaving()) m_mouse_left_down = false; @@ -612,6 +615,14 @@ void GLGizmoMeasure::restore_scene_raycasters_state() } } +GLGizmoMeasure::SelectedFeatures::Item GLGizmoMeasure::item_from_feature() const +{ + const SelectedFeatures::Item item = { m_mode, + (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), + (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; + return item; +} + void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) { static std::optional last_feature; @@ -679,6 +690,14 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::Dummy(ImVec2(rect_size, rect_size)); } ); + + if (m_hover_id != -1) { + if (m_selected_features.first.feature.has_value()) { + if (m_selected_features.first == item_from_feature()) + add_strings_row_to_table(_u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + } + if (m_mode == EMode::BasicSelection && m_hover_id != -1) add_strings_row_to_table(CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); @@ -776,12 +795,12 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::EndTable(); } - if (m_selected_features.first.feature.has_value()) { - if (m_imgui->button(_u8L("Restart"))) { - m_selected_features.reset(); - m_imgui->set_requires_extra_frame(); - } - } + //if (m_selected_features.first.feature.has_value()) { + // if (m_imgui->button(_u8L("Restart"))) { + // m_selected_features.reset(); + // m_imgui->set_requires_extra_frame(); + // } + //} if (m_selected_features.second.feature.has_value()) { const Measure::MeasurementResult measure = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index d74fb9ed8e..ccd542b86a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -110,6 +110,7 @@ class GLGizmoMeasure : public GLGizmoBase void disable_scene_raycasters(); void restore_scene_raycasters_state(); + SelectedFeatures::Item item_from_feature() const; public: GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); From 67c004498c0a38bb89466007bf273d01178a2c7e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 6 Sep 2022 15:06:56 +0200 Subject: [PATCH 051/103] Follow-up of 2b7520dc9eee8063c7d376811603c2d09c4a75e4 - Use CTRL + mouse right click to restart selection --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 35 ++++++++---------------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 - 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index ebf3139868..f1a011e572 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -109,6 +109,13 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) else if (mouse_event.LeftDown()) { if (m_hover_id != -1) { m_mouse_left_down = true; + + auto item_from_feature = [this]() { + const SelectedFeatures::Item item = { m_mode, + (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), + (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; + return item; + }; if (m_selected_features.first.feature.has_value()) { const SelectedFeatures::Item item = item_from_feature(); @@ -133,15 +140,9 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) return true; } } - else if (mouse_event.RightDown()) { - if (m_hover_id != -1) { - if (m_selected_features.first.feature.has_value()) { - if (m_selected_features.first == item_from_feature()) { - m_selected_features.reset(); - m_imgui->set_requires_extra_frame(); - } - } - } + else if (mouse_event.RightDown() && mouse_event.CmdDown()) { + m_selected_features.reset(); + m_imgui->set_requires_extra_frame(); } else if (mouse_event.Leaving()) m_mouse_left_down = false; @@ -615,14 +616,6 @@ void GLGizmoMeasure::restore_scene_raycasters_state() } } -GLGizmoMeasure::SelectedFeatures::Item GLGizmoMeasure::item_from_feature() const -{ - const SelectedFeatures::Item item = { m_mode, - (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), - (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; - return item; -} - void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) { static std::optional last_feature; @@ -691,12 +684,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } ); - if (m_hover_id != -1) { - if (m_selected_features.first.feature.has_value()) { - if (m_selected_features.first == item_from_feature()) - add_strings_row_to_table(_u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - } - } + if (m_selected_features.first.feature.has_value()) + add_strings_row_to_table(CTRL_STR + "+" + _u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); if (m_mode == EMode::BasicSelection && m_hover_id != -1) add_strings_row_to_table(CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index ccd542b86a..d74fb9ed8e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -110,7 +110,6 @@ class GLGizmoMeasure : public GLGizmoBase void disable_scene_raycasters(); void restore_scene_raycasters_state(); - SelectedFeatures::Item item_from_feature() const; public: GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); From 2dd67745a5086b72e2f7ea8b0d31e129247e6378 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 7 Sep 2022 13:07:15 +0200 Subject: [PATCH 052/103] Measuring: bunch of fixes into GLGizmoMeasure + new tech ENABLE_MEASURE_GIZMO_DEBUG to show a debug imgui dialog containing data related to Measure Gizmo --- src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 222 +++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 12 +- 3 files changed, 152 insertions(+), 83 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index ebb469e509..903612f29b 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -78,6 +78,7 @@ #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) // Enable Measure Gizmo #define ENABLE_MEASURE_GIZMO (1 && ENABLE_RAYCAST_PICKING) +#define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_MEASURE_GIZMO) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index f1a011e572..b434cea9a7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -111,7 +111,7 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) m_mouse_left_down = true; auto item_from_feature = [this]() { - const SelectedFeatures::Item item = { m_mode, + const SelectedFeatures::Item item = { (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; return item; @@ -236,6 +236,10 @@ bool GLGizmoMeasure::on_is_activable() const void GLGizmoMeasure::on_render() { +#if ENABLE_MEASURE_GIZMO_DEBUG + render_debug_dialog(); +#endif // ENABLE_MEASURE_GIZMO_DEBUG + // do not render if the user is panning/rotating the 3d scene if (m_parent.is_mouse_dragging()) return; @@ -246,14 +250,14 @@ void GLGizmoMeasure::on_render() (selection.is_single_volume() || selection.is_single_volume_instance())) { update_if_needed(); - const Transform3d& model_matrix = selection.get_first_volume()->world_matrix(); + m_volume_matrix = selection.get_first_volume()->world_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); const float inv_zoom = (float)camera.get_inv_zoom(); Vec3f position_on_model; Vec3f normal_on_model; size_t model_facet_idx; - const bool mouse_on_object = m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); + const bool mouse_on_object = m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, m_volume_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); const bool is_hovering_on_locked_feature = m_mode == EMode::ExtendedSelection && m_hover_id != -1; if (m_mode == EMode::BasicSelection) { @@ -335,31 +339,31 @@ void GLGizmoMeasure::on_render() default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { - m_curr_point_on_feature_position = position_on_feature(POINT_ID, camera); + m_curr_point_on_feature_position = m_curr_feature->get_point(); break; } case Measure::SurfaceFeatureType::Edge: { - m_curr_point_on_feature_position = position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); + m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); break; } case Measure::SurfaceFeatureType::Plane: { - m_curr_point_on_feature_position = position_on_feature(PLANE_ID, camera); + m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(PLANE_ID, camera); break; } case Measure::SurfaceFeatureType::Circle: { const auto [center, radius, normal] = m_curr_feature->get_circle(); if (m_hover_id == POINT_ID) - m_curr_point_on_feature_position = model_matrix * center; + m_curr_point_on_feature_position = center; else { const float r = radius; // needed for the following lambda - m_curr_point_on_feature_position = position_on_feature(CIRCLE_ID, camera, [r](const Vec3f& v) { + m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(CIRCLE_ID, camera, [r](const Vec3f& v) { float angle = std::atan2(v.y(), v.x()); if (angle < 0.0f) angle += 2.0f * float(M_PI); - return float(r) * Vec3f(std::cos(angle), std::sin(angle), 0.0f); + return Vec3f(float(r) * std::cos(angle), float(r) * std::sin(angle), 0.0f); }); } break; @@ -411,7 +415,7 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Circle: { - const auto& [center, radius, n] = feature.get_circle(); + const auto& [center, radius, normal] = feature.get_circle(); // render center const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(center_matrix); @@ -423,7 +427,7 @@ void GLGizmoMeasure::on_render() it->second->set_transform(center_matrix); } // render circle - const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); + const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); set_matrix_uniforms(circle_matrix); m_circle.model.set_color(colors.back()); m_circle.model.render(); @@ -437,8 +441,8 @@ void GLGizmoMeasure::on_render() case Measure::SurfaceFeatureType::Edge: { const auto& [start, end] = feature.get_edge(); - auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start) * Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); set_matrix_uniforms(feature_matrix); m_cylinder.model.set_color(colors.front()); @@ -505,25 +509,23 @@ void GLGizmoMeasure::on_render() } } - render_feature(*m_curr_feature, colors, model_matrix, inv_zoom, true); + render_feature(*m_curr_feature, colors, m_volume_matrix, inv_zoom, true); } if (m_selected_features.first.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.first.feature)) { std::vector colors; colors.emplace_back(SELECTED_1ST_COLOR); - render_feature(*m_selected_features.first.feature, colors, - (m_selected_features.first.mode == EMode::BasicSelection) ? model_matrix : Transform3d::Identity(), inv_zoom, false); + render_feature(*m_selected_features.first.feature, colors, m_volume_matrix, inv_zoom, false); } if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) { std::vector colors; colors.emplace_back(SELECTED_2ND_COLOR); - render_feature(*m_selected_features.second.feature, colors, - (m_selected_features.second.mode == EMode::BasicSelection) ? model_matrix : Transform3d::Identity(), inv_zoom, false); + render_feature(*m_selected_features.second.feature, colors, m_volume_matrix, inv_zoom, false); } if (is_hovering_on_locked_feature && m_curr_point_on_feature_position.has_value()) { if (m_hover_id != POINT_ID) { - const Transform3d matrix = Geometry::translation_transform(*m_curr_point_on_feature_position) * Geometry::scale_transform(inv_zoom); + const Transform3d matrix = m_volume_matrix * Geometry::translation_transform(*m_curr_point_on_feature_position) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); m_sphere.model.set_color(hover_selection_color()); m_sphere.model.render(); @@ -616,6 +618,100 @@ void GLGizmoMeasure::restore_scene_raycasters_state() } } +static void add_row_to_table(std::function col_1 = nullptr, std::function col_2 = nullptr) +{ + assert(col_1 != nullptr && col_2 != nullptr); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + col_1(); + ImGui::TableSetColumnIndex(1); + col_2(); +}; + +static void add_strings_row_to_table(ImGuiWrapper& imgui, const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) +{ + add_row_to_table([&]() { imgui.text_colored(col_1_color, col_1); }, [&]() { imgui.text_colored(col_2_color, col_2); }); +}; + +static std::string format_double(double value) +{ + char buf[1024]; + sprintf(buf, "%.3f", value); + return std::string(buf); +}; + +static std::string format_vec3(const Vec3d& v) +{ + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); + return std::string(buf); +}; + +#if ENABLE_MEASURE_GIZMO_DEBUG +void GLGizmoMeasure::render_debug_dialog() +{ + auto add_feature_data = [this](const SelectedFeatures::Item& item) { + add_strings_row_to_table(*m_imgui, "Type", ImGuiWrapper::COL_ORANGE_LIGHT, item.source, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + switch (item.feature->get_type()) + { + case Measure::SurfaceFeatureType::Point: + { + add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(item.feature->get_point()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + auto [from, to] = item.feature->get_edge(); + add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + auto [idx, normal, origin] = item.feature->get_plane(); + add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_value", ImGuiWrapper::COL_ORANGE_LIGHT, format_double(idx), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + auto [center, radius, normal] = item.feature->get_circle(); + add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_value", ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + } + std::optional extra_point = item.feature->get_extra_point(); + if (extra_point.has_value()) + add_strings_row_to_table(*m_imgui, "m_pt3", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*extra_point), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + }; + + m_imgui->begin(_L("Measure tool debug"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + if (!m_selected_features.first.feature.has_value() && !m_selected_features.second.feature.has_value()) + m_imgui->text("Empty selection"); + else { + const ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersH; + if (m_selected_features.first.feature.has_value()) { + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Selection 1"); + if (ImGui::BeginTable("Selection 1", 2, flags)) { + add_feature_data(m_selected_features.first); + ImGui::EndTable(); + } + } + if (m_selected_features.second.feature.has_value()) { + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Selection 2"); + if (ImGui::BeginTable("Selection 2", 2, flags)) { + add_feature_data(m_selected_features.second); + ImGui::EndTable(); + } + } + } + m_imgui->end(); +} +#endif // ENABLE_MEASURE_GIZMO_DEBUG + void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) { static std::optional last_feature; @@ -640,34 +736,6 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit last_y = y; } - auto add_row_to_table = [this](std::function col_1 = nullptr, std::function col_2 = nullptr) { - assert(col_1 != nullptr && col_2 != nullptr); - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - col_1(); - ImGui::TableSetColumnIndex(1); - col_2(); - }; - - auto add_strings_row_to_table = [this, add_row_to_table](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { - add_row_to_table( - [this, &col_1, &col_1_color]() { m_imgui->text_colored(col_1_color, col_1); }, - [this, &col_2, &col_2_color]() { m_imgui->text_colored(col_2_color, col_2); } - ); - }; - - auto format_double = [](double value) { - char buf[1024]; - sprintf(buf, "%.3f", value); - return std::string(buf); - }; - - auto format_vec3 = [](const Vec3d& v) { - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); - return std::string(buf); - }; - if (ImGui::BeginTable("Commands", 2)) { add_row_to_table( [this]() { @@ -685,10 +753,10 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ); if (m_selected_features.first.feature.has_value()) - add_strings_row_to_table(CTRL_STR + "+" + _u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, CTRL_STR + "+" + _u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); if (m_mode == EMode::BasicSelection && m_hover_id != -1) - add_strings_row_to_table(CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } @@ -696,7 +764,6 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit const std::string units = use_inches ? _u8L(" (in)") : _u8L(" (mm)"); if (m_curr_feature.has_value()) { - const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); const Measure::SurfaceFeatureType feature_type = m_curr_feature->get_type(); if (m_mode == EMode::BasicSelection) { if (feature_type != Measure::SurfaceFeatureType::Undef) { @@ -708,49 +775,49 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { - Vec3d position = volume_matrix * m_curr_feature->get_point(); + Vec3d position = m_volume_matrix * m_curr_feature->get_point(); if (use_inches) position = ObjectManipulation::mm_to_in * position; - add_strings_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Edge: { auto [from, to] = m_curr_feature->get_edge(); - from = volume_matrix * from; - to = volume_matrix * to; + from = m_volume_matrix * from; + to = m_volume_matrix * to; if (use_inches) { from = ObjectManipulation::mm_to_in * from; to = ObjectManipulation::mm_to_in * to; } - add_strings_row_to_table(_u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(_u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(_u8L("Length") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Length") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Circle: { auto [center, radius, normal] = m_curr_feature->get_circle(); - center = volume_matrix * center; - normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + center = m_volume_matrix * center; + normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; if (use_inches) { center = ObjectManipulation::mm_to_in * center; radius = ObjectManipulation::mm_to_in * radius; } - add_strings_row_to_table(_u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(_u8L("Radius") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Radius") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Plane: { auto [idx, normal, origin] = m_curr_feature->get_plane(); - origin = volume_matrix * origin; - normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + origin = m_volume_matrix * origin; + normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; if (use_inches) origin = ObjectManipulation::mm_to_in * origin; - add_strings_row_to_table(_u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } } @@ -763,11 +830,10 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::Separator(); m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id) + ":"); if (ImGui::BeginTable("Data", 2)) { - Vec3d position = ObjectManipulation::mm_to_in * *m_curr_point_on_feature_position; + Vec3d position = m_volume_matrix * *m_curr_point_on_feature_position; if (use_inches) position = ObjectManipulation::mm_to_in * position; - add_strings_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), - ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } } @@ -777,9 +843,9 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::Separator(); const ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersH; if (ImGui::BeginTable("Selection", 2, flags)) { - add_strings_row_to_table(_u8L("Selection") + " 1:", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? + add_strings_row_to_table(*m_imgui, _u8L("Selection") + " 1:", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? m_selected_features.first.source : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR)); - add_strings_row_to_table(_u8L("Selection") + " 2:", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? + add_strings_row_to_table(*m_imgui, _u8L("Selection") + " 2:", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? m_selected_features.second.source : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR)); ImGui::EndTable(); } @@ -798,28 +864,28 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit m_imgui->text(_u8L("Measure") + ":"); if (ImGui::BeginTable("Measure", 2)) { if (measure.angle.has_value()) { - add_strings_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), + add_strings_row_to_table(*m_imgui, _u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_infinite.has_value()) { - double distance = ObjectManipulation::mm_to_in * *measure.distance_infinite; + double distance = *measure.distance_infinite; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_strings_row_to_table(_u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + add_strings_row_to_table(*m_imgui, _u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_strict.has_value()) { - double distance = ObjectManipulation::mm_to_in * *measure.distance_strict; + double distance = *measure.distance_strict; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_strings_row_to_table(_u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + add_strings_row_to_table(*m_imgui, _u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_xyz.has_value()) { - Vec3d distance = ObjectManipulation::mm_to_in * *measure.distance_xyz; + Vec3d distance = *measure.distance_xyz; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_strings_row_to_table(_u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), + add_strings_row_to_table(*m_imgui, _u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } ImGui::EndTable(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index d74fb9ed8e..6576787cb5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -33,12 +33,10 @@ class GLGizmoMeasure : public GLGizmoBase { struct Item { - EMode mode{ EMode::BasicSelection }; std::string source; std::optional feature; bool operator == (const Item& other) const { - if (this->mode != other.mode) return false; if (this->source != other.source) return false; return this->feature == other.feature; } @@ -48,7 +46,6 @@ class GLGizmoMeasure : public GLGizmoBase } void reset() { - mode = EMode::BasicSelection; source.clear(); feature.reset(); } @@ -62,12 +59,12 @@ class GLGizmoMeasure : public GLGizmoBase second.reset(); } - bool operator == (const SelectedFeatures& other) const { + bool operator == (const SelectedFeatures & other) const { if (this->first != other.first) return false; return this->second == other.second; } - bool operator != (const SelectedFeatures& other) const { + bool operator != (const SelectedFeatures & other) const { return !operator == (other); } }; @@ -80,6 +77,7 @@ class GLGizmoMeasure : public GLGizmoBase PickingModel m_circle; PickingModel m_plane; + Transform3d m_volume_matrix{ Transform3d::Identity() }; std::vector m_plane_models_cache; std::map> m_raycasters; std::optional m_curr_feature; @@ -111,6 +109,10 @@ class GLGizmoMeasure : public GLGizmoBase void disable_scene_raycasters(); void restore_scene_raycasters_state(); +#if ENABLE_MEASURE_GIZMO_DEBUG + void render_debug_dialog(); +#endif // ENABLE_MEASURE_GIZMO_DEBUG + public: GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); From 3d72f224e1ffda8d4aaa90514c508d9e03d20950 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 7 Sep 2022 14:04:18 +0200 Subject: [PATCH 053/103] Measuring: Gizmo measure disabled for sinking volumes --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index b434cea9a7..db9fc0dd34 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -229,9 +229,12 @@ std::string GLGizmoMeasure::on_get_name() const bool GLGizmoMeasure::on_is_activable() const { const Selection& selection = m_parent.get_selection(); - return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) ? + bool res = (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) ? selection.is_single_full_instance() : selection.is_single_volume() || selection.is_single_volume_instance(); + if (res) + res &= !selection.get_first_volume()->is_sinking(); + return res; } void GLGizmoMeasure::on_render() From f1a59de7f4c959b0fc6b09f44824ac0c980c5845 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 15 Sep 2022 12:28:16 +0200 Subject: [PATCH 054/103] Measuring: Gizmo measure shows dimensioning for distance point-point --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 146 +++++++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 8 ++ 2 files changed, 154 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index db9fc0dd34..92046c299f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -27,6 +27,9 @@ static const int EDGE_ID = 200; static const int CIRCLE_ID = 300; static const int PLANE_ID = 400; +static const float TRIANGLE_BASE = 16.0f; +static const float TRIANGLE_HEIGHT = TRIANGLE_BASE * 1.618033f; + static const std::string CTRL_STR = #ifdef __APPLE__ "⌘" @@ -537,6 +540,8 @@ void GLGizmoMeasure::on_render() shader->stop_using(); } + + render_dimensioning(); } void GLGizmoMeasure::update_if_needed() @@ -621,6 +626,147 @@ void GLGizmoMeasure::restore_scene_raycasters_state() } } +void GLGizmoMeasure::render_dimensioning() +{ + if (!m_selected_features.first.feature.has_value() || !m_selected_features.second.feature.has_value()) + return; + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + auto point_point_2D = [this, shader](const Vec3d& v1, const Vec3d& v2) { + if (v1.isApprox(v2)) + return; + + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& view_matrix = camera.get_view_matrix(); + const Transform3d& projection_matrix = camera.get_projection_matrix(); + const Matrix4d projection_view_matrix = projection_matrix.matrix() * view_matrix.matrix(); + const std::array& viewport = camera.get_viewport(); + const double inv_zoom = camera.get_inv_zoom(); + + auto model_to_world = [this](const Vec3d& model) { + return (Vec3d)(m_volume_matrix * model); + }; + + auto world_to_clip = [&projection_view_matrix](const Vec3d& world) { + return (Vec4d)(projection_view_matrix * Vec4d(world.x(), world.y(), world.z(), 1.0)); + }; + + auto clip_to_ndc = [](const Vec4d& clip) { + return Vec2d(clip.x(), clip.y()) / clip.w(); + }; + + const double half_w = 0.5 * double(viewport[2]); + const double half_h = 0.5 * double(viewport[3]); + + auto ndc_to_ss = [&viewport, half_w, half_h](const Vec2d& ndc) { + return Vec2d(half_w * ndc.x() + double(viewport[0]) + half_w, half_h * ndc.y() + double(viewport[1]) + half_h); + }; + + Matrix4d ndc_to_ss_matrix; + ndc_to_ss_matrix << half_w, 0.0, 0.0, double(viewport[0]) + half_w, + 0.0, half_h, 0.0, double(viewport[1]) + half_h, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0; + + const Transform3d ss_to_ndc_matrix = Transform3d(ndc_to_ss_matrix.inverse()); + + // ndc coordinates + const Vec2d v1ndc = clip_to_ndc(world_to_clip(model_to_world(v1))); + const Vec2d v2ndc = clip_to_ndc(world_to_clip(model_to_world(v2))); + const Vec2d v12ndc = v2ndc - v1ndc; + const double v12ndc_len = v12ndc.norm(); + + // screen coordinates + const Vec2d v1ss = ndc_to_ss(v1ndc); + const Vec2d v2ss = ndc_to_ss(v2ndc); + + if (v1ss.isApprox(v2ss)) + return; + + const Vec2d v12ss = v2ss - v1ss; + const double v12ss_len = v12ss.norm(); + + const bool overlap = v12ss_len - 2.0 * TRIANGLE_HEIGHT < 0.0; + + const auto q12ss = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Vec3d(v12ss.x(), v12ss.y(), 0.0)); + const auto q21ss = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Vec3d(-v12ss.x(), -v12ss.y(), 0.0)); + + shader->set_uniform("projection_matrix", Transform3d::Identity()); + + const Vec3d v1ss_3 = { v1ss.x(), v1ss.y(), 0.0 }; + const Vec3d v2ss_3 = { v2ss.x(), v2ss.y(), 0.0 }; + + // stem + shader->set_uniform("view_model_matrix", overlap ? + ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss * Geometry::translation_transform(-2.0 * TRIANGLE_HEIGHT * Vec3d::UnitX()) * Geometry::scale_transform({ v12ss_len + 4.0 * TRIANGLE_HEIGHT, 1.0f, 1.0f }) : + ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss * Geometry::scale_transform({ v12ss_len, 1.0f, 1.0f })); + m_dimensioning.line.render(); + + // arrow 1 + shader->set_uniform("view_model_matrix", overlap ? + ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss : + ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q21ss); + m_dimensioning.triangle.render(); + + // arrow 2 + shader->set_uniform("view_model_matrix", overlap ? + ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q21ss : + ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q12ss); + m_dimensioning.triangle.render(); + }; + + shader->start_using(); + + if (!m_dimensioning.line.is_initialized()) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA::WHITE(); + init_data.reserve_vertices(2); + init_data.reserve_indices(2); + + // vertices + init_data.add_vertex(Vec3f(0.0f, 0.0f, 0.0f)); + init_data.add_vertex(Vec3f(1.0f, 0.0f, 0.0f)); + + // indices + init_data.add_line(0, 1); + + m_dimensioning.line.init_from(std::move(init_data)); + } + + if (!m_dimensioning.triangle.is_initialized()) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA::WHITE(); + init_data.reserve_vertices(3); + init_data.reserve_indices(3); + + // vertices + init_data.add_vertex(Vec3f(0.0f, 0.0f, 0.0f)); + init_data.add_vertex(Vec3f(-TRIANGLE_HEIGHT, 0.5f * TRIANGLE_BASE, 0.0f)); + init_data.add_vertex(Vec3f(-TRIANGLE_HEIGHT, -0.5f * TRIANGLE_BASE, 0.0f)); + + // indices + init_data.add_triangle(0, 1, 2); + + m_dimensioning.triangle.init_from(std::move(init_data)); + } + + glsafe(::glDisable(GL_DEPTH_TEST)); + + if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_point_2D(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_point()); + } + + glsafe(::glEnable(GL_DEPTH_TEST)); + + shader->stop_using(); +} + static void add_row_to_table(std::function col_1 = nullptr, std::function col_2 = nullptr) { assert(col_1 != nullptr && col_2 != nullptr); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 6576787cb5..b6bd1ee5af 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -76,6 +76,12 @@ class GLGizmoMeasure : public GLGizmoBase PickingModel m_cylinder; PickingModel m_circle; PickingModel m_plane; + struct Dimensioning + { + GLModel line; + GLModel triangle; + }; + Dimensioning m_dimensioning; Transform3d m_volume_matrix{ Transform3d::Identity() }; std::vector m_plane_models_cache; @@ -109,6 +115,8 @@ class GLGizmoMeasure : public GLGizmoBase void disable_scene_raycasters(); void restore_scene_raycasters_state(); + void render_dimensioning(); + #if ENABLE_MEASURE_GIZMO_DEBUG void render_debug_dialog(); #endif // ENABLE_MEASURE_GIZMO_DEBUG From 00bcddb19d074c023a7478511a245ffb716f222e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 15 Sep 2022 14:42:04 +0200 Subject: [PATCH 055/103] Measuring: Gizmo measure shows dimensioning for distance point-edge --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 166 +++++++++++++++++------ 1 file changed, 125 insertions(+), 41 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 92046c299f..4caae10f43 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -626,6 +626,79 @@ void GLGizmoMeasure::restore_scene_raycasters_state() } } +class DimensioningHelper +{ + struct Cache + { + std::array viewport; + Matrix4d ndc_to_ss_matrix; + Transform3d ndc_to_ss_matrix_inverse; + }; + + static Cache s_cache; + +public: + static Vec3d model_to_world(const Vec3d& model, const Transform3d& world_matrix) { + return world_matrix * model; + } + + static Vec4d world_to_clip(const Vec3d& world, const Matrix4d& projection_view_matrix) { + return projection_view_matrix * Vec4d(world.x(), world.y(), world.z(), 1.0); + } + + static Vec3d clip_to_ndc(const Vec4d& clip) { + return Vec3d(clip.x(), clip.y(), clip.z()) / clip.w(); + } + + static Vec2d ndc_to_ss(const Vec3d& ndc, const std::array& viewport) { + const double half_w = 0.5 * double(viewport[2]); + const double half_h = 0.5 * double(viewport[3]); + return { half_w * ndc.x() + double(viewport[0]) + half_w, half_h * ndc.y() + double(viewport[1]) + half_h }; + }; + + static Vec4d model_to_clip(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix) { + return world_to_clip(model_to_world(model, world_matrix), projection_view_matrix); + } + + static Vec3d model_to_ndc(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix) { + return clip_to_ndc(world_to_clip(model_to_world(model, world_matrix), projection_view_matrix)); + } + + static Vec2d model_to_ss(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix, const std::array& viewport) { + return ndc_to_ss(clip_to_ndc(world_to_clip(model_to_world(model, world_matrix), projection_view_matrix)), viewport); + } + + static const Matrix4d& ndc_to_ss_matrix(const std::array& viewport) { + update(viewport); + return s_cache.ndc_to_ss_matrix; + } + + static const Transform3d ndc_to_ss_matrix_inverse(const std::array& viewport) { + update(viewport); + return s_cache.ndc_to_ss_matrix_inverse; + } + +private: + static void update(const std::array& viewport) { + if (s_cache.viewport == viewport) + return; + + std::cout << "DimensioningHelper::update()\n"; + + const double half_w = 0.5 * double(viewport[2]); + const double half_h = 0.5 * double(viewport[3]); + s_cache.ndc_to_ss_matrix << half_w, 0.0, 0.0, double(viewport[0]) + half_w, + 0.0, half_h, 0.0, double(viewport[1]) + half_h, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0; + + s_cache.ndc_to_ss_matrix_inverse = s_cache.ndc_to_ss_matrix.inverse(); + s_cache.viewport = viewport; + } +}; + +DimensioningHelper::Cache DimensioningHelper::s_cache = { { 0, 0, 0, 0 }, Matrix4d::Identity(), Transform3d::Identity() }; + void GLGizmoMeasure::render_dimensioning() { if (!m_selected_features.first.feature.has_value() || !m_selected_features.second.feature.has_value()) @@ -635,53 +708,17 @@ void GLGizmoMeasure::render_dimensioning() if (shader == nullptr) return; - auto point_point_2D = [this, shader](const Vec3d& v1, const Vec3d& v2) { + auto point_point = [this, shader](const Vec3d& v1, const Vec3d& v2) { if (v1.isApprox(v2)) return; const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d& view_matrix = camera.get_view_matrix(); - const Transform3d& projection_matrix = camera.get_projection_matrix(); - const Matrix4d projection_view_matrix = projection_matrix.matrix() * view_matrix.matrix(); + const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); const std::array& viewport = camera.get_viewport(); - const double inv_zoom = camera.get_inv_zoom(); - - auto model_to_world = [this](const Vec3d& model) { - return (Vec3d)(m_volume_matrix * model); - }; - - auto world_to_clip = [&projection_view_matrix](const Vec3d& world) { - return (Vec4d)(projection_view_matrix * Vec4d(world.x(), world.y(), world.z(), 1.0)); - }; - - auto clip_to_ndc = [](const Vec4d& clip) { - return Vec2d(clip.x(), clip.y()) / clip.w(); - }; - - const double half_w = 0.5 * double(viewport[2]); - const double half_h = 0.5 * double(viewport[3]); - - auto ndc_to_ss = [&viewport, half_w, half_h](const Vec2d& ndc) { - return Vec2d(half_w * ndc.x() + double(viewport[0]) + half_w, half_h * ndc.y() + double(viewport[1]) + half_h); - }; - - Matrix4d ndc_to_ss_matrix; - ndc_to_ss_matrix << half_w, 0.0, 0.0, double(viewport[0]) + half_w, - 0.0, half_h, 0.0, double(viewport[1]) + half_h, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0; - - const Transform3d ss_to_ndc_matrix = Transform3d(ndc_to_ss_matrix.inverse()); - - // ndc coordinates - const Vec2d v1ndc = clip_to_ndc(world_to_clip(model_to_world(v1))); - const Vec2d v2ndc = clip_to_ndc(world_to_clip(model_to_world(v2))); - const Vec2d v12ndc = v2ndc - v1ndc; - const double v12ndc_len = v12ndc.norm(); // screen coordinates - const Vec2d v1ss = ndc_to_ss(v1ndc); - const Vec2d v2ss = ndc_to_ss(v2ndc); + const Vec2d v1ss = DimensioningHelper::model_to_ss(v1, m_volume_matrix, projection_view_matrix, viewport); + const Vec2d v2ss = DimensioningHelper::model_to_ss(v2, m_volume_matrix, projection_view_matrix, viewport); if (v1ss.isApprox(v2ss)) return; @@ -699,6 +736,8 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d v1ss_3 = { v1ss.x(), v1ss.y(), 0.0 }; const Vec3d v2ss_3 = { v2ss.x(), v2ss.y(), 0.0 }; + const Transform3d ss_to_ndc_matrix = DimensioningHelper::ndc_to_ss_matrix_inverse(viewport); + // stem shader->set_uniform("view_model_matrix", overlap ? ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss * Geometry::translation_transform(-2.0 * TRIANGLE_HEIGHT * Vec3d::UnitX()) * Geometry::scale_transform({ v12ss_len + 4.0 * TRIANGLE_HEIGHT, 1.0f, 1.0f }) : @@ -718,6 +757,43 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.triangle.render(); }; + auto point_edge = [this, shader, point_point](const Vec3d& v, const std::pair& e) { + const Vec3d e1v = v - e.first; + const Vec3d e1e2 = e.second - e.first; + const Vec3d e1e2_unit = e1e2.normalized(); + const Vec3d v_proj_on_e1e2 = e.first + e1v.dot(e1e2_unit) * e1e2_unit; + point_point(v, v_proj_on_e1e2); + + const Vec3d v_proj_on_e1e2e1 = v_proj_on_e1e2 - e.first; + bool on_e1_side = v_proj_on_e1e2e1.dot(e1e2) < 0.0; + bool on_e2_side = v_proj_on_e1e2e1.norm() > e1e2.norm(); + if (on_e1_side || on_e2_side) { + const Camera& camera = wxGetApp().plater()->get_camera(); + const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); + const std::array& viewport = camera.get_viewport(); + const Transform3d ss_to_ndc_matrix = DimensioningHelper::ndc_to_ss_matrix_inverse(viewport); + + shader->set_uniform("projection_matrix", Transform3d::Identity()); + + const Vec2d v_proj_on_e1e2ss = DimensioningHelper::model_to_ss(v_proj_on_e1e2, m_volume_matrix, projection_view_matrix, viewport); + auto render_extension = [this, &v_proj_on_e1e2ss, &projection_view_matrix, &viewport, &ss_to_ndc_matrix, shader](const Vec3d& p) { + const Vec2d pss = DimensioningHelper::model_to_ss(p, m_volume_matrix, projection_view_matrix, viewport); + if (!pss.isApprox(v_proj_on_e1e2ss)) { + const Vec2d pv_proj_on_e1e2ss = v_proj_on_e1e2ss - pss; + const double pv_proj_on_e1e2ss_len = pv_proj_on_e1e2ss.norm(); + + const auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Vec3d(pv_proj_on_e1e2ss.x(), pv_proj_on_e1e2ss.y(), 0.0)); + + shader->set_uniform("view_model_matrix", ss_to_ndc_matrix * Geometry::translation_transform({ pss.x(), pss.y(), 0.0 }) * q * + Geometry::scale_transform({ pv_proj_on_e1e2ss_len, 1.0f, 1.0f })); + m_dimensioning.line.render(); + } + }; + + render_extension(on_e1_side ? e.first : e.second); + } + }; + shader->start_using(); if (!m_dimensioning.line.is_initialized()) { @@ -759,7 +835,15 @@ void GLGizmoMeasure::render_dimensioning() if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_point_2D(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_point()); + point_point(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_point()); + } + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { + point_edge(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_edge()); + } + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_edge(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_edge()); } glsafe(::glEnable(GL_DEPTH_TEST)); From bf0f7c609dd3f6c9d35fde8a4f15ef551ad8d93f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 15 Sep 2022 15:27:49 +0200 Subject: [PATCH 056/103] Measuring: Gizmo measure shows dimensioning for distance point-plane --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 45 ++++++++++++++++-------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 4caae10f43..c1e6a0c9ff 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -758,34 +758,32 @@ void GLGizmoMeasure::render_dimensioning() }; auto point_edge = [this, shader, point_point](const Vec3d& v, const std::pair& e) { - const Vec3d e1v = v - e.first; const Vec3d e1e2 = e.second - e.first; const Vec3d e1e2_unit = e1e2.normalized(); - const Vec3d v_proj_on_e1e2 = e.first + e1v.dot(e1e2_unit) * e1e2_unit; - point_point(v, v_proj_on_e1e2); + const Vec3d v_proj = e.first + e1e2_unit.dot(v - e.first) * e1e2_unit; + point_point(v, v_proj); - const Vec3d v_proj_on_e1e2e1 = v_proj_on_e1e2 - e.first; - bool on_e1_side = v_proj_on_e1e2e1.dot(e1e2) < 0.0; - bool on_e2_side = v_proj_on_e1e2e1.norm() > e1e2.norm(); + const Vec3d v_proje1 = v_proj - e.first; + bool on_e1_side = v_proje1.dot(e1e2) < 0.0; + bool on_e2_side = v_proje1.norm() > e1e2.norm(); if (on_e1_side || on_e2_side) { const Camera& camera = wxGetApp().plater()->get_camera(); const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); const std::array& viewport = camera.get_viewport(); const Transform3d ss_to_ndc_matrix = DimensioningHelper::ndc_to_ss_matrix_inverse(viewport); - shader->set_uniform("projection_matrix", Transform3d::Identity()); - - const Vec2d v_proj_on_e1e2ss = DimensioningHelper::model_to_ss(v_proj_on_e1e2, m_volume_matrix, projection_view_matrix, viewport); - auto render_extension = [this, &v_proj_on_e1e2ss, &projection_view_matrix, &viewport, &ss_to_ndc_matrix, shader](const Vec3d& p) { + const Vec2d v_projss = DimensioningHelper::model_to_ss(v_proj, m_volume_matrix, projection_view_matrix, viewport); + auto render_extension = [this, &v_projss, &projection_view_matrix, &viewport, &ss_to_ndc_matrix, shader](const Vec3d& p) { const Vec2d pss = DimensioningHelper::model_to_ss(p, m_volume_matrix, projection_view_matrix, viewport); - if (!pss.isApprox(v_proj_on_e1e2ss)) { - const Vec2d pv_proj_on_e1e2ss = v_proj_on_e1e2ss - pss; - const double pv_proj_on_e1e2ss_len = pv_proj_on_e1e2ss.norm(); + if (!pss.isApprox(v_projss)) { + const Vec2d pv_projss = v_projss - pss; + const double pv_projss_len = pv_projss.norm(); - const auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Vec3d(pv_proj_on_e1e2ss.x(), pv_proj_on_e1e2ss.y(), 0.0)); + const auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Vec3d(pv_projss.x(), pv_projss.y(), 0.0)); + shader->set_uniform("projection_matrix", Transform3d::Identity()); shader->set_uniform("view_model_matrix", ss_to_ndc_matrix * Geometry::translation_transform({ pss.x(), pss.y(), 0.0 }) * q * - Geometry::scale_transform({ pv_proj_on_e1e2ss_len, 1.0f, 1.0f })); + Geometry::scale_transform({ pv_projss_len, 1.0f, 1.0f })); m_dimensioning.line.render(); } }; @@ -794,6 +792,15 @@ void GLGizmoMeasure::render_dimensioning() } }; + auto point_plane = [this, shader, point_point](const Vec3d& v, const std::tuple& p) { + const auto& [idx, normal, origin] = p; + const double distance = normal.dot(v - origin); + if (std::abs(distance) < EPSILON) + return; + + point_point(v, v - distance * normal); + }; + shader->start_using(); if (!m_dimensioning.line.is_initialized()) { @@ -845,6 +852,14 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { point_edge(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_edge()); } + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { + point_plane(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_plane()); + } + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_plane(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_plane()); + } glsafe(::glEnable(GL_DEPTH_TEST)); From 8d98f0869dc6c6c6e3c67da4bb0670f17c7acf36 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Sep 2022 08:30:19 +0200 Subject: [PATCH 057/103] Measuring: Gizmo measure shows dimensioning for distance edge-edge --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 39 +++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index c1e6a0c9ff..d166982144 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -792,7 +792,7 @@ void GLGizmoMeasure::render_dimensioning() } }; - auto point_plane = [this, shader, point_point](const Vec3d& v, const std::tuple& p) { + auto point_plane = [shader, point_point](const Vec3d& v, const std::tuple& p) { const auto& [idx, normal, origin] = p; const double distance = normal.dot(v - origin); if (std::abs(distance) < EPSILON) @@ -801,6 +801,39 @@ void GLGizmoMeasure::render_dimensioning() point_point(v, v - distance * normal); }; + auto edge_edge = [this, shader, point_point](const std::pair& e1, const std::pair& e2) { + auto min_distance_edge_edge = [](const std::pair& e1, const std::pair& e2) { + auto distance_point_edge = [](const Vec3d& v, const std::pair& e) { + const Vec3d e1v = v - e.first; + const Vec3d e1e2_unit = (e.second - e.first).normalized(); + const Vec3d v_proj = e.first + e1e2_unit.dot(v - e.first) * e1e2_unit; + return std::make_pair((e1v - v_proj).norm(), v_proj); + }; + + std::vector> distances; + distances.emplace_back(std::make_tuple((e2.first - e1.first).norm(), e1.first, e2.first)); + distances.emplace_back(std::make_tuple((e2.second - e1.first).norm(), e1.first, e2.second)); + distances.emplace_back(std::make_tuple((e2.first - e1.second).norm(), e1.second, e2.first)); + distances.emplace_back(std::make_tuple((e2.second - e1.second).norm(), e1.second, e2.second)); + const auto dist_e11e2 = distance_point_edge(e1.first, e2); + distances.emplace_back(std::make_tuple(dist_e11e2.first, e1.first, dist_e11e2.second)); + const auto dist_e12e2 = distance_point_edge(e1.second, e2); + distances.emplace_back(std::make_tuple(dist_e12e2.first, e1.second, dist_e12e2.second)); + const auto dist_e21e1 = distance_point_edge(e2.first, e1); + distances.emplace_back(std::make_tuple(dist_e21e1.first, e2.first, dist_e21e1.second)); + const auto dist_e22e1 = distance_point_edge(e2.second, e1); + distances.emplace_back(std::make_tuple(dist_e22e1.first, e2.second, dist_e22e1.second)); + std::sort(distances.begin(), distances.end(), + [](const std::tuple& item1, const std::tuple& item2) { + return std::get<0>(item1) < std::get<0>(item2); + }); + return distances.front(); + }; + + const auto [dist, v1, v2] = min_distance_edge_edge(e1, e2); + point_point(v1, v2); + }; + shader->start_using(); if (!m_dimensioning.line.is_initialized()) { @@ -860,6 +893,10 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { point_plane(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_plane()); } + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { + edge_edge(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_edge()); + } glsafe(::glEnable(GL_DEPTH_TEST)); From 3eae55bd0669c82cf3d1bff10d4f23455f3a5d7a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Sep 2022 09:57:07 +0200 Subject: [PATCH 058/103] Measuring: Gizmo measure shows dimensioning for distance point-circle --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 122 ++++++++++++++--------- 1 file changed, 77 insertions(+), 45 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index d166982144..bc89051520 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -19,6 +19,10 @@ namespace Slic3r { namespace GUI { +using Edge = std::pair; +using Plane = std::tuple; +using Circle = std::tuple; + static const Slic3r::ColorRGBA SELECTED_1ST_COLOR = { 0.25f, 0.75f, 0.75f, 1.0f }; static const Slic3r::ColorRGBA SELECTED_2ND_COLOR = { 0.75f, 0.25f, 0.75f, 1.0f }; @@ -64,6 +68,46 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t return ret; } +static std::tuple distance_point_plane(const Vec3d& v, const Plane& p) +{ + const double distance = std::get<1>(p).dot(v - std::get<2>(p)); + return std::make_tuple(std::abs(distance), v, v - distance * std::get<1>(p)); +} + +static std::tuple distance_point_edge(const Vec3d& v, const Edge& e) +{ + const Vec3d e1v = v - e.first; + const Vec3d e1e2_unit = (e.second - e.first).normalized(); + const Vec3d v_proj = e.first + e1e2_unit.dot(e1v) * e1e2_unit; + return std::make_tuple((e1v - v_proj).norm(), v, v_proj); +} + +static std::tuple distance_point_circle(const Vec3d& v, const Circle& c) +{ + const auto& [center, radius, normal] = c; + const auto [distance, v1, v2] = distance_point_plane(v, std::make_tuple(0, normal, center)); + const Vec3d p_on_circle = center + radius * (v2 - center).normalized(); + return std::make_tuple((v - p_on_circle).norm(), v, p_on_circle); +} + +static std::tuple min_distance_edge_edge(const Edge& e1, const Edge& e2) +{ + std::vector> distances; + distances.emplace_back(std::make_tuple((e2.first - e1.first).norm(), e1.first, e2.first)); + distances.emplace_back(std::make_tuple((e2.second - e1.first).norm(), e1.first, e2.second)); + distances.emplace_back(std::make_tuple((e2.first - e1.second).norm(), e1.second, e2.first)); + distances.emplace_back(std::make_tuple((e2.second - e1.second).norm(), e1.second, e2.second)); + distances.emplace_back(distance_point_edge(e1.first, e2)); + distances.emplace_back(distance_point_edge(e1.second, e2)); + distances.emplace_back(distance_point_edge(e2.first, e1)); + distances.emplace_back(distance_point_edge(e2.second, e1)); + std::sort(distances.begin(), distances.end(), + [](const std::tuple& item1, const std::tuple& item2) { + return std::get<0>(item1) < std::get<0>(item2); + }); + return distances.front(); +} + static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) { assert(0 <= idx && idx < (int)planes_triangles.size()); @@ -757,12 +801,11 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.triangle.render(); }; - auto point_edge = [this, shader, point_point](const Vec3d& v, const std::pair& e) { - const Vec3d e1e2 = e.second - e.first; - const Vec3d e1e2_unit = e1e2.normalized(); - const Vec3d v_proj = e.first + e1e2_unit.dot(v - e.first) * e1e2_unit; - point_point(v, v_proj); + auto point_edge = [this, shader, point_point](const Vec3d& v, const Edge& e) { + const auto [distance, v1, v_proj] = distance_point_edge(v, e); + point_point(v1, v_proj); + const Vec3d e1e2 = e.second - e.first; const Vec3d v_proje1 = v_proj - e.first; bool on_e1_side = v_proje1.dot(e1e2) < 0.0; bool on_e2_side = v_proje1.norm() > e1e2.norm(); @@ -792,45 +835,18 @@ void GLGizmoMeasure::render_dimensioning() } }; - auto point_plane = [shader, point_point](const Vec3d& v, const std::tuple& p) { - const auto& [idx, normal, origin] = p; - const double distance = normal.dot(v - origin); - if (std::abs(distance) < EPSILON) - return; - - point_point(v, v - distance * normal); + auto point_plane = [point_point](const Vec3d& v, const Plane& p) { + const auto [distance, v1, v2] = distance_point_plane(v, p); + point_point(v1, v2); }; - auto edge_edge = [this, shader, point_point](const std::pair& e1, const std::pair& e2) { - auto min_distance_edge_edge = [](const std::pair& e1, const std::pair& e2) { - auto distance_point_edge = [](const Vec3d& v, const std::pair& e) { - const Vec3d e1v = v - e.first; - const Vec3d e1e2_unit = (e.second - e.first).normalized(); - const Vec3d v_proj = e.first + e1e2_unit.dot(v - e.first) * e1e2_unit; - return std::make_pair((e1v - v_proj).norm(), v_proj); - }; + auto point_circle = [point_point](const Vec3d& v, const Circle& c) { + const auto [distance, v1, v2] = distance_point_circle(v, c); + point_point(v1, v2); + }; - std::vector> distances; - distances.emplace_back(std::make_tuple((e2.first - e1.first).norm(), e1.first, e2.first)); - distances.emplace_back(std::make_tuple((e2.second - e1.first).norm(), e1.first, e2.second)); - distances.emplace_back(std::make_tuple((e2.first - e1.second).norm(), e1.second, e2.first)); - distances.emplace_back(std::make_tuple((e2.second - e1.second).norm(), e1.second, e2.second)); - const auto dist_e11e2 = distance_point_edge(e1.first, e2); - distances.emplace_back(std::make_tuple(dist_e11e2.first, e1.first, dist_e11e2.second)); - const auto dist_e12e2 = distance_point_edge(e1.second, e2); - distances.emplace_back(std::make_tuple(dist_e12e2.first, e1.second, dist_e12e2.second)); - const auto dist_e21e1 = distance_point_edge(e2.first, e1); - distances.emplace_back(std::make_tuple(dist_e21e1.first, e2.first, dist_e21e1.second)); - const auto dist_e22e1 = distance_point_edge(e2.second, e1); - distances.emplace_back(std::make_tuple(dist_e22e1.first, e2.second, dist_e22e1.second)); - std::sort(distances.begin(), distances.end(), - [](const std::tuple& item1, const std::tuple& item2) { - return std::get<0>(item1) < std::get<0>(item2); - }); - return distances.front(); - }; - - const auto [dist, v1, v2] = min_distance_edge_edge(e1, e2); + auto edge_edge = [point_point](const Edge& e1, const Edge& e2) { + const auto [distance, v1, v2] = min_distance_edge_edge(e1, e2); point_point(v1, v2); }; @@ -873,26 +889,42 @@ void GLGizmoMeasure::render_dimensioning() glsafe(::glDisable(GL_DEPTH_TEST)); + // point-point if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { point_point(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_point()); } + // point-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { point_edge(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_edge()); } - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_edge(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_edge()); - } + // point-plane else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { point_plane(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_plane()); } + // point-circle + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { + point_circle(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_circle()); + } + // edge-point + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_edge(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_edge()); + } + // plane-point else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { point_plane(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_plane()); } + // circle-point + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_circle(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_circle()); + } + // edge-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { edge_edge(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_edge()); From e5c22b5694eab16d50a5220effef0ca704b6f3ff Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Sep 2022 11:24:44 +0200 Subject: [PATCH 059/103] Measuring: Use eigen library in distance calculations for Gizmo measure --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index bc89051520..bfcef7365b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -70,23 +70,22 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t static std::tuple distance_point_plane(const Vec3d& v, const Plane& p) { - const double distance = std::get<1>(p).dot(v - std::get<2>(p)); - return std::make_tuple(std::abs(distance), v, v - distance * std::get<1>(p)); + const auto& [idx, normal, origin] = p; + const Eigen::Hyperplane plane(normal, origin); + return std::make_tuple(plane.absDistance(v), v, plane.projection(v)); } static std::tuple distance_point_edge(const Vec3d& v, const Edge& e) { - const Vec3d e1v = v - e.first; - const Vec3d e1e2_unit = (e.second - e.first).normalized(); - const Vec3d v_proj = e.first + e1e2_unit.dot(e1v) * e1e2_unit; - return std::make_tuple((e1v - v_proj).norm(), v, v_proj); + const Eigen::ParametrizedLine line(e.first, (e.second - e.first).normalized()); + return std::make_tuple(line.distance(v), v, line.projection(v)); } static std::tuple distance_point_circle(const Vec3d& v, const Circle& c) { const auto& [center, radius, normal] = c; - const auto [distance, v1, v2] = distance_point_plane(v, std::make_tuple(0, normal, center)); - const Vec3d p_on_circle = center + radius * (v2 - center).normalized(); + const Eigen::Hyperplane plane(normal, center); + const Vec3d p_on_circle = center + radius * (plane.projection(v) - center).normalized(); return std::make_tuple((v - p_on_circle).norm(), v, p_on_circle); } From 3a2a6d4a2f47f5ecfd7bbe13d97309b6d65842dc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Sep 2022 13:28:15 +0200 Subject: [PATCH 060/103] Measuring: Gizmo measure shows dimensioning for distance plane-plane --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index bfcef7365b..9e0d2f60f8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -89,6 +89,14 @@ static std::tuple distance_point_circle(const Vec3d& v, co return std::make_tuple((v - p_on_circle).norm(), v, p_on_circle); } +static std::tuple distance_plane_plane(const Plane& p1, const Plane& p2) +{ + const auto& [idx1, normal1, origin1] = p1; + const auto& [idx2, normal2, origin2] = p2; + return (std::abs(std::abs(normal1.dot(normal2)) - 1.0) < EPSILON) ? distance_point_plane(origin2, p1) : + std::make_tuple(0.0, Vec3d::Zero(), Vec3d::Zero()); +} + static std::tuple min_distance_edge_edge(const Edge& e1, const Edge& e2) { std::vector> distances; @@ -726,8 +734,6 @@ private: if (s_cache.viewport == viewport) return; - std::cout << "DimensioningHelper::update()\n"; - const double half_w = 0.5 * double(viewport[2]); const double half_h = 0.5 * double(viewport[3]); s_cache.ndc_to_ss_matrix << half_w, 0.0, 0.0, double(viewport[0]) + half_w, @@ -849,6 +855,11 @@ void GLGizmoMeasure::render_dimensioning() point_point(v1, v2); }; + auto plane_plane = [point_point](const Plane& p1, const Plane& p2) { + const auto [distance, v1, v2] = distance_plane_plane(p1, p2); + point_point(v1, v2); + }; + shader->start_using(); if (!m_dimensioning.line.is_initialized()) { @@ -928,6 +939,11 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { edge_edge(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_edge()); } + // plane-plane + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { + plane_plane(m_selected_features.first.feature->get_plane(), m_selected_features.second.feature->get_plane()); + } glsafe(::glEnable(GL_DEPTH_TEST)); From 81d28c545cc1bbb31ee618934ae3afc72f3fc668 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 19 Sep 2022 10:01:02 +0200 Subject: [PATCH 061/103] Measuring: Gizmo measure shows dimensioning for distance edge-circle --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 62 +++++++++++++++++++----- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 9e0d2f60f8..bdb0420a8c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -89,15 +89,7 @@ static std::tuple distance_point_circle(const Vec3d& v, co return std::make_tuple((v - p_on_circle).norm(), v, p_on_circle); } -static std::tuple distance_plane_plane(const Plane& p1, const Plane& p2) -{ - const auto& [idx1, normal1, origin1] = p1; - const auto& [idx2, normal2, origin2] = p2; - return (std::abs(std::abs(normal1.dot(normal2)) - 1.0) < EPSILON) ? distance_point_plane(origin2, p1) : - std::make_tuple(0.0, Vec3d::Zero(), Vec3d::Zero()); -} - -static std::tuple min_distance_edge_edge(const Edge& e1, const Edge& e2) +static std::tuple distance_edge_edge(const Edge& e1, const Edge& e2) { std::vector> distances; distances.emplace_back(std::make_tuple((e2.first - e1.first).norm(), e1.first, e2.first)); @@ -115,6 +107,37 @@ static std::tuple min_distance_edge_edge(const Edge& e1, c return distances.front(); } +static std::tuple distance_edge_circle(const Edge& e, const Circle& c) +{ + const auto& [center, radius, normal] = c; + const Vec3d e1e2_unit = (e.second - e.first).normalized(); + double dot = std::abs(e1e2_unit.dot(normal)); + + if (dot < EPSILON) { + // edge parallel to circle's plane + const Eigen::Hyperplane plane(e1e2_unit, center); + const Eigen::ParametrizedLine line(e.first, e1e2_unit); + const Vec3d inter = line.intersectionPoint(plane); + return distance_point_circle(inter, c); + } + else if (std::abs(dot - 1.0) < EPSILON) + // edge parallel to circle's normal + return distance_point_circle(e.first, c); + else { + const auto [distance1, v11, v12] = distance_point_circle(e.first, c); + const auto [distance2, v21, v22] = distance_point_circle(e.second, c); + return (distance1 <= distance2) ? std::make_tuple(distance1, v11, v12) : std::make_tuple(distance2, v21, v22); + } +} + +static std::tuple distance_plane_plane(const Plane& p1, const Plane& p2) +{ + const auto& [idx1, normal1, origin1] = p1; + const auto& [idx2, normal2, origin2] = p2; + return (std::abs(std::abs(normal1.dot(normal2)) - 1.0) < EPSILON) ? distance_point_plane(origin2, p1) : + std::make_tuple(0.0, Vec3d::Zero(), Vec3d::Zero()); +} + static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) { assert(0 <= idx && idx < (int)planes_triangles.size()); @@ -812,8 +835,8 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e1e2 = e.second - e.first; const Vec3d v_proje1 = v_proj - e.first; - bool on_e1_side = v_proje1.dot(e1e2) < 0.0; - bool on_e2_side = v_proje1.norm() > e1e2.norm(); + const bool on_e1_side = v_proje1.dot(e1e2) < 0.0; + const bool on_e2_side = v_proje1.norm() > e1e2.norm(); if (on_e1_side || on_e2_side) { const Camera& camera = wxGetApp().plater()->get_camera(); const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); @@ -851,7 +874,12 @@ void GLGizmoMeasure::render_dimensioning() }; auto edge_edge = [point_point](const Edge& e1, const Edge& e2) { - const auto [distance, v1, v2] = min_distance_edge_edge(e1, e2); + const auto [distance, v1, v2] = distance_edge_edge(e1, e2); + point_point(v1, v2); + }; + + auto edge_circle = [point_point](const Edge& e, const Circle& c) { + const auto [distance, v1, v2] = distance_edge_circle(e, c); point_point(v1, v2); }; @@ -939,11 +967,21 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { edge_edge(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_edge()); } + // edge-circle + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { + edge_circle(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_circle()); + } // plane-plane else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { plane_plane(m_selected_features.first.feature->get_plane(), m_selected_features.second.feature->get_plane()); } + // circle-edge + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { + edge_circle(m_selected_features.second.feature->get_edge(), m_selected_features.first.feature->get_circle()); + } glsafe(::glEnable(GL_DEPTH_TEST)); From c87c9886a51cdf2a670aff4dd953e1d9e37aa72e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 19 Sep 2022 11:44:03 +0200 Subject: [PATCH 062/103] Follow-up of 81d28c545cc1bbb31ee618934ae3afc72f3fc668 - Distance edge-circle calculated as in Fusion 360 --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 35 ++++++++++++------------ 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index bdb0420a8c..89396071c7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -110,24 +110,25 @@ static std::tuple distance_edge_edge(const Edge& e1, const static std::tuple distance_edge_circle(const Edge& e, const Circle& c) { const auto& [center, radius, normal] = c; - const Vec3d e1e2_unit = (e.second - e.first).normalized(); - double dot = std::abs(e1e2_unit.dot(normal)); + const Vec3d e1e2 = (e.second - e.first); + const Vec3d e1e2_unit = e1e2.normalized(); - if (dot < EPSILON) { - // edge parallel to circle's plane - const Eigen::Hyperplane plane(e1e2_unit, center); - const Eigen::ParametrizedLine line(e.first, e1e2_unit); - const Vec3d inter = line.intersectionPoint(plane); - return distance_point_circle(inter, c); - } - else if (std::abs(dot - 1.0) < EPSILON) - // edge parallel to circle's normal - return distance_point_circle(e.first, c); - else { - const auto [distance1, v11, v12] = distance_point_circle(e.first, c); - const auto [distance2, v21, v22] = distance_point_circle(e.second, c); - return (distance1 <= distance2) ? std::make_tuple(distance1, v11, v12) : std::make_tuple(distance2, v21, v22); - } + std::vector> distances; + distances.emplace_back(distance_point_circle(e.first, c)); + distances.emplace_back(distance_point_circle(e.second, c)); + + const Eigen::Hyperplane plane(e1e2_unit, center); + const Eigen::ParametrizedLine line(e.first, e1e2_unit); + const Vec3d inter = line.intersectionPoint(plane); + const Vec3d e1inter = inter - e.first; + if (e1inter.dot(e1e2) >= 0.0 && e1inter.norm() < e1e2.norm()) + distances.emplace_back(distance_point_circle(inter, c)); + + std::sort(distances.begin(), distances.end(), + [](const std::tuple& item1, const std::tuple& item2) { + return std::get<0>(item1) < std::get<0>(item2); + }); + return distances.front(); } static std::tuple distance_plane_plane(const Plane& p1, const Plane& p2) From 116644677436b51b935d4250081a42544c1b2cb4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 19 Sep 2022 13:22:09 +0200 Subject: [PATCH 063/103] Follow-up of 8d98f0869dc6c6c6e3c67da4bb0670f17c7acf36 - Distance edge-edge calculated as in Fusion 360 --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 89396071c7..5a477034ea 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -92,14 +92,22 @@ static std::tuple distance_point_circle(const Vec3d& v, co static std::tuple distance_edge_edge(const Edge& e1, const Edge& e2) { std::vector> distances; + auto add_point_edge_distance = [&distances](const Vec3d& v, const Edge& e) { + const auto [distance, v1, v2] = distance_point_edge(v, e); + const Vec3d e1e2 = e.second - e.first; + const Vec3d e1v2 = v2 - e.first; + if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) + distances.emplace_back(std::make_tuple(distance, v, v2)); + }; + distances.emplace_back(std::make_tuple((e2.first - e1.first).norm(), e1.first, e2.first)); distances.emplace_back(std::make_tuple((e2.second - e1.first).norm(), e1.first, e2.second)); distances.emplace_back(std::make_tuple((e2.first - e1.second).norm(), e1.second, e2.first)); distances.emplace_back(std::make_tuple((e2.second - e1.second).norm(), e1.second, e2.second)); - distances.emplace_back(distance_point_edge(e1.first, e2)); - distances.emplace_back(distance_point_edge(e1.second, e2)); - distances.emplace_back(distance_point_edge(e2.first, e1)); - distances.emplace_back(distance_point_edge(e2.second, e1)); + add_point_edge_distance(e1.first, e2); + add_point_edge_distance(e1.second, e2); + add_point_edge_distance(e2.first, e1); + add_point_edge_distance(e2.second, e1); std::sort(distances.begin(), distances.end(), [](const std::tuple& item1, const std::tuple& item2) { return std::get<0>(item1) < std::get<0>(item2); From a79e78cee106afe64bfca206f7719c10e9893e26 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 21 Sep 2022 09:16:49 +0200 Subject: [PATCH 064/103] Measuring: Gizmo measure shows dimensioning for angle edge-edge --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 135 ++++++++++++++++++++++- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 3 +- 2 files changed, 133 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 5a477034ea..f6a6a46ca5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -31,7 +31,7 @@ static const int EDGE_ID = 200; static const int CIRCLE_ID = 300; static const int PLANE_ID = 400; -static const float TRIANGLE_BASE = 16.0f; +static const float TRIANGLE_BASE = 10.0f; static const float TRIANGLE_HEIGHT = TRIANGLE_BASE * 1.618033f; static const std::string CTRL_STR = @@ -782,6 +782,8 @@ DimensioningHelper::Cache DimensioningHelper::s_cache = { { 0, 0, 0, 0 }, Matrix void GLGizmoMeasure::render_dimensioning() { + static SelectedFeatures last_selected_features; + if (!m_selected_features.first.feature.has_value() || !m_selected_features.second.feature.has_value()) return; @@ -844,8 +846,8 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e1e2 = e.second - e.first; const Vec3d v_proje1 = v_proj - e.first; - const bool on_e1_side = v_proje1.dot(e1e2) < 0.0; - const bool on_e2_side = v_proje1.norm() > e1e2.norm(); + const bool on_e1_side = v_proje1.dot(e1e2) < -EPSILON; + const bool on_e2_side = !on_e1_side && v_proje1.norm() > e1e2.norm(); if (on_e1_side || on_e2_side) { const Camera& camera = wxGetApp().plater()->get_camera(); const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); @@ -882,9 +884,131 @@ void GLGizmoMeasure::render_dimensioning() point_point(v1, v2); }; - auto edge_edge = [point_point](const Edge& e1, const Edge& e2) { + auto arc_edge_edge = [this, shader](Edge e1, Edge e2) { + Vec3d e1_unit = (e1.second - e1.first).normalized(); + Vec3d e2_unit = (e2.second - e2.first).normalized(); + const double dot = e1_unit.dot(e2_unit); + if (std::abs(std::abs(dot) - 1.0) < EPSILON) + // edges are parallel, return + return; + + // project edges on the plane defined by them + Vec3d normal = e1_unit.cross(e2_unit).normalized(); + const Eigen::Hyperplane plane(normal, e1.first); + Vec3d e11_proj = plane.projection(e1.first); + Vec3d e12_proj = plane.projection(e1.second); + Vec3d e21_proj = plane.projection(e2.first); + Vec3d e22_proj = plane.projection(e2.second); + + const bool coplanar = (e2.first - e21_proj).norm() < EPSILON && (e2.second - e22_proj).norm() < EPSILON; + + // rotate the plane to become the XY plane + auto qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); + auto qp_inverse = qp.inverse(); + const Vec3d e11_rot = qp * e11_proj; + const Vec3d e12_rot = qp * e12_proj; + const Vec3d e21_rot = qp * e21_proj; + const Vec3d e22_rot = qp * e22_proj; + + // discard Z + const Vec2d e11_rot_2d = Vec2d(e11_rot.x(), e11_rot.y()); + const Vec2d e12_rot_2d = Vec2d(e12_rot.x(), e12_rot.y()); + const Vec2d e21_rot_2d = Vec2d(e21_rot.x(), e21_rot.y()); + const Vec2d e22_rot_2d = Vec2d(e22_rot.x(), e22_rot.y()); + + // find intersection of edges in XY plane + const Eigen::Hyperplane e1_rot_2d_line = Eigen::Hyperplane::Through(e11_rot_2d, e12_rot_2d); + const Eigen::Hyperplane e2_rot_2d_line = Eigen::Hyperplane::Through(e21_rot_2d, e22_rot_2d); + const Vec2d center_rot_2d = e1_rot_2d_line.intersection(e2_rot_2d_line); + + // center in world coordinate + const Vec3d center = qp.inverse() * Vec3d(center_rot_2d.x(), center_rot_2d.y(), e11_rot.z()); + + // revert edges, if needed (we want them to move away from the center) + unsigned int revert_count = 0; + if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { + std::swap(e1.first, e1.second); + std::swap(e11_proj, e12_proj); + e1_unit = -e1_unit; + ++revert_count; + } + if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { + std::swap(e2.first, e2.second); + std::swap(e21_proj, e22_proj); + e2_unit = -e2_unit; + ++revert_count; + } + + if (revert_count == 1) { + normal = -normal; + qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); + qp_inverse = qp.inverse(); + } + + // arc angle + const double angle = std::acos(std::clamp(e1_unit.dot(e2_unit), -1.0, 1.0)); + // arc radius + const Vec3d e1_proj_mid = 0.5 * (e11_proj + e12_proj); + const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); + const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); + + if (!m_dimensioning.arc.is_initialized()) { + const unsigned int resolution = std::max(2, 64 * angle / double(PI)); + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA::WHITE(); + init_data.reserve_vertices(resolution + 1); + init_data.reserve_indices(resolution + 1); + + // vertices + indices + const double step = angle / double(resolution); + for (unsigned int i = 0; i <= resolution; ++i) { + const double a = step * double(i); + const Vec3d v = radius * (Eigen::Quaternion(Eigen::AngleAxisd(a, normal)) * e1_unit); + init_data.add_vertex((Vec3f)v.cast()); + init_data.add_index(i); + } + + m_dimensioning.arc.init_from(std::move(init_data)); + } + + auto render_edge_entension = [this, shader, ¢er, radius](const Edge& e, bool coplanar) { + const Vec3d e1center = center - e.first; + const Vec3d e1e2 = e.second - e.first; + const bool on_e1_side = e1center.dot(e1e2) < -EPSILON; + const bool on_e2_side = !on_e1_side && e1center.norm() > e1e2.norm(); + if (!coplanar || on_e1_side || on_e2_side) { + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + auto render_extension = [this, shader, &camera, ¢er, radius, coplanar](const Vec3d& p) { + const Vec3d centerp = p - center; + const auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), centerp.normalized()); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * q * + Geometry::scale_transform({ coplanar ? centerp.norm() : radius, 1.0f, 1.0f })); + m_dimensioning.line.render(); + }; + render_extension(on_e1_side ? e.first : e.second); + } + }; + + auto render_arc = [this, shader, ¢er]() { + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center)); + m_dimensioning.arc.render(); + }; + + render_edge_entension({ e11_proj , e12_proj }, true); + render_edge_entension({ e21_proj , e22_proj }, coplanar); + render_arc(); + }; + + auto edge_edge = [point_point, arc_edge_edge](const Edge& e1, const Edge& e2) { + // distance const auto [distance, v1, v2] = distance_edge_edge(e1, e2); point_point(v1, v2); + // arc + arc_edge_edge(e1, e2); }; auto edge_circle = [point_point](const Edge& e, const Circle& c) { @@ -934,6 +1058,9 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.triangle.init_from(std::move(init_data)); } + if (last_selected_features != m_selected_features) + m_dimensioning.arc.reset(); + glsafe(::glDisable(GL_DEPTH_TEST)); // point-point diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index b6bd1ee5af..11e861cc7c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -80,7 +80,8 @@ class GLGizmoMeasure : public GLGizmoBase { GLModel line; GLModel triangle; - }; + GLModel arc; + }; Dimensioning m_dimensioning; Transform3d m_volume_matrix{ Transform3d::Identity() }; From ba21f54afb245333dc1adeea1d03fa128af9fb54 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 23 Sep 2022 12:44:06 +0200 Subject: [PATCH 065/103] Measuring: Gizmo measure shows dimensioning for angle edge-plane --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 284 +++++++++++++++-------- 1 file changed, 181 insertions(+), 103 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index f6a6a46ca5..fc49e9883f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -75,20 +75,33 @@ static std::tuple distance_point_plane(const Vec3d& v, con return std::make_tuple(plane.absDistance(v), v, plane.projection(v)); } +static Vec3d vector_direction(const Vec3d& from, const Vec3d& to) +{ + return (to - from).normalized(); +} + +static Vec3d edge_direction(const Edge& e) +{ + return vector_direction(e.first, e.second); +} + +// returns: distance, 1st vertex, 2nd vertex static std::tuple distance_point_edge(const Vec3d& v, const Edge& e) { - const Eigen::ParametrizedLine line(e.first, (e.second - e.first).normalized()); + const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); return std::make_tuple(line.distance(v), v, line.projection(v)); } +// returns: distance, 1st vertex, 2nd vertex static std::tuple distance_point_circle(const Vec3d& v, const Circle& c) { const auto& [center, radius, normal] = c; const Eigen::Hyperplane plane(normal, center); - const Vec3d p_on_circle = center + radius * (plane.projection(v) - center).normalized(); + const Vec3d p_on_circle = center + radius * vector_direction(center, plane.projection(v)); return std::make_tuple((v - p_on_circle).norm(), v, p_on_circle); } +// returns: distance, 1st vertex, 2nd vertex static std::tuple distance_edge_edge(const Edge& e1, const Edge& e2) { std::vector> distances; @@ -115,18 +128,19 @@ static std::tuple distance_edge_edge(const Edge& e1, const return distances.front(); } +// returns: distance, 1st vertex, 2nd vertex static std::tuple distance_edge_circle(const Edge& e, const Circle& c) { const auto& [center, radius, normal] = c; const Vec3d e1e2 = (e.second - e.first); - const Vec3d e1e2_unit = e1e2.normalized(); + const Vec3d e1e2_unit = vector_direction(e.first, e.second); std::vector> distances; distances.emplace_back(distance_point_circle(e.first, c)); distances.emplace_back(distance_point_circle(e.second, c)); const Eigen::Hyperplane plane(e1e2_unit, center); - const Eigen::ParametrizedLine line(e.first, e1e2_unit); + const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); const Vec3d inter = line.intersectionPoint(plane); const Vec3d e1inter = inter - e.first; if (e1inter.dot(e1e2) >= 0.0 && e1inter.norm() < e1e2.norm()) @@ -139,6 +153,7 @@ static std::tuple distance_edge_circle(const Edge& e, cons return distances.front(); } +// returns: distance, 1st vertex, 2nd vertex static std::tuple distance_plane_plane(const Plane& p1, const Plane& p2) { const auto& [idx1, normal1, origin1] = p1; @@ -147,6 +162,71 @@ static std::tuple distance_plane_plane(const Plane& p1, co std::make_tuple(0.0, Vec3d::Zero(), Vec3d::Zero()); } +// returns: angle in rad, center of arc, radius of arc, whether or not the edges are coplanar +// After return, the edges are oriented so that they point away from their intersection point +static std::tuple angle_edge_edge(Edge& e1, Edge& e2) +{ + Vec3d e1_unit = edge_direction(e1); + Vec3d e2_unit = edge_direction(e2); + const double dot = e1_unit.dot(e2_unit); + // are edges parallel ? + if (std::abs(std::abs(dot) - 1.0) < EPSILON) + return std::make_tuple(0.0, e1.first, 0.0, true); + + // project edges on the plane defined by them + Vec3d normal = e1_unit.cross(e2_unit).normalized(); + const Eigen::Hyperplane plane(normal, e1.first); + Vec3d e11_proj = plane.projection(e1.first); + Vec3d e12_proj = plane.projection(e1.second); + Vec3d e21_proj = plane.projection(e2.first); + Vec3d e22_proj = plane.projection(e2.second); + + const bool coplanar = (e2.first - e21_proj).norm() < EPSILON && (e2.second - e22_proj).norm() < EPSILON; + + // rotate the plane to become the XY plane + auto qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); + auto qp_inverse = qp.inverse(); + const Vec3d e11_rot = qp * e11_proj; + const Vec3d e12_rot = qp * e12_proj; + const Vec3d e21_rot = qp * e21_proj; + const Vec3d e22_rot = qp * e22_proj; + + // discard Z + const Vec2d e11_rot_2d = Vec2d(e11_rot.x(), e11_rot.y()); + const Vec2d e12_rot_2d = Vec2d(e12_rot.x(), e12_rot.y()); + const Vec2d e21_rot_2d = Vec2d(e21_rot.x(), e21_rot.y()); + const Vec2d e22_rot_2d = Vec2d(e22_rot.x(), e22_rot.y()); + + // find intersection (arc center) of edges in XY plane + const Eigen::Hyperplane e1_rot_2d_line = Eigen::Hyperplane::Through(e11_rot_2d, e12_rot_2d); + const Eigen::Hyperplane e2_rot_2d_line = Eigen::Hyperplane::Through(e21_rot_2d, e22_rot_2d); + const Vec2d center_rot_2d = e1_rot_2d_line.intersection(e2_rot_2d_line); + + // arc center in original coordinate + const Vec3d center = qp_inverse * Vec3d(center_rot_2d.x(), center_rot_2d.y(), e11_rot.z()); + + // ensure the edges are pointing away from the center + if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { + std::swap(e1.first, e1.second); + std::swap(e11_proj, e12_proj); + e1_unit = -e1_unit; + } + if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { + std::swap(e2.first, e2.second); + std::swap(e21_proj, e22_proj); + e2_unit = -e2_unit; + } + + // arc angle + const double angle = std::acos(std::clamp(e1_unit.dot(e2_unit), -1.0, 1.0)); + // arc radius + const Vec3d e1_proj_mid = 0.5 * (e11_proj + e12_proj); + const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); + const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); + + return std::make_tuple(angle, center, radius, coplanar); +} + static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) { assert(0 <= idx && idx < (int)planes_triangles.size()); @@ -884,73 +964,21 @@ void GLGizmoMeasure::render_dimensioning() point_point(v1, v2); }; - auto arc_edge_edge = [this, shader](Edge e1, Edge e2) { - Vec3d e1_unit = (e1.second - e1.first).normalized(); - Vec3d e2_unit = (e2.second - e2.first).normalized(); - const double dot = e1_unit.dot(e2_unit); - if (std::abs(std::abs(dot) - 1.0) < EPSILON) - // edges are parallel, return + auto arc_edge_edge = [this, shader](const Edge& e1, const Edge& e2, const double* const force_radius = nullptr) { + Edge e1copy = e1; + Edge e2copy = e2; + const auto [angle, center, radius, coplanar] = angle_edge_edge(e1copy, e2copy); + + if (radius == 0.0) return; - // project edges on the plane defined by them - Vec3d normal = e1_unit.cross(e2_unit).normalized(); - const Eigen::Hyperplane plane(normal, e1.first); - Vec3d e11_proj = plane.projection(e1.first); - Vec3d e12_proj = plane.projection(e1.second); - Vec3d e21_proj = plane.projection(e2.first); - Vec3d e22_proj = plane.projection(e2.second); + assert(force_radius == nullptr || *force_radius > 0.0); - const bool coplanar = (e2.first - e21_proj).norm() < EPSILON && (e2.second - e22_proj).norm() < EPSILON; + const double draw_radius = (force_radius != nullptr) ? *force_radius : radius; - // rotate the plane to become the XY plane - auto qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); - auto qp_inverse = qp.inverse(); - const Vec3d e11_rot = qp * e11_proj; - const Vec3d e12_rot = qp * e12_proj; - const Vec3d e21_rot = qp * e21_proj; - const Vec3d e22_rot = qp * e22_proj; - - // discard Z - const Vec2d e11_rot_2d = Vec2d(e11_rot.x(), e11_rot.y()); - const Vec2d e12_rot_2d = Vec2d(e12_rot.x(), e12_rot.y()); - const Vec2d e21_rot_2d = Vec2d(e21_rot.x(), e21_rot.y()); - const Vec2d e22_rot_2d = Vec2d(e22_rot.x(), e22_rot.y()); - - // find intersection of edges in XY plane - const Eigen::Hyperplane e1_rot_2d_line = Eigen::Hyperplane::Through(e11_rot_2d, e12_rot_2d); - const Eigen::Hyperplane e2_rot_2d_line = Eigen::Hyperplane::Through(e21_rot_2d, e22_rot_2d); - const Vec2d center_rot_2d = e1_rot_2d_line.intersection(e2_rot_2d_line); - - // center in world coordinate - const Vec3d center = qp.inverse() * Vec3d(center_rot_2d.x(), center_rot_2d.y(), e11_rot.z()); - - // revert edges, if needed (we want them to move away from the center) - unsigned int revert_count = 0; - if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { - std::swap(e1.first, e1.second); - std::swap(e11_proj, e12_proj); - e1_unit = -e1_unit; - ++revert_count; - } - if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { - std::swap(e2.first, e2.second); - std::swap(e21_proj, e22_proj); - e2_unit = -e2_unit; - ++revert_count; - } - - if (revert_count == 1) { - normal = -normal; - qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); - qp_inverse = qp.inverse(); - } - - // arc angle - const double angle = std::acos(std::clamp(e1_unit.dot(e2_unit), -1.0, 1.0)); - // arc radius - const Vec3d e1_proj_mid = 0.5 * (e11_proj + e12_proj); - const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); - const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); + const Vec3d e1_unit = edge_direction(e1copy); + const Vec3d e2_unit = edge_direction(e2copy); + const Vec3d normal = e1_unit.cross(e2_unit).normalized(); if (!m_dimensioning.arc.is_initialized()) { const unsigned int resolution = std::max(2, 64 * angle / double(PI)); @@ -964,7 +992,7 @@ void GLGizmoMeasure::render_dimensioning() const double step = angle / double(resolution); for (unsigned int i = 0; i <= resolution; ++i) { const double a = step * double(i); - const Vec3d v = radius * (Eigen::Quaternion(Eigen::AngleAxisd(a, normal)) * e1_unit); + const Vec3d v = draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(a, normal)) * e1_unit); init_data.add_vertex((Vec3f)v.cast()); init_data.add_index(i); } @@ -972,35 +1000,70 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.arc.init_from(std::move(init_data)); } - auto render_edge_entension = [this, shader, ¢er, radius](const Edge& e, bool coplanar) { - const Vec3d e1center = center - e.first; - const Vec3d e1e2 = e.second - e.first; - const bool on_e1_side = e1center.dot(e1e2) < -EPSILON; - const bool on_e2_side = !on_e1_side && e1center.norm() > e1e2.norm(); - if (!coplanar || on_e1_side || on_e2_side) { - const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - auto render_extension = [this, shader, &camera, ¢er, radius, coplanar](const Vec3d& p) { - const Vec3d centerp = p - center; - const auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), centerp.normalized()); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * q * - Geometry::scale_transform({ coplanar ? centerp.norm() : radius, 1.0f, 1.0f })); - m_dimensioning.line.render(); - }; - render_extension(on_e1_side ? e.first : e.second); - } - }; + // render arc + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center)); + m_dimensioning.arc.render(); - auto render_arc = [this, shader, ¢er]() { + // render edge 1 extension + const Vec3d e11e12 = e1copy.second - e1copy.first; + const Vec3d e11center = center - e1copy.first; + const double e11center_len = e11center.norm(); + if (e11center_len > EPSILON && e11center.dot(e11e12) < 0.0) { const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center)); - m_dimensioning.arc.render(); - }; + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e1copy)) * + Geometry::scale_transform({ e11center_len, 1.0f, 1.0f })); + m_dimensioning.line.render(); + } - render_edge_entension({ e11_proj , e12_proj }, true); - render_edge_entension({ e21_proj , e22_proj }, coplanar); - render_arc(); + // render edge 2 extension + const Vec3d e21center = center - e2copy.first; + const double e21center_len = e21center.norm(); + if (e21center_len > EPSILON) { + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e2copy)) * + Geometry::scale_transform({ (coplanar && (force_radius == nullptr)) ? e21center_len : draw_radius, 1.0f, 1.0f })); + m_dimensioning.line.render(); + } + }; + + auto arc_edge_plane = [this, arc_edge_edge](const Edge& e, const Plane& p) { + const auto& [idx, normal, origin] = p; + const Vec3d e1e2 = e.second - e.first; + const double abs_dot = std::abs(normal.dot(edge_direction(e))); + if (abs_dot < EPSILON || std::abs(abs_dot - 1.0) < EPSILON) + return; + + const Eigen::Hyperplane plane(normal, origin); + const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); + const Vec3d inters = line.intersectionPoint(plane); + + // ensure the edge is pointing away from the intersection + Edge ecopy = e; + Vec3d e1e2copy = e1e2; + if ((ecopy.first - inters).squaredNorm() > (ecopy.second - inters).squaredNorm()) { + std::swap(ecopy.first, ecopy.second); + e1e2copy = -e1e2copy; + } + + // calculate 2nd edge (on the plane) + const Vec3d temp = normal.cross(e1e2copy); + const Vec3d edge_on_plane_unit = normal.cross(temp).normalized(); + Edge edge_on_plane = { origin, origin + e1e2copy.norm() * edge_on_plane_unit }; + + // ensure the 2nd edge is pointing in the correct direction + const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2copy); + if (test_edge.dot(temp) < 0.0) + edge_on_plane = { origin, origin - e1e2copy.norm() * edge_on_plane_unit }; + + const Vec3d e1e2copy_mid = 0.5 * (ecopy.second + ecopy.first); + const double radius = (inters - e1e2copy_mid).norm(); + arc_edge_edge(ecopy, edge_on_plane, &radius); }; auto edge_edge = [point_point, arc_edge_edge](const Edge& e1, const Edge& e2) { @@ -1011,6 +1074,11 @@ void GLGizmoMeasure::render_dimensioning() arc_edge_edge(e1, e2); }; + auto edge_plane = [point_point, arc_edge_plane](const Edge& e, const Plane& p) { + // arc + arc_edge_plane(e, p); + }; + auto edge_circle = [point_point](const Edge& e, const Circle& c) { const auto [distance, v1, v2] = distance_edge_circle(e, c); point_point(v1, v2); @@ -1088,31 +1156,41 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { point_edge(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_edge()); } - // plane-point - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_plane(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_plane()); - } - // circle-point - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_circle(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_circle()); - } // edge-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { edge_edge(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_edge()); } + // edge-plane + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { + edge_plane(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_plane()); + } // edge-circle else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { edge_circle(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_circle()); } + // plane-point + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_plane(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_plane()); + } + // plane-edge + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { + edge_plane(m_selected_features.second.feature->get_edge(), m_selected_features.first.feature->get_plane()); + } // plane-plane else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { plane_plane(m_selected_features.first.feature->get_plane(), m_selected_features.second.feature->get_plane()); } + // circle-point + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_circle(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_circle()); + } // circle-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { From 4dd005a554439e3fd87e4a34f83f4ec821fe7e5d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 26 Sep 2022 13:17:41 +0200 Subject: [PATCH 066/103] Fixed bug in get_measurement() function --- src/libslic3r/Measure.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 1a79369a3e..c2b7edd5e2 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -518,7 +518,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& const auto& [idx2, normal2, pt2] = f2.get_plane(); double angle = 0.; - if (! normal1.isApprox(normal2)) { + if (normal1.isApprox(normal2)) { // The planes are parallel, calculate distance. Eigen::Hyperplane plane(normal1, pt1); result.distance_infinite = plane.absDistance(pt2); From b71d61f0f7b895e7f63c117b2cffd90712cbbf1d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 27 Sep 2022 10:21:57 +0200 Subject: [PATCH 067/103] Measuring - Refactoring in GLGizmoMeasure related to scene raycasters state cache --- src/slic3r/GUI/GLCanvas3D.cpp | 6 +++--- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 25 +++++++++++------------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 9 +++++++-- src/slic3r/GUI/SceneRaycaster.cpp | 25 ++++++++++++++++++++++++ src/slic3r/GUI/SceneRaycaster.hpp | 4 ++++ 5 files changed, 50 insertions(+), 19 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index b5fea57bea..ed82a3a8da 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5517,11 +5517,11 @@ void GLCanvas3D::_picking_pass() ImGui::Separator(); imgui.text("Registered for picking:"); - sprintf(buf, "Beds: %d", (int)m_scene_raycaster.beds_count()); + sprintf(buf, "Beds: %d (%d)", (int)m_scene_raycaster.beds_count(), (int)m_scene_raycaster.active_beds_count()); imgui.text(std::string(buf)); - sprintf(buf, "Volumes: %d", (int)m_scene_raycaster.volumes_count()); + sprintf(buf, "Volumes: %d (%d)", (int)m_scene_raycaster.volumes_count(), (int)m_scene_raycaster.active_volumes_count()); imgui.text(std::string(buf)); - sprintf(buf, "Gizmo elements: %d", (int)m_scene_raycaster.gizmos_count()); + sprintf(buf, "Gizmo elements: %d (%d)", (int)m_scene_raycaster.gizmos_count(), (int)m_scene_raycaster.active_gizmos_count()); imgui.text(std::string(buf)); imgui.end(); #endif // ENABLE_RAYCAST_PICKING_DEBUG diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index fc49e9883f..c511f7e501 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -372,11 +372,13 @@ void GLGizmoMeasure::on_set_state() else { m_mode = EMode::BasicSelection; // store current state of scene raycaster for later use - m_scene_raycaster_state.clear(); - m_scene_raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); - if (m_scene_raycasters != nullptr) { - for (const auto& r : *m_scene_raycasters) { - m_scene_raycaster_state.emplace_back(r->is_active()); + m_scene_raycasters.clear(); + auto scene_raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); + if (scene_raycasters != nullptr) { + m_scene_raycasters.reserve(scene_raycasters->size()); + for (auto r : *scene_raycasters) { + SceneRaycasterState state = { r, r->is_active() }; + m_scene_raycasters.emplace_back(state); } } } @@ -772,20 +774,15 @@ void GLGizmoMeasure::update_if_needed() void GLGizmoMeasure::disable_scene_raycasters() { - if (m_scene_raycasters != nullptr) { - for (auto r : *m_scene_raycasters) { - r->set_active(false); - } + for (auto r : m_scene_raycasters) { + r.raycaster->set_active(false); } } void GLGizmoMeasure::restore_scene_raycasters_state() { - if (m_scene_raycasters != nullptr) { - assert(m_scene_raycasters->size() == m_scene_raycaster_state.size()); - for (size_t i = 0; i < m_scene_raycasters->size(); ++i) { - (*m_scene_raycasters)[i]->set_active(m_scene_raycaster_state[i]); - } + for (auto r : m_scene_raycasters) { + r.raycaster->set_active(r.state); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 11e861cc7c..0e967472ae 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -89,8 +89,13 @@ class GLGizmoMeasure : public GLGizmoBase std::map> m_raycasters; std::optional m_curr_feature; std::optional m_curr_point_on_feature_position; - std::vector>* m_scene_raycasters{ nullptr }; - std::vector m_scene_raycaster_state; + struct SceneRaycasterState + { + std::shared_ptr raycaster{ nullptr }; + bool state{true}; + + }; + std::vector m_scene_raycasters; // These hold information to decide whether recalculation is necessary: std::vector m_volumes_matrices; diff --git a/src/slic3r/GUI/SceneRaycaster.cpp b/src/slic3r/GUI/SceneRaycaster.cpp index 49a59789e3..78cb59c1bc 100644 --- a/src/slic3r/GUI/SceneRaycaster.cpp +++ b/src/slic3r/GUI/SceneRaycaster.cpp @@ -173,6 +173,31 @@ void SceneRaycaster::render_hit(const Camera& camera) shader->stop_using(); } + +size_t SceneRaycaster::active_beds_count() const { + size_t count = 0; + for (const auto& b : m_bed) { + if (b->is_active()) + ++count; + } + return count; +} +size_t SceneRaycaster::active_volumes_count() const { + size_t count = 0; + for (const auto& v : m_volumes) { + if (v->is_active()) + ++count; + } + return count; +} +size_t SceneRaycaster::active_gizmos_count() const { + size_t count = 0; + for (const auto& g : m_gizmos) { + if (g->is_active()) + ++count; + } + return count; +} #endif // ENABLE_RAYCAST_PICKING_DEBUG std::vector>* SceneRaycaster::get_raycasters(EType type) diff --git a/src/slic3r/GUI/SceneRaycaster.hpp b/src/slic3r/GUI/SceneRaycaster.hpp index 8993c51a98..7acf82fe11 100644 --- a/src/slic3r/GUI/SceneRaycaster.hpp +++ b/src/slic3r/GUI/SceneRaycaster.hpp @@ -100,6 +100,10 @@ public: size_t beds_count() const { return m_bed.size(); } size_t volumes_count() const { return m_volumes.size(); } size_t gizmos_count() const { return m_gizmos.size(); } + + size_t active_beds_count() const; + size_t active_volumes_count() const; + size_t active_gizmos_count() const; #endif // ENABLE_RAYCAST_PICKING_DEBUG private: From 5b7e1c0677829bd84869818ca1e6207974394ec9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 29 Sep 2022 08:43:03 +0200 Subject: [PATCH 068/103] Measuring - GLGizmoMeasure - Allow to deselect second feature by clicking on it --- src/slic3r/GUI/GLCanvas3D.cpp | 51 ++++++++++----- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 83 +++++++++++++++++++----- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + src/slic3r/GUI/SceneRaycaster.cpp | 6 +- src/slic3r/GUI/SceneRaycaster.hpp | 3 +- 5 files changed, 108 insertions(+), 36 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ed82a3a8da..9c69003618 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5501,28 +5501,49 @@ void GLCanvas3D::_picking_pass() } default: { break; } } - char buf[1024]; + + auto add_strings_row_to_table = [&imgui](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(col_1_color, col_1.c_str()); + ImGui::TableSetColumnIndex(1); + imgui.text_colored(col_2_color, col_2.c_str()); + }; + if (hit.type != SceneRaycaster::EType::None) { - sprintf(buf, "Object ID: %d", hit.raycaster_id); - imgui.text(std::string(buf)); - sprintf(buf, "Type: %s", object_type.c_str()); - imgui.text(std::string(buf)); - sprintf(buf, "Position: %.3f, %.3f, %.3f", hit.position.x(), hit.position.y(), hit.position.z()); - imgui.text(std::string(buf)); - sprintf(buf, "Normal: %.3f, %.3f, %.3f", hit.normal.x(), hit.normal.y(), hit.normal.z()); - imgui.text(std::string(buf)); + if (ImGui::BeginTable("Hit", 2)) { + char buf[1024]; + add_strings_row_to_table("Object ID:", ImGuiWrapper::COL_ORANGE_LIGHT, + std::to_string(hit.raycaster_id), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); + add_strings_row_to_table("Type:", ImGuiWrapper::COL_ORANGE_LIGHT, + object_type, ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); + sprintf(buf, "%.3f, %.3f, %.3f", hit.position.x(), hit.position.y(), hit.position.z()); + add_strings_row_to_table("Position:", ImGuiWrapper::COL_ORANGE_LIGHT, + std::string(buf), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); + sprintf(buf, "%.3f, %.3f, %.3f", hit.normal.x(), hit.normal.y(), hit.normal.z()); + add_strings_row_to_table("Normal:", ImGuiWrapper::COL_ORANGE_LIGHT, + std::string(buf), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); + ImGui::EndTable(); + } } else imgui.text("NO HIT"); ImGui::Separator(); imgui.text("Registered for picking:"); - sprintf(buf, "Beds: %d (%d)", (int)m_scene_raycaster.beds_count(), (int)m_scene_raycaster.active_beds_count()); - imgui.text(std::string(buf)); - sprintf(buf, "Volumes: %d (%d)", (int)m_scene_raycaster.volumes_count(), (int)m_scene_raycaster.active_volumes_count()); - imgui.text(std::string(buf)); - sprintf(buf, "Gizmo elements: %d (%d)", (int)m_scene_raycaster.gizmos_count(), (int)m_scene_raycaster.active_gizmos_count()); - imgui.text(std::string(buf)); + if (ImGui::BeginTable("Counters", 2)) { + char buf[1024]; + sprintf(buf, "%d (%d)", (int)m_scene_raycaster.beds_count(), (int)m_scene_raycaster.active_beds_count()); + add_strings_row_to_table("Beds:", ImGuiWrapper::COL_ORANGE_LIGHT, + std::string(buf), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); + sprintf(buf, "%d (%d)", (int)m_scene_raycaster.volumes_count(), (int)m_scene_raycaster.active_volumes_count()); + add_strings_row_to_table("Volumes:", ImGuiWrapper::COL_ORANGE_LIGHT, + std::string(buf), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); + sprintf(buf, "%d (%d)", (int)m_scene_raycaster.gizmos_count(), (int)m_scene_raycaster.active_gizmos_count()); + add_strings_row_to_table("Gizmo elements:", ImGuiWrapper::COL_ORANGE_LIGHT, + std::string(buf), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); + ImGui::EndTable(); + } imgui.end(); #endif // ENABLE_RAYCAST_PICKING_DEBUG } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index c511f7e501..5dee42ad1e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -30,6 +30,8 @@ static const int POINT_ID = 100; static const int EDGE_ID = 200; static const int CIRCLE_ID = 300; static const int PLANE_ID = 400; +static const int SELECTION_1_ID = 501; +static const int SELECTION_2_ID = 502; static const float TRIANGLE_BASE = 10.0f; static const float TRIANGLE_HEIGHT = TRIANGLE_BASE * 1.618033f; @@ -47,11 +49,11 @@ static std::string surface_feature_type_as_string(Measure::SurfaceFeatureType ty switch (type) { default: - case Measure::SurfaceFeatureType::Undef: { return L("Undefined"); } - case Measure::SurfaceFeatureType::Point: { return L("Vertex"); } - case Measure::SurfaceFeatureType::Edge: { return L("Edge"); } - case Measure::SurfaceFeatureType::Circle: { return L("Circle"); } - case Measure::SurfaceFeatureType::Plane: { return L("Plane"); } + case Measure::SurfaceFeatureType::Undef: { return _u8L("Undefined"); } + case Measure::SurfaceFeatureType::Point: { return _u8L("Vertex"); } + case Measure::SurfaceFeatureType::Edge: { return _u8L("Edge"); } + case Measure::SurfaceFeatureType::Circle: { return _u8L("Circle"); } + case Measure::SurfaceFeatureType::Plane: { return _u8L("Plane"); } } } @@ -277,20 +279,44 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) m_mouse_left_down = true; auto item_from_feature = [this]() { - const SelectedFeatures::Item item = { - (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), - (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; + SelectedFeatures::Item item; + if (m_hover_id == SELECTION_1_ID && m_selected_features.first.feature.has_value()) + item = m_selected_features.first; + else if (m_hover_id == SELECTION_2_ID && m_selected_features.second.feature.has_value()) + item = m_selected_features.second; + else { + item = { + (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), + (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature + }; + } return item; }; if (m_selected_features.first.feature.has_value()) { - const SelectedFeatures::Item item = item_from_feature(); - if (m_selected_features.first != item) - m_selected_features.second = item; - } - else - m_selected_features.first = item_from_feature(); + auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), + [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_2_ID; }); + if (it != m_selection_raycasters.end()) + m_selection_raycasters.erase(it); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_2_ID); + const SelectedFeatures::Item item = item_from_feature(); + if (m_selected_features.first != item) { + if (m_selected_features.second == item) + m_selected_features.second.reset(); + else { + m_selected_features.second = item; + if (m_mode == EMode::ExtendedSelection) + m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_2_ID, *m_sphere.mesh_raycaster)); + } + } + } + else { + const SelectedFeatures::Item item = item_from_feature(); + m_selected_features.first = item; + if (m_mode == EMode::ExtendedSelection) + m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_1_ID, *m_sphere.mesh_raycaster)); + } return true; } @@ -308,6 +334,7 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) } else if (mouse_event.RightDown() && mouse_event.CmdDown()) { m_selected_features.reset(); + m_selection_raycasters.clear(); m_imgui->set_requires_extra_frame(); } else if (mouse_event.Leaving()) @@ -332,6 +359,7 @@ void GLGizmoMeasure::data_changed() m_last_inv_zoom = 0.0f; m_last_plane_idx = -1; m_selected_features.reset(); + m_selection_raycasters.clear(); } bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) @@ -435,7 +463,11 @@ void GLGizmoMeasure::on_render() std::optional curr_feature = mouse_on_object ? m_measuring->get_feature(model_facet_idx, position_on_model.cast()) : std::nullopt; m_curr_point_on_feature_position.reset(); if (m_curr_feature != curr_feature) { - GLGizmoMeasure::on_unregister_raycasters_for_picking(); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID); + m_raycasters.clear(); m_curr_feature = curr_feature; if (!m_curr_feature.has_value()) return; @@ -687,11 +719,23 @@ void GLGizmoMeasure::on_render() std::vector colors; colors.emplace_back(SELECTED_1ST_COLOR); render_feature(*m_selected_features.first.feature, colors, m_volume_matrix, inv_zoom, false); + if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point) { + auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), + [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_1_ID; }); + if (it != m_selection_raycasters.end()) + (*it)->set_transform(m_volume_matrix * Geometry::translation_transform(m_selected_features.first.feature->get_point()) * Geometry::scale_transform(inv_zoom)); + } } if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) { std::vector colors; colors.emplace_back(SELECTED_2ND_COLOR); render_feature(*m_selected_features.second.feature, colors, m_volume_matrix, inv_zoom, false); + if (m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), + [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_2_ID; }); + if (it != m_selection_raycasters.end()) + (*it)->set_transform(m_volume_matrix * Geometry::translation_transform(m_selected_features.second.feature->get_point()) * Geometry::scale_transform(inv_zoom)); + } } if (is_hovering_on_locked_feature && m_curr_point_on_feature_position.has_value()) { @@ -1269,7 +1313,7 @@ void GLGizmoMeasure::render_debug_dialog() add_strings_row_to_table(*m_imgui, "m_pt3", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*extra_point), ImGui::GetStyleColorVec4(ImGuiCol_Text)); }; - m_imgui->begin(_L("Measure tool debug"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + m_imgui->begin(_L("Measure tool debug"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); if (!m_selected_features.first.feature.has_value() && !m_selected_features.second.feature.has_value()) m_imgui->text("Empty selection"); else { @@ -1323,7 +1367,10 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Left mouse button")); }, [this]() { - m_imgui->text_colored(ImGui::GetStyleColorVec4(ImGuiCol_Text), (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point")); + m_imgui->text_colored(ImGui::GetStyleColorVec4(ImGuiCol_Text), + m_selected_features.second.feature.has_value() ? + ((m_mode == EMode::BasicSelection) ? _u8L("Select/Unselect feature") : _u8L("Select/Unselect point")) : + ((m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point"))); ImGui::SameLine(); const ImVec2 pos = ImGui::GetCursorScreenPos(); const float rect_size = ImGui::GetTextLineHeight(); @@ -1434,6 +1481,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit //if (m_selected_features.first.feature.has_value()) { // if (m_imgui->button(_u8L("Restart"))) { // m_selected_features.reset(); + // m_selection_raycasters.clear(); // m_imgui->set_requires_extra_frame(); // } //} @@ -1498,6 +1546,7 @@ void GLGizmoMeasure::on_unregister_raycasters_for_picking() m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); m_parent.set_raycaster_gizmos_on_top(false); m_raycasters.clear(); + m_selection_raycasters.clear(); } } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 0e967472ae..441d91c3a2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -87,6 +87,7 @@ class GLGizmoMeasure : public GLGizmoBase Transform3d m_volume_matrix{ Transform3d::Identity() }; std::vector m_plane_models_cache; std::map> m_raycasters; + std::vector> m_selection_raycasters; std::optional m_curr_feature; std::optional m_curr_point_on_feature_position; struct SceneRaycasterState diff --git a/src/slic3r/GUI/SceneRaycaster.cpp b/src/slic3r/GUI/SceneRaycaster.cpp index 78cb59c1bc..a92c622c1b 100644 --- a/src/slic3r/GUI/SceneRaycaster.cpp +++ b/src/slic3r/GUI/SceneRaycaster.cpp @@ -37,10 +37,10 @@ std::shared_ptr SceneRaycaster::add_raycaster(EType type, in const Transform3d& trafo, bool use_back_faces) { switch (type) { - case EType::Bed: { return m_bed.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } + case EType::Bed: { return m_bed.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } case EType::Volume: { return m_volumes.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } - case EType::Gizmo: { return m_gizmos.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } - default: { assert(false); return nullptr; } + case EType::Gizmo: { return m_gizmos.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } + default: { assert(false); return nullptr; } }; } diff --git a/src/slic3r/GUI/SceneRaycaster.hpp b/src/slic3r/GUI/SceneRaycaster.hpp index 7acf82fe11..4a7eaab276 100644 --- a/src/slic3r/GUI/SceneRaycaster.hpp +++ b/src/slic3r/GUI/SceneRaycaster.hpp @@ -106,9 +106,10 @@ public: size_t active_gizmos_count() const; #endif // ENABLE_RAYCAST_PICKING_DEBUG + static int decode_id(EType type, int id); + private: static int encode_id(EType type, int id); - static int decode_id(EType type, int id); static int base_id(EType type); }; From ee088bf79877b18eed1b370e9d7419f13693aa70 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 29 Sep 2022 10:19:40 +0200 Subject: [PATCH 069/103] Measuring - GLGizmoMeasure - Added option to copy to clipboard the result of measurement --- src/imgui/imconfig.h | 16 +++++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 57 ++++++++++++++++++++---- src/slic3r/GUI/ImGuiWrapper.cpp | 3 ++ 3 files changed, 68 insertions(+), 8 deletions(-) diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index f2c3ef0837..7c79058c4c 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -156,6 +156,21 @@ namespace ImGui const wchar_t InfoMarker = 0x2603; const wchar_t SliderFloatEditBtnIcon = 0x2604; const wchar_t SliderFloatEditBtnPressedIcon = 0x2605; +#if ENABLE_MEASURE_GIZMO + const wchar_t ClipboardBtnIcon = 0x2606; + const wchar_t LegendTravel = 0x2701; + const wchar_t LegendWipe = 0x2702; + const wchar_t LegendRetract = 0x2703; + const wchar_t LegendDeretract = 0x2704; + const wchar_t LegendSeams = 0x2705; + const wchar_t LegendToolChanges = 0x2706; + const wchar_t LegendColorChanges = 0x2707; + const wchar_t LegendPausePrints = 0x2708; + const wchar_t LegendCustomGCodes = 0x2709; + const wchar_t LegendCOG = 0x2710; + const wchar_t LegendShells = 0x2711; + const wchar_t LegendToolMarker = 0x2712; +#else const wchar_t LegendTravel = 0x2606; const wchar_t LegendWipe = 0x2607; const wchar_t LegendRetract = 0x2608; @@ -168,6 +183,7 @@ namespace ImGui const wchar_t LegendCOG = 0x2615; const wchar_t LegendShells = 0x2616; const wchar_t LegendToolMarker = 0x2617; +#endif // ENABLE_MEASURE_GIZMO // void MyFunction(const char* name, const MyMatrix44& v); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 5dee42ad1e..c232405019 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -10,6 +10,8 @@ #include "libslic3r/Model.hpp" #include "libslic3r/PresetBundle.hpp" +#include + #include #include @@ -1251,7 +1253,7 @@ static void add_row_to_table(std::function col_1 = nullptr, std::fun col_1(); ImGui::TableSetColumnIndex(1); col_2(); -}; +} static void add_strings_row_to_table(ImGuiWrapper& imgui, const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { @@ -1263,14 +1265,14 @@ static std::string format_double(double value) char buf[1024]; sprintf(buf, "%.3f", value); return std::string(buf); -}; +} static std::string format_vec3(const Vec3d& v) { char buf[1024]; sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); return std::string(buf); -}; +} #if ENABLE_MEASURE_GIZMO_DEBUG void GLGizmoMeasure::render_debug_dialog() @@ -1486,36 +1488,75 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit // } //} + auto add_measure_row_to_table = [this](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + m_imgui->text_colored(col_1_color, col_1); + ImGui::TableSetColumnIndex(1); + m_imgui->text_colored(col_2_color, col_2); + ImGui::TableSetColumnIndex(2); + ImGuiIO& io = ImGui::GetIO(); + const ImTextureID tex_id = io.Fonts->TexID; + assert(io.Fonts->TexWidth > 0 && io.Fonts->TexHeight > 0); + float inv_tex_w = 1.0f / float(io.Fonts->TexWidth); + float inv_tex_h = 1.0f / float(io.Fonts->TexHeight); + const ImFontAtlasCustomRect* const rect = m_imgui->GetTextureCustomRect(ImGui::ClipboardBtnIcon); + const ImVec2 size = { float(rect->Width), float(rect->Height) }; + const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h); + const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h); + ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f }); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, 1.0f }); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.25f, 0.25f, 0.25f, 1.0f }); + if (m_imgui->image_button(tex_id, size, uv0, uv1)) { + wxTheClipboard->Open(); + wxTheClipboard->SetData(new wxTextDataObject(col_1 + ": " + col_2)); + wxTheClipboard->Close(); + } + ImGui::PopStyleColor(3); + if (ImGui::IsItemHovered()) { + const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; + m_imgui->tooltip(into_u8(_L("Copy to clipboard")).c_str(), max_tooltip_width); + } + }; + if (m_selected_features.second.feature.has_value()) { const Measure::MeasurementResult measure = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); ImGui::Separator(); if (measure.has_any_data()) { m_imgui->text(_u8L("Measure") + ":"); - if (ImGui::BeginTable("Measure", 2)) { + if (ImGui::BeginTable("Measure", 3)) { if (measure.angle.has_value()) { - add_strings_row_to_table(*m_imgui, _u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), + ImGui::PushID((void*)(intptr_t)1); + add_measure_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::PopID(); } if (measure.distance_infinite.has_value()) { double distance = *measure.distance_infinite; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_strings_row_to_table(*m_imgui, _u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + ImGui::PushID((void*)(intptr_t)2); + add_measure_row_to_table(_u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::PopID(); } if (measure.distance_strict.has_value()) { double distance = *measure.distance_strict; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_strings_row_to_table(*m_imgui, _u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + ImGui::PushID((void*)(intptr_t)3); + add_measure_row_to_table(_u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::PopID(); } if (measure.distance_xyz.has_value()) { Vec3d distance = *measure.distance_xyz; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_strings_row_to_table(*m_imgui, _u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), + ImGui::PushID((void*)(intptr_t)4); + add_measure_row_to_table(_u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::PopID(); } ImGui::EndTable(); } diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index fa6e2c9031..4ce9dfefc0 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -56,6 +56,9 @@ static const std::map font_icons = { {ImGui::PreferencesHoverButton, "notification_preferences_hover"}, {ImGui::SliderFloatEditBtnIcon, "edit_button" }, {ImGui::SliderFloatEditBtnPressedIcon, "edit_button_pressed" }, +#if ENABLE_MEASURE_GIZMO + {ImGui::ClipboardBtnIcon , "copy_menu" }, +#endif // ENABLE_MEASURE_GIZMO #if ENABLE_LEGEND_TOOLBAR_ICONS {ImGui::LegendTravel , "legend_travel" }, {ImGui::LegendWipe , "legend_wipe" }, From 5ad4fafbf168631c12fd5e795eba4b7ecddaf83d Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 29 Sep 2022 11:22:23 +0200 Subject: [PATCH 070/103] Measurement: moving arrow-drawing functions from frontend to the backend (1/4) --- src/libslic3r/Measure.cpp | 24 +++-- src/libslic3r/Measure.hpp | 10 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 126 ++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 4 +- 4 files changed, 92 insertions(+), 72 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index c2b7edd5e2..a627fe62cc 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -349,7 +349,7 @@ std::optional MeasuringImpl::get_feature(size_t face_idx, const // which is needless and relatively expensive. res = get_measurement(plane.surface_features[i], point_sf); if (res.distance_strict) { // TODO: this should become an assert after all combinations are implemented. - double dist = *res.distance_strict; + double dist = res.distance_strict->dist; if (dist < feature_hover_limit && dist < min_dist) { min_dist = std::min(dist, min_dist); closest_feature_idx = i; @@ -455,8 +455,9 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& if (f1.get_type() == SurfaceFeatureType::Point) { if (f2.get_type() == SurfaceFeatureType::Point) { Vec3d diff = (f2.get_point() - f1.get_point()); - result.distance_strict = diff.norm(); + result.distance_strict = std::make_optional(DistAndPoints{diff.norm(), f1.get_point(), f2.get_point()}); result.distance_xyz = diff; + /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Edge) { const auto& [s,e] = f2.get_edge(); @@ -468,11 +469,12 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& double dist_end_sq = (proj-e).squaredNorm(); if (dist_start_sq < len_sq && dist_end_sq < len_sq) { // projection falls on the line - the strict distance is the same as infinite - result.distance_strict = std::make_optional(dist_inf); + result.distance_strict = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); // TODO } else { // the result is the closer of the endpoints - result.distance_strict = std::make_optional(std::sqrt(std::min(dist_start_sq, dist_end_sq) + dist_inf)); + bool s_is_closer = dist_start_sq < dist_end_sq; + result.distance_strict = std::make_optional(DistAndPoints{std::sqrt(std::min(dist_start_sq, dist_end_sq) + dist_inf), f1.get_point(), s_is_closer ? s : e}); } - result.distance_infinite = std::make_optional(dist_inf); + result.distance_infinite = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { // Find a plane containing normal, center and the point. @@ -482,12 +484,13 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) + (f1.get_point() - proj).squaredNorm()); - result.distance_strict = std::make_optional(dist); + const Vec3d p_on_circle = c + radius * (circle_plane.projection(f1.get_point()) - c).normalized(); + result.distance_strict = std::make_optional(DistAndPoints{dist, f1.get_point(), p_on_circle}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { const auto& [idx, normal, pt] = f2.get_plane(); Eigen::Hyperplane plane(normal, pt); - result.distance_infinite = plane.absDistance(f1.get_point()); + result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(f1.get_point()), f1.get_point(), plane.projection(f1.get_point())}); // TODO // TODO: result.distance_strict = } /////////////////////////////////////////////////////////////////////////// @@ -495,18 +498,23 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Edge) { if (f2.get_type() == SurfaceFeatureType::Edge) { + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Circle) { if (f2.get_type() == SurfaceFeatureType::Circle) { + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// @@ -521,7 +529,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& if (normal1.isApprox(normal2)) { // The planes are parallel, calculate distance. Eigen::Hyperplane plane(normal1, pt1); - result.distance_infinite = plane.absDistance(pt2); + result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(pt2), Vec3d::Zero(), Vec3d::Zero()}); } else { // Planes are not parallel, calculate angle. angle = std::acos(std::abs(normal1.dot(normal2))); diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 5d71983f0b..6b7940f2bf 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -110,12 +110,16 @@ private: }; - +struct DistAndPoints { + double dist; + Vec3d from; + Vec3d to; +}; struct MeasurementResult { std::optional angle; - std::optional distance_infinite; - std::optional distance_strict; + std::optional distance_infinite; + std::optional distance_strict; std::optional distance_xyz; bool has_any_data() const { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index c232405019..11cd651e5c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -72,13 +72,6 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t return ret; } -static std::tuple distance_point_plane(const Vec3d& v, const Plane& p) -{ - const auto& [idx, normal, origin] = p; - const Eigen::Hyperplane plane(normal, origin); - return std::make_tuple(plane.absDistance(v), v, plane.projection(v)); -} - static Vec3d vector_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); @@ -89,21 +82,13 @@ static Vec3d edge_direction(const Edge& e) return vector_direction(e.first, e.second); } -// returns: distance, 1st vertex, 2nd vertex -static std::tuple distance_point_edge(const Vec3d& v, const Edge& e) -{ - const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); - return std::make_tuple(line.distance(v), v, line.projection(v)); -} -// returns: distance, 1st vertex, 2nd vertex -static std::tuple distance_point_circle(const Vec3d& v, const Circle& c) -{ - const auto& [center, radius, normal] = c; - const Eigen::Hyperplane plane(normal, center); - const Vec3d p_on_circle = center + radius * vector_direction(center, plane.projection(v)); - return std::make_tuple((v - p_on_circle).norm(), v, p_on_circle); -} + + + + +/* + // returns: distance, 1st vertex, 2nd vertex static std::tuple distance_edge_edge(const Edge& e1, const Edge& e2) @@ -165,6 +150,34 @@ static std::tuple distance_plane_plane(const Plane& p1, co return (std::abs(std::abs(normal1.dot(normal2)) - 1.0) < EPSILON) ? distance_point_plane(origin2, p1) : std::make_tuple(0.0, Vec3d::Zero(), Vec3d::Zero()); } +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + // returns: angle in rad, center of arc, radius of arc, whether or not the edges are coplanar // After return, the edges are oriented so that they point away from their intersection point @@ -278,6 +291,9 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) } else if (mouse_event.LeftDown()) { if (m_hover_id != -1) { + SelectedFeatures selected_features_old = m_selected_features; + + m_mouse_left_down = true; auto item_from_feature = [this]() { @@ -319,6 +335,10 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) if (m_mode == EMode::ExtendedSelection) m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_1_ID, *m_sphere.mesh_raycaster)); } + + if (m_selected_features != selected_features_old && m_selected_features.second.feature.has_value()) + m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); + return true; } @@ -963,8 +983,11 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.triangle.render(); }; + auto point_edge = [this, shader, point_point](const Vec3d& v, const Edge& e) { - const auto [distance, v1, v_proj] = distance_point_edge(v, e); + const double distance = m_measurement_result.distance_infinite->dist; + const Vec3d v1 = m_measurement_result.distance_infinite->from; + const Vec3d v_proj = m_measurement_result.distance_infinite->to; point_point(v1, v_proj); const Vec3d e1e2 = e.second - e.first; @@ -997,16 +1020,7 @@ void GLGizmoMeasure::render_dimensioning() } }; - auto point_plane = [point_point](const Vec3d& v, const Plane& p) { - const auto [distance, v1, v2] = distance_point_plane(v, p); - point_point(v1, v2); - }; - - auto point_circle = [point_point](const Vec3d& v, const Circle& c) { - const auto [distance, v1, v2] = distance_point_circle(v, c); - point_point(v1, v2); - }; - +/* auto arc_edge_edge = [this, shader](const Edge& e1, const Edge& e2, const double* const force_radius = nullptr) { Edge e1copy = e1; Edge e2copy = e2; @@ -1130,7 +1144,7 @@ void GLGizmoMeasure::render_dimensioning() auto plane_plane = [point_point](const Plane& p1, const Plane& p2) { const auto [distance, v1, v2] = distance_plane_plane(p1, p2); point_point(v1, v2); - }; + };*/ shader->start_using(); @@ -1174,26 +1188,26 @@ void GLGizmoMeasure::render_dimensioning() glsafe(::glDisable(GL_DEPTH_TEST)); - // point-point - if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_point(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_point()); + // Render the arrow between the points that the backend passed: + if (m_selected_features.second.feature.has_value()) { + const Measure::DistAndPoints& dap = m_measurement_result.distance_infinite.has_value() + ? *m_measurement_result.distance_infinite + : *m_measurement_result.distance_strict; + point_point(dap.from, dap.to); } + + // Now if one of the features is an edge, draw also the extension of the edge to where the dist is measured: + // TODO... + + // point-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { point_edge(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_edge()); } - // point-plane - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { - point_plane(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_plane()); - } - // point-circle - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { - point_circle(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_circle()); - } + + + /* // edge-point else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { @@ -1214,11 +1228,6 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { edge_circle(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_circle()); } - // plane-point - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_plane(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_plane()); - } // plane-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { @@ -1229,16 +1238,12 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { plane_plane(m_selected_features.first.feature->get_plane(), m_selected_features.second.feature->get_plane()); } - // circle-point - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_circle(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_circle()); - } // circle-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { edge_circle(m_selected_features.second.feature->get_edge(), m_selected_features.first.feature->get_circle()); } +*/ glsafe(::glEnable(GL_DEPTH_TEST)); @@ -1520,7 +1525,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit }; if (m_selected_features.second.feature.has_value()) { - const Measure::MeasurementResult measure = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); + const Measure::MeasurementResult& measure = m_measurement_result; + ImGui::Separator(); if (measure.has_any_data()) { m_imgui->text(_u8L("Measure") + ":"); @@ -1532,7 +1538,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::PopID(); } if (measure.distance_infinite.has_value()) { - double distance = *measure.distance_infinite; + double distance = measure.distance_infinite->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; ImGui::PushID((void*)(intptr_t)2); @@ -1541,7 +1547,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::PopID(); } if (measure.distance_strict.has_value()) { - double distance = *measure.distance_strict; + double distance = measure.distance_strict->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; ImGui::PushID((void*)(intptr_t)3); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 441d91c3a2..a5312ebf57 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -70,7 +70,9 @@ class GLGizmoMeasure : public GLGizmoBase }; EMode m_mode{ EMode::BasicSelection }; - std::unique_ptr m_measuring; + Measure::MeasurementResult m_measurement_result; + + std::unique_ptr m_measuring; // PIMPL PickingModel m_sphere; PickingModel m_cylinder; From 05c03e54d9172b235151f6dd2c7738ccb01b16f1 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 29 Sep 2022 12:33:20 +0200 Subject: [PATCH 071/103] Measurement: moving arrow-drawing functions from frontend to the backend (2/4) --- src/libslic3r/Measure.cpp | 35 +++++++++++++++++++++++- src/libslic3r/Measure.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 2 +- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index a627fe62cc..01f48579d2 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -498,7 +498,40 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Edge) { if (f2.get_type() == SurfaceFeatureType::Edge) { - result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + + std::vector distances; + + auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair& e) { + //const auto [distance, v1, v2] = distance_point_edge(v, SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second)); + + const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second, std::optional(), 0.)); + double distance = res.distance_strict->dist; + Vec3d v1 = res.distance_strict->from; + Vec3d v2 = res.distance_strict->to; + + + const Vec3d e1e2 = e.second - e.first; + const Vec3d e1v2 = v2 - e.first; + if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) + distances.emplace_back(distance, v, v2); + }; + + std::pair e1 = f1.get_edge(); + std::pair e2 = f2.get_edge(); + + distances.emplace_back((e2.first - e1.first).norm(), e1.first, e2.first); + distances.emplace_back((e2.second - e1.first).norm(), e1.first, e2.second); + distances.emplace_back((e2.first - e1.second).norm(), e1.second, e2.first); + distances.emplace_back((e2.second - e1.second).norm(), e1.second, e2.second); + add_point_edge_distance(e1.first, e2); + add_point_edge_distance(e1.second, e2); + add_point_edge_distance(e2.first, e1); + add_point_edge_distance(e2.second, e1); + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(*it); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 6b7940f2bf..fb36b50095 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -111,6 +111,7 @@ private: struct DistAndPoints { + DistAndPoints(double dist_, Vec3d from_, Vec3d to_) : dist(dist_), from(from_), to(to_) {} double dist; Vec3d from; Vec3d to; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 11cd651e5c..222d8087a0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -90,7 +90,7 @@ static Vec3d edge_direction(const Edge& e) /* -// returns: distance, 1st vertex, 2nd vertex +// returns: distance, 1st vertex, 2nd vertexs static std::tuple distance_edge_edge(const Edge& e1, const Edge& e2) { std::vector> distances; From 699a210c31b2b6fc912236e1e9fc0df2652a3507 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 29 Sep 2022 14:50:29 +0200 Subject: [PATCH 072/103] Measurement: moving arrow-drawing functions from frontend to the backend (3/4) --- src/libslic3r/Measure.cpp | 98 ++++++- src/libslic3r/Measure.hpp | 25 +- src/libslic3r/SurfaceMesh.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 310 ++++------------------- 4 files changed, 156 insertions(+), 278 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 01f48579d2..fbf248c0f7 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -431,6 +431,67 @@ std::vector> Measuring::get_planes_triangle_indices() const +static AngleAndPoints angle_edge_edge(const std::pair& e1, const std::pair& e2) +{ + Vec3d e1_unit = (e1.second - e1.first).normalized(); + Vec3d e2_unit = (e2.second - e2.first).normalized(); + const double dot = e1_unit.dot(e2_unit); + // are edges parallel ? + if (std::abs(std::abs(dot) - 1.0) < EPSILON) + return AngleAndPoints(0.0, e1.first, Vec3d::UnitX(), Vec3d::UnitX(), 0., true); + + // project edges on the plane defined by them + Vec3d normal = e1_unit.cross(e2_unit).normalized(); + const Eigen::Hyperplane plane(normal, e1.first); + Vec3d e11_proj = plane.projection(e1.first); + Vec3d e12_proj = plane.projection(e1.second); + Vec3d e21_proj = plane.projection(e2.first); + Vec3d e22_proj = plane.projection(e2.second); + + const bool coplanar = (e2.first - e21_proj).norm() < EPSILON && (e2.second - e22_proj).norm() < EPSILON; + + // rotate the plane to become the XY plane + auto qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); + auto qp_inverse = qp.inverse(); + const Vec3d e11_rot = qp * e11_proj; + const Vec3d e12_rot = qp * e12_proj; + const Vec3d e21_rot = qp * e21_proj; + const Vec3d e22_rot = qp * e22_proj; + + // discard Z + const Vec2d e11_rot_2d = Vec2d(e11_rot.x(), e11_rot.y()); + const Vec2d e12_rot_2d = Vec2d(e12_rot.x(), e12_rot.y()); + const Vec2d e21_rot_2d = Vec2d(e21_rot.x(), e21_rot.y()); + const Vec2d e22_rot_2d = Vec2d(e22_rot.x(), e22_rot.y()); + + // find intersection (arc center) of edges in XY plane + const Eigen::Hyperplane e1_rot_2d_line = Eigen::Hyperplane::Through(e11_rot_2d, e12_rot_2d); + const Eigen::Hyperplane e2_rot_2d_line = Eigen::Hyperplane::Through(e21_rot_2d, e22_rot_2d); + const Vec2d center_rot_2d = e1_rot_2d_line.intersection(e2_rot_2d_line); + + // arc center in original coordinate + const Vec3d center = qp_inverse * Vec3d(center_rot_2d.x(), center_rot_2d.y(), e11_rot.z()); + + // ensure the edges are pointing away from the center + if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { + std::swap(e11_proj, e12_proj); + e1_unit = -e1_unit; + } + if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { + std::swap(e21_proj, e22_proj); + e2_unit = -e2_unit; + } + + // arc angle + const double angle = std::acos(std::clamp(e1_unit.dot(e2_unit), -1.0, 1.0)); + // arc radius + const Vec3d e1_proj_mid = 0.5 * (e11_proj + e12_proj); + const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); + const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); + + return AngleAndPoints(angle, center, e1_unit, e2_unit, radius, coplanar); +} + @@ -469,12 +530,12 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& double dist_end_sq = (proj-e).squaredNorm(); if (dist_start_sq < len_sq && dist_end_sq < len_sq) { // projection falls on the line - the strict distance is the same as infinite - result.distance_strict = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); // TODO + result.distance_strict = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); } else { // the result is the closer of the endpoints bool s_is_closer = dist_start_sq < dist_end_sq; result.distance_strict = std::make_optional(DistAndPoints{std::sqrt(std::min(dist_start_sq, dist_end_sq) + dist_inf), f1.get_point(), s_is_closer ? s : e}); } - result.distance_infinite = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); // TODO + result.distance_infinite = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { // Find a plane containing normal, center and the point. @@ -498,18 +559,13 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Edge) { if (f2.get_type() == SurfaceFeatureType::Edge) { - std::vector distances; auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair& e) { - //const auto [distance, v1, v2] = distance_point_edge(v, SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second)); - const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second, std::optional(), 0.)); double distance = res.distance_strict->dist; - Vec3d v1 = res.distance_strict->from; Vec3d v2 = res.distance_strict->to; - const Vec3d e1e2 = e.second - e.first; const Vec3d e1v2 = v2 - e.first; if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) @@ -531,10 +587,32 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& [](const DistAndPoints& item1, const DistAndPoints& item2) { return item1.dist < item2.dist; }); - result.distance_infinite = std::make_optional(*it); // TODO + result.distance_infinite = std::make_optional(*it); + + result.angle = angle_edge_edge(f1.get_edge(), f2.get_edge()); /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { - result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + const std::pair e = f1.get_edge(); + const auto& [center, radius, normal] = f2.get_circle(); + const Vec3d e1e2 = (e.second - e.first); + const Vec3d e1e2_unit = (e.second - e.first).normalized(); + + std::vector distances; + distances.emplace_back(*get_measurement(SurfaceFeature(e.first), f2).distance_strict); + distances.emplace_back(*get_measurement(SurfaceFeature(e.second), f2).distance_strict); + + const Eigen::Hyperplane plane(e1e2_unit, center); + const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); + const Vec3d inter = line.intersectionPoint(plane); + const Vec3d e1inter = inter - e.first; + if (e1inter.dot(e1e2) >= 0.0 && e1inter.norm() < e1e2.norm()) + distances.emplace_back(*get_measurement(SurfaceFeature(inter), f2).distance_strict); + + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(DistAndPoints{it->dist, it->from, it->to}); /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO @@ -567,7 +645,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& // Planes are not parallel, calculate angle. angle = std::acos(std::abs(normal1.dot(normal2))); } - result.angle = angle; + result.angle = std::make_optional(AngleAndPoints(angle, Vec3d::Zero(), Vec3d::UnitX(), Vec3d::UnitX(), 0., false)); // TODO } diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index fb36b50095..b88411a9f6 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -15,12 +15,12 @@ namespace Slic3r { namespace Measure { -enum class SurfaceFeatureType { - Undef, - Point, - Edge, - Circle, - Plane +enum class SurfaceFeatureType : int { + Undef = 0, + Point = 1 << 0, + Edge = 1 << 1, + Circle = 1 << 2, + Plane = 1 << 3 }; class SurfaceFeature { @@ -117,8 +117,19 @@ struct DistAndPoints { Vec3d to; }; +struct AngleAndPoints { + AngleAndPoints(double angle_, Vec3d center_, Vec3d e1_, Vec3d e2_, double radius_, bool coplanar_) + : angle(angle_), center(center_), e1(e1_), e2(e2_), radius(radius_), coplanar(coplanar_) {} + double angle; + Vec3d center; + Vec3d e1; + Vec3d e2; + double radius; + bool coplanar; +}; + struct MeasurementResult { - std::optional angle; + std::optional angle; std::optional distance_infinite; std::optional distance_strict; std::optional distance_xyz; diff --git a/src/libslic3r/SurfaceMesh.hpp b/src/libslic3r/SurfaceMesh.hpp index a4b261ceb9..9e547eec49 100644 --- a/src/libslic3r/SurfaceMesh.hpp +++ b/src/libslic3r/SurfaceMesh.hpp @@ -2,6 +2,7 @@ #define slic3r_SurfaceMesh_hpp_ #include +#include namespace Slic3r { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 222d8087a0..e9fbd7f8c0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -16,15 +16,13 @@ #include +#include + #if ENABLE_MEASURE_GIZMO namespace Slic3r { namespace GUI { -using Edge = std::pair; -using Plane = std::tuple; -using Circle = std::tuple; - static const Slic3r::ColorRGBA SELECTED_1ST_COLOR = { 0.25f, 0.75f, 0.75f, 1.0f }; static const Slic3r::ColorRGBA SELECTED_2ND_COLOR = { 0.75f, 0.25f, 0.75f, 1.0f }; @@ -72,176 +70,9 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t return ret; } -static Vec3d vector_direction(const Vec3d& from, const Vec3d& to) +static Vec3d edge_direction(const std::pair& e) { - return (to - from).normalized(); -} - -static Vec3d edge_direction(const Edge& e) -{ - return vector_direction(e.first, e.second); -} - - - - - - -/* - - -// returns: distance, 1st vertex, 2nd vertexs -static std::tuple distance_edge_edge(const Edge& e1, const Edge& e2) -{ - std::vector> distances; - auto add_point_edge_distance = [&distances](const Vec3d& v, const Edge& e) { - const auto [distance, v1, v2] = distance_point_edge(v, e); - const Vec3d e1e2 = e.second - e.first; - const Vec3d e1v2 = v2 - e.first; - if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) - distances.emplace_back(std::make_tuple(distance, v, v2)); - }; - - distances.emplace_back(std::make_tuple((e2.first - e1.first).norm(), e1.first, e2.first)); - distances.emplace_back(std::make_tuple((e2.second - e1.first).norm(), e1.first, e2.second)); - distances.emplace_back(std::make_tuple((e2.first - e1.second).norm(), e1.second, e2.first)); - distances.emplace_back(std::make_tuple((e2.second - e1.second).norm(), e1.second, e2.second)); - add_point_edge_distance(e1.first, e2); - add_point_edge_distance(e1.second, e2); - add_point_edge_distance(e2.first, e1); - add_point_edge_distance(e2.second, e1); - std::sort(distances.begin(), distances.end(), - [](const std::tuple& item1, const std::tuple& item2) { - return std::get<0>(item1) < std::get<0>(item2); - }); - return distances.front(); -} - -// returns: distance, 1st vertex, 2nd vertex -static std::tuple distance_edge_circle(const Edge& e, const Circle& c) -{ - const auto& [center, radius, normal] = c; - const Vec3d e1e2 = (e.second - e.first); - const Vec3d e1e2_unit = vector_direction(e.first, e.second); - - std::vector> distances; - distances.emplace_back(distance_point_circle(e.first, c)); - distances.emplace_back(distance_point_circle(e.second, c)); - - const Eigen::Hyperplane plane(e1e2_unit, center); - const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); - const Vec3d inter = line.intersectionPoint(plane); - const Vec3d e1inter = inter - e.first; - if (e1inter.dot(e1e2) >= 0.0 && e1inter.norm() < e1e2.norm()) - distances.emplace_back(distance_point_circle(inter, c)); - - std::sort(distances.begin(), distances.end(), - [](const std::tuple& item1, const std::tuple& item2) { - return std::get<0>(item1) < std::get<0>(item2); - }); - return distances.front(); -} - -// returns: distance, 1st vertex, 2nd vertex -static std::tuple distance_plane_plane(const Plane& p1, const Plane& p2) -{ - const auto& [idx1, normal1, origin1] = p1; - const auto& [idx2, normal2, origin2] = p2; - return (std::abs(std::abs(normal1.dot(normal2)) - 1.0) < EPSILON) ? distance_point_plane(origin2, p1) : - std::make_tuple(0.0, Vec3d::Zero(), Vec3d::Zero()); -} -*/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// returns: angle in rad, center of arc, radius of arc, whether or not the edges are coplanar -// After return, the edges are oriented so that they point away from their intersection point -static std::tuple angle_edge_edge(Edge& e1, Edge& e2) -{ - Vec3d e1_unit = edge_direction(e1); - Vec3d e2_unit = edge_direction(e2); - const double dot = e1_unit.dot(e2_unit); - // are edges parallel ? - if (std::abs(std::abs(dot) - 1.0) < EPSILON) - return std::make_tuple(0.0, e1.first, 0.0, true); - - // project edges on the plane defined by them - Vec3d normal = e1_unit.cross(e2_unit).normalized(); - const Eigen::Hyperplane plane(normal, e1.first); - Vec3d e11_proj = plane.projection(e1.first); - Vec3d e12_proj = plane.projection(e1.second); - Vec3d e21_proj = plane.projection(e2.first); - Vec3d e22_proj = plane.projection(e2.second); - - const bool coplanar = (e2.first - e21_proj).norm() < EPSILON && (e2.second - e22_proj).norm() < EPSILON; - - // rotate the plane to become the XY plane - auto qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); - auto qp_inverse = qp.inverse(); - const Vec3d e11_rot = qp * e11_proj; - const Vec3d e12_rot = qp * e12_proj; - const Vec3d e21_rot = qp * e21_proj; - const Vec3d e22_rot = qp * e22_proj; - - // discard Z - const Vec2d e11_rot_2d = Vec2d(e11_rot.x(), e11_rot.y()); - const Vec2d e12_rot_2d = Vec2d(e12_rot.x(), e12_rot.y()); - const Vec2d e21_rot_2d = Vec2d(e21_rot.x(), e21_rot.y()); - const Vec2d e22_rot_2d = Vec2d(e22_rot.x(), e22_rot.y()); - - // find intersection (arc center) of edges in XY plane - const Eigen::Hyperplane e1_rot_2d_line = Eigen::Hyperplane::Through(e11_rot_2d, e12_rot_2d); - const Eigen::Hyperplane e2_rot_2d_line = Eigen::Hyperplane::Through(e21_rot_2d, e22_rot_2d); - const Vec2d center_rot_2d = e1_rot_2d_line.intersection(e2_rot_2d_line); - - // arc center in original coordinate - const Vec3d center = qp_inverse * Vec3d(center_rot_2d.x(), center_rot_2d.y(), e11_rot.z()); - - // ensure the edges are pointing away from the center - if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { - std::swap(e1.first, e1.second); - std::swap(e11_proj, e12_proj); - e1_unit = -e1_unit; - } - if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { - std::swap(e2.first, e2.second); - std::swap(e21_proj, e22_proj); - e2_unit = -e2_unit; - } - - // arc angle - const double angle = std::acos(std::clamp(e1_unit.dot(e2_unit), -1.0, 1.0)); - // arc radius - const Vec3d e1_proj_mid = 0.5 * (e11_proj + e12_proj); - const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); - const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); - - return std::make_tuple(angle, center, radius, coplanar); + return (e.second - e.first).normalized(); } static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) @@ -984,11 +815,9 @@ void GLGizmoMeasure::render_dimensioning() }; - auto point_edge = [this, shader, point_point](const Vec3d& v, const Edge& e) { - const double distance = m_measurement_result.distance_infinite->dist; - const Vec3d v1 = m_measurement_result.distance_infinite->from; + auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); const Vec3d v_proj = m_measurement_result.distance_infinite->to; - point_point(v1, v_proj); const Vec3d e1e2 = e.second - e.first; const Vec3d v_proje1 = v_proj - e.first; @@ -1020,21 +849,30 @@ void GLGizmoMeasure::render_dimensioning() } }; -/* - auto arc_edge_edge = [this, shader](const Edge& e1, const Edge& e2, const double* const force_radius = nullptr) { - Edge e1copy = e1; - Edge e2copy = e2; - const auto [angle, center, radius, coplanar] = angle_edge_edge(e1copy, e2copy); + auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, double* const force_radius = nullptr) { + Measure::MeasurementResult res = Measure::get_measurement(f1, f2); + const double angle = res.angle->angle; + const Vec3d center = res.angle->center; + const Vec3d e1_unit = res.angle->e1; + const Vec3d e2_unit = res.angle->e2; + const double radius = res.angle->radius; + const bool coplanar = res.angle->coplanar; - if (radius == 0.0) + std::pair e1 = m_selected_features.first.feature->get_edge(); + std::pair e2 = m_selected_features.second.feature->get_edge(); + + if ((e1.second - e1.first).dot(e1_unit) < 0.) + std::swap(e1.first, e1.second); + if ((e2.second - e2.first).dot(e2_unit) < 0.) + std::swap(e2.first, e2.second); + + if (radius == 0.) return; assert(force_radius == nullptr || *force_radius > 0.0); - const double draw_radius = (force_radius != nullptr) ? *force_radius : radius; + double draw_radius = force_radius ? *force_radius : radius; - const Vec3d e1_unit = edge_direction(e1copy); - const Vec3d e2_unit = edge_direction(e2copy); const Vec3d normal = e1_unit.cross(e2_unit).normalized(); if (!m_dimensioning.arc.is_initialized()) { @@ -1064,31 +902,33 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.arc.render(); // render edge 1 extension - const Vec3d e11e12 = e1copy.second - e1copy.first; - const Vec3d e11center = center - e1copy.first; + const Vec3d e11e12 = e1.second - e1.first; + const Vec3d e11center = center - e1.first; const double e11center_len = e11center.norm(); if (e11center_len > EPSILON && e11center.dot(e11e12) < 0.0) { const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e1copy)) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e1)) * Geometry::scale_transform({ e11center_len, 1.0f, 1.0f })); m_dimensioning.line.render(); } // render edge 2 extension - const Vec3d e21center = center - e2copy.first; + const Vec3d e21center = center - e2.first; const double e21center_len = e21center.norm(); if (e21center_len > EPSILON) { const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e2copy)) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e2)) * Geometry::scale_transform({ (coplanar && (force_radius == nullptr)) ? e21center_len : draw_radius, 1.0f, 1.0f })); m_dimensioning.line.render(); } }; + +/* auto arc_edge_plane = [this, arc_edge_edge](const Edge& e, const Plane& p) { const auto& [idx, normal, origin] = p; const Vec3d e1e2 = e.second - e.first; @@ -1122,29 +962,7 @@ void GLGizmoMeasure::render_dimensioning() const double radius = (inters - e1e2copy_mid).norm(); arc_edge_edge(ecopy, edge_on_plane, &radius); }; - - auto edge_edge = [point_point, arc_edge_edge](const Edge& e1, const Edge& e2) { - // distance - const auto [distance, v1, v2] = distance_edge_edge(e1, e2); - point_point(v1, v2); - // arc - arc_edge_edge(e1, e2); - }; - - auto edge_plane = [point_point, arc_edge_plane](const Edge& e, const Plane& p) { - // arc - arc_edge_plane(e, p); - }; - - auto edge_circle = [point_point](const Edge& e, const Circle& c) { - const auto [distance, v1, v2] = distance_edge_circle(e, c); - point_point(v1, v2); - }; - - auto plane_plane = [point_point](const Plane& p1, const Plane& p2) { - const auto [distance, v1, v2] = distance_plane_plane(p1, p2); - point_point(v1, v2); - };*/ +*/ shader->start_using(); @@ -1188,63 +1006,33 @@ void GLGizmoMeasure::render_dimensioning() glsafe(::glDisable(GL_DEPTH_TEST)); - // Render the arrow between the points that the backend passed: if (m_selected_features.second.feature.has_value()) { + // Render the arrow between the points that the backend passed: const Measure::DistAndPoints& dap = m_measurement_result.distance_infinite.has_value() ? *m_measurement_result.distance_infinite : *m_measurement_result.distance_strict; point_point(dap.from, dap.to); - } - // Now if one of the features is an edge, draw also the extension of the edge to where the dist is measured: - // TODO... + const Measure::SurfaceFeature& f1 = *m_selected_features.first.feature; + const Measure::SurfaceFeature& f2 = *m_selected_features.second.feature; + Measure::SurfaceFeatureType ft1 = f1.get_type(); + Measure::SurfaceFeatureType ft2 = f2.get_type(); - // point-edge - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { - point_edge(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_edge()); + // Where needed, draw also the extension of the edge to where the dist is measured: + if ((int(ft1) | int(ft2)) == + (int(Measure::SurfaceFeatureType::Point) | int(Measure::SurfaceFeatureType::Edge))) + point_edge(f1, f2); + + + // Now if there is an angle to show, draw the arc: + if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Edge) + arc_edge_edge(f1, f2); + //if (int(ft1) | int(ft2) == (int(Measure::SurfaceFeatureType::Edge) | int(Measure::SurfaceFeatureType::Plane))) + // arc_edge_plane(); } - /* - // edge-point - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_edge(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_edge()); - } - // edge-edge - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { - edge_edge(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_edge()); - } - // edge-plane - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { - edge_plane(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_plane()); - } - // edge-circle - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { - edge_circle(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_circle()); - } - // plane-edge - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { - edge_plane(m_selected_features.second.feature->get_edge(), m_selected_features.first.feature->get_plane()); - } - // plane-plane - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { - plane_plane(m_selected_features.first.feature->get_plane(), m_selected_features.second.feature->get_plane()); - } - // circle-edge - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { - edge_circle(m_selected_features.second.feature->get_edge(), m_selected_features.first.feature->get_circle()); - } -*/ - glsafe(::glEnable(GL_DEPTH_TEST)); shader->stop_using(); @@ -1533,7 +1321,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (ImGui::BeginTable("Measure", 3)) { if (measure.angle.has_value()) { ImGui::PushID((void*)(intptr_t)1); - add_measure_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), + add_measure_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } From 5091d77adb9083152e0b92da515e3b15f9ead8be Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 29 Sep 2022 16:39:39 +0200 Subject: [PATCH 073/103] Measurement: moving arrow-drawing functions from frontend to the backend (4/4) --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index e9fbd7f8c0..568d5db3a2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -849,7 +849,7 @@ void GLGizmoMeasure::render_dimensioning() } }; - auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, double* const force_radius = nullptr) { + auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, const double* force_radius = nullptr) { Measure::MeasurementResult res = Measure::get_measurement(f1, f2); const double angle = res.angle->angle; const Vec3d center = res.angle->center; @@ -928,8 +928,12 @@ void GLGizmoMeasure::render_dimensioning() }; -/* - auto arc_edge_plane = [this, arc_edge_edge](const Edge& e, const Plane& p) { + + auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + + std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); + std::tuple p = f1.get_type() == Measure::SurfaceFeatureType::Plane ? f1.get_plane() : f2.get_plane(); + const auto& [idx, normal, origin] = p; const Vec3d e1e2 = e.second - e.first; const double abs_dot = std::abs(normal.dot(edge_direction(e))); @@ -941,7 +945,7 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d inters = line.intersectionPoint(plane); // ensure the edge is pointing away from the intersection - Edge ecopy = e; + std::pair ecopy = e; Vec3d e1e2copy = e1e2; if ((ecopy.first - inters).squaredNorm() > (ecopy.second - inters).squaredNorm()) { std::swap(ecopy.first, ecopy.second); @@ -951,7 +955,7 @@ void GLGizmoMeasure::render_dimensioning() // calculate 2nd edge (on the plane) const Vec3d temp = normal.cross(e1e2copy); const Vec3d edge_on_plane_unit = normal.cross(temp).normalized(); - Edge edge_on_plane = { origin, origin + e1e2copy.norm() * edge_on_plane_unit }; + std::pair edge_on_plane = { origin, origin + e1e2copy.norm() * edge_on_plane_unit }; // ensure the 2nd edge is pointing in the correct direction const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2copy); @@ -960,9 +964,11 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e1e2copy_mid = 0.5 * (ecopy.second + ecopy.first); const double radius = (inters - e1e2copy_mid).norm(); - arc_edge_edge(ecopy, edge_on_plane, &radius); + arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, ecopy.second, ecopy.first, std::optional(), 0.), + Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, edge_on_plane.second, edge_on_plane.first, std::optional(), 0.), + &radius); }; -*/ + shader->start_using(); @@ -1028,8 +1034,8 @@ void GLGizmoMeasure::render_dimensioning() // Now if there is an angle to show, draw the arc: if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Edge) arc_edge_edge(f1, f2); - //if (int(ft1) | int(ft2) == (int(Measure::SurfaceFeatureType::Edge) | int(Measure::SurfaceFeatureType::Plane))) - // arc_edge_plane(); + if (int(ft1) | int(ft2) == (int(Measure::SurfaceFeatureType::Edge) | int(Measure::SurfaceFeatureType::Plane))) + arc_edge_plane(f1, f2); } From 97e69c04f87a4b3d6d34b406c56808b8e04f27e5 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 30 Sep 2022 08:52:38 +0200 Subject: [PATCH 074/103] Fixed crashing asserts due to a bug in the just merged branch --- src/libslic3r/Measure.cpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 26 +++++++++++++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index fbf248c0f7..78d3d524e7 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -646,6 +646,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& angle = std::acos(std::abs(normal1.dot(normal2))); } result.angle = std::make_optional(AngleAndPoints(angle, Vec3d::Zero(), Vec3d::UnitX(), Vec3d::UnitX(), 0., false)); // TODO + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 568d5db3a2..6aebd0fa37 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -858,8 +858,8 @@ void GLGizmoMeasure::render_dimensioning() const double radius = res.angle->radius; const bool coplanar = res.angle->coplanar; - std::pair e1 = m_selected_features.first.feature->get_edge(); - std::pair e2 = m_selected_features.second.feature->get_edge(); + std::pair e1 = f1.get_edge(); + std::pair e2 = f2.get_edge(); if ((e1.second - e1.first).dot(e1_unit) < 0.) std::swap(e1.first, e1.second); @@ -1019,23 +1019,25 @@ void GLGizmoMeasure::render_dimensioning() : *m_measurement_result.distance_strict; point_point(dap.from, dap.to); - const Measure::SurfaceFeature& f1 = *m_selected_features.first.feature; - const Measure::SurfaceFeature& f2 = *m_selected_features.second.feature; - Measure::SurfaceFeatureType ft1 = f1.get_type(); - Measure::SurfaceFeatureType ft2 = f2.get_type(); + const Measure::SurfaceFeature* f1 = &(*m_selected_features.first.feature); + const Measure::SurfaceFeature* f2 = &(*m_selected_features.second.feature); + Measure::SurfaceFeatureType ft1 = f1->get_type(); + Measure::SurfaceFeatureType ft2 = f2->get_type(); + // Order features by type so following conditions are simple. + if (ft2 > ft2) + std::swap(ft1, ft2); // Where needed, draw also the extension of the edge to where the dist is measured: - if ((int(ft1) | int(ft2)) == - (int(Measure::SurfaceFeatureType::Point) | int(Measure::SurfaceFeatureType::Edge))) - point_edge(f1, f2); + if (ft1 == Measure::SurfaceFeatureType::Point && ft2 == Measure::SurfaceFeatureType::Edge) + point_edge(*f1, *f2); // Now if there is an angle to show, draw the arc: if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Edge) - arc_edge_edge(f1, f2); - if (int(ft1) | int(ft2) == (int(Measure::SurfaceFeatureType::Edge) | int(Measure::SurfaceFeatureType::Plane))) - arc_edge_plane(f1, f2); + arc_edge_edge(*f1, *f2); + if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Plane) + arc_edge_plane(*f1, *f2); } From 9b915bdd0985fd3deb7b99f4d3598b63fa5b3b99 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 09:44:22 +0200 Subject: [PATCH 075/103] Measuring - GLGizmoMeasure - Visualization and selection of extra point for edges --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 28 ++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 6aebd0fa37..76d2dacd64 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -62,7 +62,7 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t std::string ret; switch (type) { case Measure::SurfaceFeatureType::Point: { ret = _u8L("Vertex"); break; } - case Measure::SurfaceFeatureType::Edge: { ret = _u8L("Point on edge"); break; } + case Measure::SurfaceFeatureType::Edge: { ret = (hover_id == POINT_ID) ? _u8L("Center of edge") : _u8L("Point on edge"); break; } case Measure::SurfaceFeatureType::Circle: { ret = (hover_id == POINT_ID) ? _u8L("Center of circle") : _u8L("Point on circle"); break; } case Measure::SurfaceFeatureType::Plane: { ret = _u8L("Point on plane"); break; } default: { assert(false); break; } @@ -335,6 +335,8 @@ void GLGizmoMeasure::on_render() case Measure::SurfaceFeatureType::Edge: { m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); + if (m_curr_feature->get_extra_point().has_value()) + m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); break; } case Measure::SurfaceFeatureType::Circle: @@ -400,7 +402,11 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Edge: { - m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); + const std::optional extra = m_curr_feature->get_extra_point(); + if (extra.has_value() && m_hover_id == POINT_ID) + m_curr_point_on_feature_position = *extra; + else + m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); break; } case Measure::SurfaceFeatureType::Plane: @@ -497,11 +503,25 @@ void GLGizmoMeasure::on_render() case Measure::SurfaceFeatureType::Edge: { const auto& [start, end] = feature.get_edge(); + // render extra point + const std::optional extra = m_curr_feature->get_extra_point(); + if (extra.has_value()) { + const Transform3d point_matrix = model_matrix * Geometry::translation_transform(*extra) * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(point_matrix); + m_sphere.model.set_color(colors.front()); + m_sphere.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(point_matrix); + } + } + // render edge const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start) * Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); set_matrix_uniforms(feature_matrix); - m_cylinder.model.set_color(colors.front()); + m_cylinder.model.set_color(colors.back()); m_cylinder.model.render(); if (update_raycasters) { auto it = m_raycasters.find(EDGE_ID); @@ -550,13 +570,13 @@ void GLGizmoMeasure::on_render() colors.emplace_back(hover_selection_color()); break; } + case Measure::SurfaceFeatureType::Edge: case Measure::SurfaceFeatureType::Circle: { colors.emplace_back((m_hover_id == POINT_ID) ? hover_selection_color() : hovering_color()); colors.emplace_back(hovering_color()); break; } - case Measure::SurfaceFeatureType::Edge: case Measure::SurfaceFeatureType::Plane: { colors.emplace_back(hovering_color()); From f7afed4bd3ba70339e77ee3b87b398ec2a1d3a16 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 12:06:07 +0200 Subject: [PATCH 076/103] Follow-up of 9b915bdd0985fd3deb7b99f4d3598b63fa5b3b99 - Fixed crash --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 76d2dacd64..5cc4ce24e3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -504,7 +504,7 @@ void GLGizmoMeasure::on_render() { const auto& [start, end] = feature.get_edge(); // render extra point - const std::optional extra = m_curr_feature->get_extra_point(); + const std::optional extra = feature.get_extra_point(); if (extra.has_value()) { const Transform3d point_matrix = model_matrix * Geometry::translation_transform(*extra) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(point_matrix); From 8111fdfb544c139f404312533252a238a222b9dc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 12:54:00 +0200 Subject: [PATCH 077/103] Measuring - Added a bunch of utility functions in Measure.hpp --- src/libslic3r/Measure.cpp | 6 ++--- src/libslic3r/Measure.hpp | 32 ++++++++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 21 ++++------------ 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 78d3d524e7..b27425ea91 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -633,11 +633,11 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& } else if (f1.get_type() == SurfaceFeatureType::Plane) { assert(f2.get_type() == SurfaceFeatureType::Plane); - const auto& [idx1, normal1, pt1] = f1.get_plane(); - const auto& [idx2, normal2, pt2] = f2.get_plane(); + const auto [idx1, normal1, pt1] = f1.get_plane(); + const auto [idx2, normal2, pt2] = f2.get_plane(); double angle = 0.; - if (normal1.isApprox(normal2)) { + if (are_parallel(normal1, normal2)) { // The planes are parallel, calculate distance. Eigen::Hyperplane plane(normal1, pt1); result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(pt2), Vec3d::Zero(), Vec3d::Zero()}); diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index b88411a9f6..4ac56a87f3 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -142,6 +142,38 @@ struct MeasurementResult { // Returns distance/angle between two SurfaceFeatures. MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b); +inline Vec3d edge_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); } +inline Vec3d edge_direction(const SurfaceFeature& edge) { + assert(edge.get_type() == SurfaceFeatureType::Edge); + const auto [from, to] = edge.get_edge(); + return edge_direction(from, to); +} + +inline Vec3d plane_normal(const SurfaceFeature& plane) { + assert(plane.get_type() == SurfaceFeatureType::Plane); + return std::get<1>(plane.get_plane()); +} + +inline bool are_parallel(const Vec3d& v1, const Vec3d& v2) { return std::abs(std::abs(v1.dot(v2)) - 1.0) < EPSILON; } +inline bool are_perpendicular(const Vec3d& v1, const Vec3d& v2) { return std::abs(v1.dot(v2)) < EPSILON; } + +inline bool are_parallel(const SurfaceFeature& f1, const SurfaceFeature& f2) { + if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Edge) + return are_parallel(edge_direction(f1), edge_direction(f2)); + else if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Plane) + return are_perpendicular(edge_direction(f1), plane_normal(f2)); + else + return false; +} + +inline bool are_perpendicular(const SurfaceFeature& f1, const SurfaceFeature& f2) { + if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Edge) + return are_perpendicular(edge_direction(f1), edge_direction(f2)); + else if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Plane) + return are_parallel(edge_direction(f1), plane_normal(f2)); + else + return false; +} } // namespace Measure } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 5cc4ce24e3..421dfbedd5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -70,11 +70,6 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t return ret; } -static Vec3d edge_direction(const std::pair& e) -{ - return (e.second - e.first).normalized(); -} - static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) { assert(0 <= idx && idx < (int)planes_triangles.size()); @@ -123,8 +118,6 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) else if (mouse_event.LeftDown()) { if (m_hover_id != -1) { SelectedFeatures selected_features_old = m_selected_features; - - m_mouse_left_down = true; auto item_from_feature = [this]() { @@ -834,7 +827,6 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.triangle.render(); }; - auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); const Vec3d v_proj = m_measurement_result.distance_infinite->to; @@ -929,7 +921,7 @@ void GLGizmoMeasure::render_dimensioning() const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e1)) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e1.first, e1.second)) * Geometry::scale_transform({ e11center_len, 1.0f, 1.0f })); m_dimensioning.line.render(); } @@ -941,14 +933,12 @@ void GLGizmoMeasure::render_dimensioning() const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e2)) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e2.first, e2.second)) * Geometry::scale_transform({ (coplanar && (force_radius == nullptr)) ? e21center_len : draw_radius, 1.0f, 1.0f })); m_dimensioning.line.render(); } }; - - auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); @@ -956,8 +946,8 @@ void GLGizmoMeasure::render_dimensioning() const auto& [idx, normal, origin] = p; const Vec3d e1e2 = e.second - e.first; - const double abs_dot = std::abs(normal.dot(edge_direction(e))); - if (abs_dot < EPSILON || std::abs(abs_dot - 1.0) < EPSILON) + const double abs_dot = std::abs(normal.dot(Measure::edge_direction(f1))); + if (Measure::are_parallel(f1, f2) || Measure::are_perpendicular(f1, f2)) return; const Eigen::Hyperplane plane(normal, origin); @@ -1052,11 +1042,10 @@ void GLGizmoMeasure::render_dimensioning() if (ft1 == Measure::SurfaceFeatureType::Point && ft2 == Measure::SurfaceFeatureType::Edge) point_edge(*f1, *f2); - // Now if there is an angle to show, draw the arc: if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Edge) arc_edge_edge(*f1, *f2); - if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Plane) + else if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Plane) arc_edge_plane(*f1, *f2); } From 0ef811556408ecbf0d41058563d51b8c86166005 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 13:18:17 +0200 Subject: [PATCH 078/103] Measuring - Refactoring and bug fixing in GLGizmoMeasure::render_dimensioning() --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 36 +++++++++++------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 421dfbedd5..9a62a8c39f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -828,7 +828,8 @@ void GLGizmoMeasure::render_dimensioning() }; auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { - std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); + assert(f1.get_type() == Measure::SurfaceFeatureType::Point && f2.get_type() == Measure::SurfaceFeatureType::Edge); + const std::pair e = f2.get_edge(); const Vec3d v_proj = m_measurement_result.distance_infinite->to; const Vec3d e1e2 = e.second - e.first; @@ -862,7 +863,8 @@ void GLGizmoMeasure::render_dimensioning() }; auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, const double* force_radius = nullptr) { - Measure::MeasurementResult res = Measure::get_measurement(f1, f2); + assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Edge); + const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); const double angle = res.angle->angle; const Vec3d center = res.angle->center; const Vec3d e1_unit = res.angle->e1; @@ -940,13 +942,9 @@ void GLGizmoMeasure::render_dimensioning() }; auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { - - std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); - std::tuple p = f1.get_type() == Measure::SurfaceFeatureType::Plane ? f1.get_plane() : f2.get_plane(); - - const auto& [idx, normal, origin] = p; - const Vec3d e1e2 = e.second - e.first; - const double abs_dot = std::abs(normal.dot(Measure::edge_direction(f1))); + assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Plane); + std::pair e = f1.get_edge(); + const auto [idx, normal, origin] = f2.get_plane(); if (Measure::are_parallel(f1, f2) || Measure::are_perpendicular(f1, f2)) return; @@ -956,21 +954,20 @@ void GLGizmoMeasure::render_dimensioning() // ensure the edge is pointing away from the intersection std::pair ecopy = e; - Vec3d e1e2copy = e1e2; - if ((ecopy.first - inters).squaredNorm() > (ecopy.second - inters).squaredNorm()) { + if ((ecopy.first - inters).squaredNorm() > (ecopy.second - inters).squaredNorm()) std::swap(ecopy.first, ecopy.second); - e1e2copy = -e1e2copy; - } // calculate 2nd edge (on the plane) - const Vec3d temp = normal.cross(e1e2copy); + const Vec3d e1e2 = ecopy.second - ecopy.first; + const double e1e2_len = e1e2.norm(); + const Vec3d temp = normal.cross(e1e2); const Vec3d edge_on_plane_unit = normal.cross(temp).normalized(); - std::pair edge_on_plane = { origin, origin + e1e2copy.norm() * edge_on_plane_unit }; + std::pair edge_on_plane = { origin, origin + e1e2_len * edge_on_plane_unit }; // ensure the 2nd edge is pointing in the correct direction - const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2copy); + const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2); if (test_edge.dot(temp) < 0.0) - edge_on_plane = { origin, origin - e1e2copy.norm() * edge_on_plane_unit }; + edge_on_plane = { origin, origin - e1e2_len * edge_on_plane_unit }; const Vec3d e1e2copy_mid = 0.5 * (ecopy.second + ecopy.first); const double radius = (inters - e1e2copy_mid).norm(); @@ -1035,8 +1032,10 @@ void GLGizmoMeasure::render_dimensioning() Measure::SurfaceFeatureType ft2 = f2->get_type(); // Order features by type so following conditions are simple. - if (ft2 > ft2) + if (ft1 > ft2) { std::swap(ft1, ft2); + std::swap(f1, f2); + } // Where needed, draw also the extension of the edge to where the dist is measured: if (ft1 == Measure::SurfaceFeatureType::Point && ft2 == Measure::SurfaceFeatureType::Edge) @@ -1048,7 +1047,6 @@ void GLGizmoMeasure::render_dimensioning() else if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Plane) arc_edge_plane(*f1, *f2); } - glsafe(::glEnable(GL_DEPTH_TEST)); From 6be56413f6ad36d0fa5aebc5d3d72b5f501018b1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 14:13:17 +0200 Subject: [PATCH 079/103] Measuring - Some refactoring --- src/libslic3r/Measure.cpp | 21 ++++++++++----------- src/libslic3r/Measure.hpp | 7 +++++-- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 18 ++++++++++-------- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index b27425ea91..ff07aa0810 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -250,7 +250,7 @@ void MeasuringImpl::extract_features() // Add the circle and remember indices into borders. const auto& [center, radius] = get_center_and_radius(border, start_idx, i, trafo); circles_idxs.emplace_back(start_idx, i); - circles.emplace_back(SurfaceFeature(SurfaceFeatureType::Circle, center, plane.normal, std::optional(), radius)); + circles.emplace_back(SurfaceFeature(SurfaceFeatureType::Circle, center, plane.normal, std::nullopt, radius)); circle = false; } } @@ -269,7 +269,7 @@ void MeasuringImpl::extract_features() const Vec3d center = std::get<0>(circles[i].get_circle()); for (int j=(int)circles_idxs[i].first + 1; j<=(int)circles_idxs[i].second; ++j) plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, - border[j-1], border[j], std::make_optional(center), 0.)); + border[j - 1], border[j], std::make_optional(center))); } else { // This will be handled just like a regular edge. circles_idxs.erase(circles_idxs.begin() + i); @@ -288,8 +288,8 @@ void MeasuringImpl::extract_features() for (int i=1; i (int)circles_idxs[cidx].first) i = circles_idxs[cidx++].second; - else plane.surface_features.emplace_back(SurfaceFeature( - SurfaceFeatureType::Edge, border[i-1], border[i], std::optional(), 0.)); + else + plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, border[i - 1], border[i])); } // FIXME Throw away / do not create edges which are parts of circles or @@ -307,7 +307,7 @@ void MeasuringImpl::extract_features() // The last surface feature is the plane itself. plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Plane, - plane.normal, plane.borders.front().front(), std::optional(), i + 0.0001)); + plane.normal, plane.borders.front().front(), std::nullopt, i + 0.0001)); plane.borders.clear(); plane.borders.shrink_to_fit(); @@ -433,13 +433,12 @@ std::vector> Measuring::get_planes_triangle_indices() const static AngleAndPoints angle_edge_edge(const std::pair& e1, const std::pair& e2) { - Vec3d e1_unit = (e1.second - e1.first).normalized(); - Vec3d e2_unit = (e2.second - e2.first).normalized(); - const double dot = e1_unit.dot(e2_unit); - // are edges parallel ? - if (std::abs(std::abs(dot) - 1.0) < EPSILON) + if (are_parallel(e1, e2)) return AngleAndPoints(0.0, e1.first, Vec3d::UnitX(), Vec3d::UnitX(), 0., true); + Vec3d e1_unit = edge_direction(e1.first, e1.second); + Vec3d e2_unit = edge_direction(e2.first, e2.second); + // project edges on the plane defined by them Vec3d normal = e1_unit.cross(e2_unit).normalized(); const Eigen::Hyperplane plane(normal, e1.first); @@ -562,7 +561,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& std::vector distances; auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair& e) { - const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second, std::optional(), 0.)); + const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second)); double distance = res.distance_strict->dist; Vec3d v2 = res.distance_strict->to; diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 4ac56a87f3..1c51289e8d 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -25,8 +25,8 @@ enum class SurfaceFeatureType : int { class SurfaceFeature { public: - SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional pt3, double value) - : m_type{type}, m_pt1{pt1}, m_pt2{pt2}, m_pt3{pt3}, m_value{value} {} + SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional pt3 = std::nullopt, double value = 0.0) + : m_type{ type }, m_pt1{ pt1 }, m_pt2{ pt2 }, m_pt3{ pt3 }, m_value{ value } {} explicit SurfaceFeature(const Vec3d& pt) : m_type{SurfaceFeatureType::Point}, m_pt1{pt} {} @@ -157,6 +157,9 @@ inline Vec3d plane_normal(const SurfaceFeature& plane) { inline bool are_parallel(const Vec3d& v1, const Vec3d& v2) { return std::abs(std::abs(v1.dot(v2)) - 1.0) < EPSILON; } inline bool are_perpendicular(const Vec3d& v1, const Vec3d& v2) { return std::abs(v1.dot(v2)) < EPSILON; } +inline bool are_parallel(const std::pair& e1, const std::pair& e2) { + return are_parallel(e1.second - e1.first, e2.second - e2.first); +} inline bool are_parallel(const SurfaceFeature& f1, const SurfaceFeature& f2) { if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Edge) return are_parallel(edge_direction(f1), edge_direction(f2)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 9a62a8c39f..d6488b83a9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -887,8 +887,6 @@ void GLGizmoMeasure::render_dimensioning() double draw_radius = force_radius ? *force_radius : radius; - const Vec3d normal = e1_unit.cross(e2_unit).normalized(); - if (!m_dimensioning.arc.is_initialized()) { const unsigned int resolution = std::max(2, 64 * angle / double(PI)); GLModel::Geometry init_data; @@ -898,6 +896,7 @@ void GLGizmoMeasure::render_dimensioning() init_data.reserve_indices(resolution + 1); // vertices + indices + const Vec3d normal = e1_unit.cross(e2_unit).normalized(); const double step = angle / double(resolution); for (unsigned int i = 0; i <= resolution; ++i) { const double a = step * double(i); @@ -943,16 +942,19 @@ void GLGizmoMeasure::render_dimensioning() auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Plane); - std::pair e = f1.get_edge(); - const auto [idx, normal, origin] = f2.get_plane(); if (Measure::are_parallel(f1, f2) || Measure::are_perpendicular(f1, f2)) return; + const std::pair e = f1.get_edge(); + const auto [idx, normal, origin] = f2.get_plane(); + + // ensure the edge is pointing away from the intersection + // 1st calculate instersection between edge and plane const Eigen::Hyperplane plane(normal, origin); const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); const Vec3d inters = line.intersectionPoint(plane); - // ensure the edge is pointing away from the intersection + // then verify edge direction and revert it, if needed std::pair ecopy = e; if ((ecopy.first - inters).squaredNorm() > (ecopy.second - inters).squaredNorm()) std::swap(ecopy.first, ecopy.second); @@ -971,9 +973,9 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e1e2copy_mid = 0.5 * (ecopy.second + ecopy.first); const double radius = (inters - e1e2copy_mid).norm(); - arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, ecopy.second, ecopy.first, std::optional(), 0.), - Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, edge_on_plane.second, edge_on_plane.first, std::optional(), 0.), - &radius); + arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, ecopy.first, ecopy.second), + Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, edge_on_plane.first, edge_on_plane.second), + &radius); }; From 7cdc7ac5357c6a89fb2fa1bde0f2000346c5a8b1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 14:35:39 +0200 Subject: [PATCH 080/103] Measuring - struct AngleAndPoints reworked as struct AngleAndEdges --- src/libslic3r/Measure.cpp | 14 +++++++------- src/libslic3r/Measure.hpp | 20 +++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 15 +++++---------- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index ff07aa0810..b9fb0f3448 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -427,14 +427,12 @@ std::vector> Measuring::get_planes_triangle_indices() const } +const AngleAndEdges AngleAndEdges::Dummy = { 0.0, Vec3d::Zero(), { Vec3d::Zero(), Vec3d::Zero() }, { Vec3d::Zero(), Vec3d::Zero() }, 0.0, true }; - - - -static AngleAndPoints angle_edge_edge(const std::pair& e1, const std::pair& e2) +static AngleAndEdges angle_edge_edge(std::pair e1, std::pair e2) { if (are_parallel(e1, e2)) - return AngleAndPoints(0.0, e1.first, Vec3d::UnitX(), Vec3d::UnitX(), 0., true); + return AngleAndEdges::Dummy; Vec3d e1_unit = edge_direction(e1.first, e1.second); Vec3d e2_unit = edge_direction(e2.first, e2.second); @@ -474,10 +472,12 @@ static AngleAndPoints angle_edge_edge(const std::pair& e1, const s // ensure the edges are pointing away from the center if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { std::swap(e11_proj, e12_proj); + std::swap(e1.first, e1.second); e1_unit = -e1_unit; } if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { std::swap(e21_proj, e22_proj); + std::swap(e2.first, e2.second); e2_unit = -e2_unit; } @@ -488,7 +488,7 @@ static AngleAndPoints angle_edge_edge(const std::pair& e1, const s const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); - return AngleAndPoints(angle, center, e1_unit, e2_unit, radius, coplanar); + return { angle, center, e1, e2, radius, coplanar }; } @@ -644,7 +644,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& // Planes are not parallel, calculate angle. angle = std::acos(std::abs(normal1.dot(normal2))); } - result.angle = std::make_optional(AngleAndPoints(angle, Vec3d::Zero(), Vec3d::UnitX(), Vec3d::UnitX(), 0., false)); // TODO + result.angle = std::make_optional(AngleAndEdges(angle, Vec3d::Zero(), { Vec3d::Zero(), Vec3d::Zero() }, { Vec3d::Zero(), Vec3d::Zero() }, 0., false)); // TODO result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO } diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 1c51289e8d..c59c9bbbc7 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -26,7 +26,7 @@ enum class SurfaceFeatureType : int { class SurfaceFeature { public: SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional pt3 = std::nullopt, double value = 0.0) - : m_type{ type }, m_pt1{ pt1 }, m_pt2{ pt2 }, m_pt3{ pt3 }, m_value{ value } {} + : m_type(type), m_pt1(pt1), m_pt2(pt2), m_pt3(pt3), m_value(value) {} explicit SurfaceFeature(const Vec3d& pt) : m_type{SurfaceFeatureType::Point}, m_pt1{pt} {} @@ -117,19 +117,21 @@ struct DistAndPoints { Vec3d to; }; -struct AngleAndPoints { - AngleAndPoints(double angle_, Vec3d center_, Vec3d e1_, Vec3d e2_, double radius_, bool coplanar_) - : angle(angle_), center(center_), e1(e1_), e2(e2_), radius(radius_), coplanar(coplanar_) {} +struct AngleAndEdges { + AngleAndEdges(double angle_, const Vec3d& center_, const std::pair& e1_, const std::pair& e2_, double radius_, bool coplanar_) + : angle(angle_), center(center_), e1(e1_), e2(e2_), radius(radius_), coplanar(coplanar_) {} double angle; Vec3d center; - Vec3d e1; - Vec3d e2; + std::pair e1; + std::pair e2; double radius; bool coplanar; + + static const AngleAndEdges Dummy; }; struct MeasurementResult { - std::optional angle; + std::optional angle; std::optional distance_infinite; std::optional distance_strict; std::optional distance_xyz; @@ -143,10 +145,10 @@ struct MeasurementResult { MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b); inline Vec3d edge_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); } +inline Vec3d edge_direction(const std::pair& e) { return edge_direction(e.first, e.second); } inline Vec3d edge_direction(const SurfaceFeature& edge) { assert(edge.get_type() == SurfaceFeatureType::Edge); - const auto [from, to] = edge.get_edge(); - return edge_direction(from, to); + return edge_direction(edge.get_edge()); } inline Vec3d plane_normal(const SurfaceFeature& plane) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index d6488b83a9..138e8c54a3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -867,19 +867,11 @@ void GLGizmoMeasure::render_dimensioning() const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); const double angle = res.angle->angle; const Vec3d center = res.angle->center; - const Vec3d e1_unit = res.angle->e1; - const Vec3d e2_unit = res.angle->e2; + const std::pair e1 = res.angle->e1; + const std::pair e2 = res.angle->e2; const double radius = res.angle->radius; const bool coplanar = res.angle->coplanar; - std::pair e1 = f1.get_edge(); - std::pair e2 = f2.get_edge(); - - if ((e1.second - e1.first).dot(e1_unit) < 0.) - std::swap(e1.first, e1.second); - if ((e2.second - e2.first).dot(e2_unit) < 0.) - std::swap(e2.first, e2.second); - if (radius == 0.) return; @@ -887,6 +879,9 @@ void GLGizmoMeasure::render_dimensioning() double draw_radius = force_radius ? *force_radius : radius; + const Vec3d e1_unit = Measure::edge_direction(e1); + const Vec3d e2_unit = Measure::edge_direction(e2); + if (!m_dimensioning.arc.is_initialized()) { const unsigned int resolution = std::max(2, 64 * angle / double(PI)); GLModel::Geometry init_data; From 6f63a69e04b507749e056d58d4de3d511cd8c678 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 15:50:31 +0200 Subject: [PATCH 081/103] Measuring - Calculation of angle between edge and plane moved to backend --- src/libslic3r/Measure.cpp | 48 ++++++++++++++++++++-- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 52 ++++++------------------ 2 files changed, 56 insertions(+), 44 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index b9fb0f3448..f2cbb9f640 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -429,7 +429,7 @@ std::vector> Measuring::get_planes_triangle_indices() const const AngleAndEdges AngleAndEdges::Dummy = { 0.0, Vec3d::Zero(), { Vec3d::Zero(), Vec3d::Zero() }, { Vec3d::Zero(), Vec3d::Zero() }, 0.0, true }; -static AngleAndEdges angle_edge_edge(std::pair e1, std::pair e2) +static AngleAndEdges angle_edge_edge(const std::pair& e1, const std::pair& e2) { if (are_parallel(e1, e2)) return AngleAndEdges::Dummy; @@ -470,14 +470,16 @@ static AngleAndEdges angle_edge_edge(std::pair e1, std::pair out_e1 = e1; + std::pair out_e2 = e2; if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { std::swap(e11_proj, e12_proj); - std::swap(e1.first, e1.second); + std::swap(out_e1.first, out_e1.second); e1_unit = -e1_unit; } if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { std::swap(e21_proj, e22_proj); - std::swap(e2.first, e2.second); + std::swap(out_e2.first, out_e2.second); e2_unit = -e2_unit; } @@ -488,9 +490,46 @@ static AngleAndEdges angle_edge_edge(std::pair e1, std::pair& e, const std::tuple& p) +{ + const auto& [idx, normal, origin] = p; + const Vec3d e1e2_unit = edge_direction(e); + if (are_parallel(e1e2_unit, normal) || are_perpendicular(e1e2_unit, normal)) + return AngleAndEdges::Dummy; + + // ensure the edge is pointing away from the intersection + // 1st calculate instersection between edge and plane + const Eigen::Hyperplane plane(normal, origin); + const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); + const Vec3d inters = line.intersectionPoint(plane); + + // then verify edge direction and revert it, if needed + Vec3d e1 = e.first; + Vec3d e2 = e.second; + if ((e1 - inters).squaredNorm() > (e2 - inters).squaredNorm()) + std::swap(e1, e2); + + const Vec3d e1e2 = e2 - e1; + const double e1e2_len = e1e2.norm(); + + // calculate 2nd edge (on the plane) + const Vec3d temp = normal.cross(e1e2); + const Vec3d edge_on_plane_unit = normal.cross(temp).normalized(); + std::pair edge_on_plane = { origin, origin + e1e2_len * edge_on_plane_unit }; + + // ensure the 2nd edge is pointing in the correct direction + const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2); + if (test_edge.dot(temp) < 0.0) + edge_on_plane = { origin, origin - e1e2_len * edge_on_plane_unit }; + + AngleAndEdges ret = angle_edge_edge({ e1, e2 }, edge_on_plane); + const Vec3d e1e2copy_mid = 0.5 * (e1 + e2); + ret.radius = (inters - e1e2copy_mid).norm(); + return ret; +} @@ -615,6 +654,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + result.angle = angle_edge_plane(f1.get_edge(), f2.get_plane()); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 138e8c54a3..db015bf1c2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -862,22 +862,20 @@ void GLGizmoMeasure::render_dimensioning() } }; - auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, const double* force_radius = nullptr) { + auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, double radius = 0.0) { assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Edge); const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); const double angle = res.angle->angle; const Vec3d center = res.angle->center; const std::pair e1 = res.angle->e1; const std::pair e2 = res.angle->e2; - const double radius = res.angle->radius; + const double calc_radius = res.angle->radius; const bool coplanar = res.angle->coplanar; - if (radius == 0.) + if (calc_radius == 0.0) return; - assert(force_radius == nullptr || *force_radius > 0.0); - - double draw_radius = force_radius ? *force_radius : radius; + const double draw_radius = (radius > 0.0) ? radius : calc_radius; const Vec3d e1_unit = Measure::edge_direction(e1); const Vec3d e2_unit = Measure::edge_direction(e2); @@ -930,50 +928,24 @@ void GLGizmoMeasure::render_dimensioning() shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e2.first, e2.second)) * - Geometry::scale_transform({ (coplanar && (force_radius == nullptr)) ? e21center_len : draw_radius, 1.0f, 1.0f })); + Geometry::scale_transform({ (coplanar && radius > 0.0) ? e21center_len : draw_radius, 1.0f, 1.0f })); m_dimensioning.line.render(); } }; auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Plane); - if (Measure::are_parallel(f1, f2) || Measure::are_perpendicular(f1, f2)) + const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); + const std::pair e1 = res.angle->e1; + const std::pair e2 = res.angle->e2; + const double calc_radius = res.angle->radius; + if (calc_radius == 0.0) return; - const std::pair e = f1.get_edge(); - const auto [idx, normal, origin] = f2.get_plane(); - - // ensure the edge is pointing away from the intersection - // 1st calculate instersection between edge and plane - const Eigen::Hyperplane plane(normal, origin); - const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); - const Vec3d inters = line.intersectionPoint(plane); - - // then verify edge direction and revert it, if needed - std::pair ecopy = e; - if ((ecopy.first - inters).squaredNorm() > (ecopy.second - inters).squaredNorm()) - std::swap(ecopy.first, ecopy.second); - - // calculate 2nd edge (on the plane) - const Vec3d e1e2 = ecopy.second - ecopy.first; - const double e1e2_len = e1e2.norm(); - const Vec3d temp = normal.cross(e1e2); - const Vec3d edge_on_plane_unit = normal.cross(temp).normalized(); - std::pair edge_on_plane = { origin, origin + e1e2_len * edge_on_plane_unit }; - - // ensure the 2nd edge is pointing in the correct direction - const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2); - if (test_edge.dot(temp) < 0.0) - edge_on_plane = { origin, origin - e1e2_len * edge_on_plane_unit }; - - const Vec3d e1e2copy_mid = 0.5 * (ecopy.second + ecopy.first); - const double radius = (inters - e1e2copy_mid).norm(); - arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, ecopy.first, ecopy.second), - Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, edge_on_plane.first, edge_on_plane.second), - &radius); + arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e1.first, e1.second), + Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e2.first, e2.second), calc_radius); }; - shader->start_using(); if (!m_dimensioning.line.is_initialized()) { From edc90170f4f79bd4200375ccbbd29f7e5e8c31cf Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Oct 2022 09:14:47 +0200 Subject: [PATCH 082/103] Measuring - Gizmo measure shows dimensioning for angle plane-plane --- src/libslic3r/Measure.cpp | 54 ++++++++++++++++++++++-- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 15 +++++++ 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index f2cbb9f640..4d37ea712c 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -306,8 +306,17 @@ void MeasuringImpl::extract_features() } // The last surface feature is the plane itself. + Vec3d cog = Vec3d::Zero(); + size_t counter = 0; + for (const std::vector& b : plane.borders) { + for (size_t i = 1; i < b.size(); ++i) { + cog += b[i]; + ++counter; + } + } + cog /= double(counter); plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Plane, - plane.normal, plane.borders.front().front(), std::nullopt, i + 0.0001)); + plane.normal, cog, std::optional(), i + 0.0001)); plane.borders.clear(); plane.borders.shrink_to_fit(); @@ -526,11 +535,48 @@ static AngleAndEdges angle_edge_plane(const std::pair& e, const st edge_on_plane = { origin, origin - e1e2_len * edge_on_plane_unit }; AngleAndEdges ret = angle_edge_edge({ e1, e2 }, edge_on_plane); - const Vec3d e1e2copy_mid = 0.5 * (e1 + e2); - ret.radius = (inters - e1e2copy_mid).norm(); + ret.radius = (inters - 0.5 * (e1 + e2)).norm(); return ret; } +static AngleAndEdges angle_plane_plane(const std::tuple& p1, const std::tuple& p2) +{ + const auto& [idx1, normal1, origin1] = p1; + const auto& [idx2, normal2, origin2] = p2; + + // are planes parallel ? + if (are_parallel(normal1, normal2)) + return AngleAndEdges::Dummy; + + auto intersection_plane_plane = [](const Vec3d& n1, const Vec3d& o1, const Vec3d& n2, const Vec3d& o2) { + Eigen::MatrixXd m(2, 3); + m << n1.x(), n1.y(), n1.z(), n2.x(), n2.y(), n2.z(); + Eigen::VectorXd b(2); + b << o1.dot(n1), o2.dot(n2); + Eigen::VectorXd x = m.colPivHouseholderQr().solve(b); + return std::make_pair(n1.cross(n2).normalized(), Vec3d(x(0), x(1), x(2))); + }; + + // Calculate intersection line between planes + const auto [intersection_line_direction, intersection_line_origin] = intersection_plane_plane(normal1, origin1, normal2, origin2); + + // Project planes' origin on intersection line + const Eigen::ParametrizedLine intersection_line = Eigen::ParametrizedLine(intersection_line_origin, intersection_line_direction); + const Vec3d origin1_proj = intersection_line.projection(origin1); + const Vec3d origin2_proj = intersection_line.projection(origin2); + + // Calculate edges on planes + const Vec3d edge_on_plane1_unit = (origin1 - origin1_proj).normalized(); + const Vec3d edge_on_plane2_unit = (origin2 - origin2_proj).normalized(); + const double edges_angle = std::acos(std::clamp(edge_on_plane1_unit.dot(edge_on_plane2_unit), -1.0, 1.0)); + const double radius = std::max(10.0, std::max((origin1 - origin1_proj).norm(), (origin2 - origin2_proj).norm())); + const std::pair edge_on_plane1 = { origin1_proj + radius * edge_on_plane1_unit, origin1_proj + 2.0 * radius * edge_on_plane1_unit }; + const std::pair edge_on_plane2 = { origin2_proj + radius * edge_on_plane2_unit, origin2_proj + 2.0 * radius * edge_on_plane2_unit }; + + AngleAndEdges ret = angle_edge_edge(edge_on_plane1, edge_on_plane2); + ret.radius = radius; + return ret; +} @@ -684,7 +730,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& // Planes are not parallel, calculate angle. angle = std::acos(std::abs(normal1.dot(normal2))); } - result.angle = std::make_optional(AngleAndEdges(angle, Vec3d::Zero(), { Vec3d::Zero(), Vec3d::Zero() }, { Vec3d::Zero(), Vec3d::Zero() }, 0., false)); // TODO + result.angle = angle_plane_plane(f1.get_plane(), f2.get_plane()); result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index db015bf1c2..b790e43306 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -946,6 +946,19 @@ void GLGizmoMeasure::render_dimensioning() Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e2.first, e2.second), calc_radius); }; + auto arc_plane_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + assert(f1.get_type() == Measure::SurfaceFeatureType::Plane && f2.get_type() == Measure::SurfaceFeatureType::Plane); + const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); + const std::pair e1 = res.angle->e1; + const std::pair e2 = res.angle->e2; + const double calc_radius = res.angle->radius; + if (calc_radius == 0.0) + return; + + arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e1.first, e1.second), + Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e2.first, e2.second), calc_radius); + }; + shader->start_using(); if (!m_dimensioning.line.is_initialized()) { @@ -1015,6 +1028,8 @@ void GLGizmoMeasure::render_dimensioning() arc_edge_edge(*f1, *f2); else if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Plane) arc_edge_plane(*f1, *f2); + else if (ft1 == Measure::SurfaceFeatureType::Plane && ft2 == Measure::SurfaceFeatureType::Plane) + arc_plane_plane(*f1, *f2); } glsafe(::glEnable(GL_DEPTH_TEST)); From 2e81200db039e0bf3c895f9a9984badb703a4af9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Oct 2022 10:50:38 +0200 Subject: [PATCH 083/103] Measuring - Gizmo measure shows value of distance dimensioning in 3D scene --- src/libslic3r/Measure.cpp | 8 ++-- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 51 ++++++++++++++++-------- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 4d37ea712c..529c6f1231 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -663,10 +663,10 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& distances.emplace_back((e2.second - e1.first).norm(), e1.first, e2.second); distances.emplace_back((e2.first - e1.second).norm(), e1.second, e2.first); distances.emplace_back((e2.second - e1.second).norm(), e1.second, e2.second); - add_point_edge_distance(e1.first, e2); - add_point_edge_distance(e1.second, e2); - add_point_edge_distance(e2.first, e1); - add_point_edge_distance(e2.second, e1); +// add_point_edge_distance(e1.first, e2); +// add_point_edge_distance(e1.second, e2); +// add_point_edge_distance(e2.first, e1); +// add_point_edge_distance(e2.second, e1); auto it = std::min_element(distances.begin(), distances.end(), [](const DistAndPoints& item1, const DistAndPoints& item2) { return item1.dist < item2.dist; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index b790e43306..4efaf67d78 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -44,6 +44,20 @@ static const std::string CTRL_STR = #endif //__APPLE__ ; +static std::string format_double(double value) +{ + char buf[1024]; + sprintf(buf, "%.3f", value); + return std::string(buf); +} + +static std::string format_vec3(const Vec3d& v) +{ + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); + return std::string(buf); +} + static std::string surface_feature_type_as_string(Measure::SurfaceFeatureType type) { switch (type) @@ -778,7 +792,7 @@ void GLGizmoMeasure::render_dimensioning() if (shader == nullptr) return; - auto point_point = [this, shader](const Vec3d& v1, const Vec3d& v2) { + auto point_point = [this, shader](const Vec3d& v1, const Vec3d& v2, float distance) { if (v1.isApprox(v2)) return; @@ -825,6 +839,23 @@ void GLGizmoMeasure::render_dimensioning() ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q21ss : ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q12ss); m_dimensioning.triangle.render(); + + if (distance > 0.0) { + const Vec2d label_position = 0.5 * (v1ss + v2ss); + m_imgui->set_next_window_pos(label_position.x(), viewport[3] - label_position.y(), ImGuiCond_Always, 0.0f, 1.0f); + m_imgui->set_next_window_bg_alpha(0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + m_imgui->begin(_L("##distance"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); + std::string txt = format_double(distance) + " "; + if (wxGetApp().app_config->get("use_inches") == "1") + txt += _u8L("in"); + else + txt += _u8L("mm"); + m_imgui->text(txt); + m_imgui->end(); + ImGui::PopStyleVar(); + } }; auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { @@ -872,7 +903,7 @@ void GLGizmoMeasure::render_dimensioning() const double calc_radius = res.angle->radius; const bool coplanar = res.angle->coplanar; - if (calc_radius == 0.0) + if (std::abs(angle) < EPSILON || std::abs(calc_radius) < EPSILON) return; const double draw_radius = (radius > 0.0) ? radius : calc_radius; @@ -1006,7 +1037,7 @@ void GLGizmoMeasure::render_dimensioning() const Measure::DistAndPoints& dap = m_measurement_result.distance_infinite.has_value() ? *m_measurement_result.distance_infinite : *m_measurement_result.distance_strict; - point_point(dap.from, dap.to); + point_point(dap.from, dap.to, dap.dist); const Measure::SurfaceFeature* f1 = &(*m_selected_features.first.feature); const Measure::SurfaceFeature* f2 = &(*m_selected_features.second.feature); @@ -1052,20 +1083,6 @@ static void add_strings_row_to_table(ImGuiWrapper& imgui, const std::string& col add_row_to_table([&]() { imgui.text_colored(col_1_color, col_1); }, [&]() { imgui.text_colored(col_2_color, col_2); }); }; -static std::string format_double(double value) -{ - char buf[1024]; - sprintf(buf, "%.3f", value); - return std::string(buf); -} - -static std::string format_vec3(const Vec3d& v) -{ - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); - return std::string(buf); -} - #if ENABLE_MEASURE_GIZMO_DEBUG void GLGizmoMeasure::render_debug_dialog() { From 1f5859deb1af3fb1e5b76cc29434c257df63c991 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Oct 2022 11:36:08 +0200 Subject: [PATCH 084/103] Measuring - Gizmo measure shows value of angle dimensioning in 3D scene --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 63 ++++++++++++++---------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 4efaf67d78..8a4cd17d94 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -840,22 +840,18 @@ void GLGizmoMeasure::render_dimensioning() ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q12ss); m_dimensioning.triangle.render(); - if (distance > 0.0) { - const Vec2d label_position = 0.5 * (v1ss + v2ss); - m_imgui->set_next_window_pos(label_position.x(), viewport[3] - label_position.y(), ImGuiCond_Always, 0.0f, 1.0f); - m_imgui->set_next_window_bg_alpha(0.0f); - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); - m_imgui->begin(_L("##distance"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); - ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); - std::string txt = format_double(distance) + " "; - if (wxGetApp().app_config->get("use_inches") == "1") - txt += _u8L("in"); - else - txt += _u8L("mm"); - m_imgui->text(txt); - m_imgui->end(); - ImGui::PopStyleVar(); - } + const Vec2d label_position = 0.5 * (v1ss + v2ss); + m_imgui->set_next_window_pos(label_position.x(), viewport[3] - label_position.y(), ImGuiCond_Always, 0.0f, 1.0f); + m_imgui->set_next_window_bg_alpha(0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + m_imgui->begin(_L("##distance"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); + const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; + const std::string txt = use_inches ? format_double(ObjectManipulation::mm_to_in * distance) + " " + _u8L("in") : + format_double(distance) + " " + _u8L("mm"); + m_imgui->text(txt); + m_imgui->end(); + ImGui::PopStyleVar(); }; auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { @@ -911,8 +907,11 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e1_unit = Measure::edge_direction(e1); const Vec3d e2_unit = Measure::edge_direction(e2); + const unsigned int resolution = std::max(2, 64 * angle / double(PI)); + const double step = angle / double(resolution); + const Vec3d normal = e1_unit.cross(e2_unit).normalized(); + if (!m_dimensioning.arc.is_initialized()) { - const unsigned int resolution = std::max(2, 64 * angle / double(PI)); GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3 }; init_data.color = ColorRGBA::WHITE(); @@ -920,8 +919,6 @@ void GLGizmoMeasure::render_dimensioning() init_data.reserve_indices(resolution + 1); // vertices + indices - const Vec3d normal = e1_unit.cross(e2_unit).normalized(); - const double step = angle / double(resolution); for (unsigned int i = 0; i <= resolution; ++i) { const double a = step * double(i); const Vec3d v = draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(a, normal)) * e1_unit); @@ -943,8 +940,6 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e11center = center - e1.first; const double e11center_len = e11center.norm(); if (e11center_len > EPSILON && e11center.dot(e11e12) < 0.0) { - const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e1.first, e1.second)) * Geometry::scale_transform({ e11center_len, 1.0f, 1.0f })); @@ -955,13 +950,29 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e21center = center - e2.first; const double e21center_len = e21center.norm(); if (e21center_len > EPSILON) { - const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e2.first, e2.second)) * Geometry::scale_transform({ (coplanar && radius > 0.0) ? e21center_len : draw_radius, 1.0f, 1.0f })); m_dimensioning.line.render(); } + + // world coordinates + const Vec3d label_position_world = Geometry::translation_transform(center) * (draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(step * 0.5 * double(resolution), normal)) * e1_unit)); + + // screen coordinates + const std::array& viewport = camera.get_viewport(); + const Vec2d label_position_ss = DimensioningHelper::model_to_ss(label_position_world, m_volume_matrix, + camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(), viewport); + + m_imgui->set_next_window_pos(label_position_ss.x(), viewport[3] - label_position_ss.y(), ImGuiCond_Always, 0.0f, 1.0f); + m_imgui->set_next_window_bg_alpha(0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + m_imgui->begin(_L("##angle"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); + std::string txt = format_double(Geometry::rad2deg(angle)) + "°"; + m_imgui->text(txt); + m_imgui->end(); + ImGui::PopStyleVar(); }; auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { @@ -1341,7 +1352,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } - if (measure.distance_infinite.has_value()) { + if (measure.distance_infinite.has_value() && measure.distance_infinite->dist > 0.0) { double distance = measure.distance_infinite->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; @@ -1350,7 +1361,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } - if (measure.distance_strict.has_value()) { + if (measure.distance_strict.has_value() && measure.distance_strict->dist > 0.0) { double distance = measure.distance_strict->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; @@ -1359,7 +1370,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } - if (measure.distance_xyz.has_value()) { + if (measure.distance_xyz.has_value() && measure.distance_xyz->norm() > EPSILON) { Vec3d distance = *measure.distance_xyz; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; From a5a4fc4dcfb3e5f4ea267e137e70e49b89499d1f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Oct 2022 12:54:22 +0200 Subject: [PATCH 085/103] Measuring - Gizmo measure shows arrows at endpoints of angle dimensioning --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 35 ++++++++++++++++++------ 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 8a4cd17d94..f55874071d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -929,13 +929,30 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.arc.init_from(std::move(init_data)); } - // render arc + // arc const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center)); m_dimensioning.arc.render(); - // render edge 1 extension + // arrows + auto render_arrow = [this, shader, &camera, &normal, ¢er, &e1_unit, draw_radius, step, resolution](unsigned int endpoint_id) { + const double angle = (endpoint_id == 1) ? 0.0 : step * double(resolution); + const Vec3d position_model = Geometry::translation_transform(center) * (draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(angle, normal)) * e1_unit)); + const Vec3d direction_model = (endpoint_id == 1) ? -normal.cross(position_model - center).normalized() : normal.cross(position_model - center).normalized(); + const Transform3d view_model_matrix = camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(position_model) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), direction_model) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal) * + Geometry::scale_transform(camera.get_inv_zoom()); + shader->set_uniform("view_model_matrix", view_model_matrix); + m_dimensioning.triangle.render(); + }; + + glsafe(::glDisable(GL_CULL_FACE)); + render_arrow(1); + render_arrow(2); + glsafe(::glEnable(GL_CULL_FACE)); + + // edge 1 extension const Vec3d e11e12 = e1.second - e1.first; const Vec3d e11center = center - e1.first; const double e11center_len = e11center.norm(); @@ -946,7 +963,7 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.line.render(); } - // render edge 2 extension + // edge 2 extension const Vec3d e21center = center - e2.first; const double e21center_len = e21center.norm(); if (e21center_len > EPSILON) { @@ -956,12 +973,13 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.line.render(); } - // world coordinates - const Vec3d label_position_world = Geometry::translation_transform(center) * (draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(step * 0.5 * double(resolution), normal)) * e1_unit)); + // label + // label model coordinates + const Vec3d label_position_model = Geometry::translation_transform(center) * (draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(step * 0.5 * double(resolution), normal)) * e1_unit)); - // screen coordinates + // label screen coordinates const std::array& viewport = camera.get_viewport(); - const Vec2d label_position_ss = DimensioningHelper::model_to_ss(label_position_world, m_volume_matrix, + const Vec2d label_position_ss = DimensioningHelper::model_to_ss(label_position_model, m_volume_matrix, camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(), viewport); m_imgui->set_next_window_pos(label_position_ss.x(), viewport[3] - label_position_ss.y(), ImGuiCond_Always, 0.0f, 1.0f); @@ -969,8 +987,7 @@ void GLGizmoMeasure::render_dimensioning() ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); m_imgui->begin(_L("##angle"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); - std::string txt = format_double(Geometry::rad2deg(angle)) + "°"; - m_imgui->text(txt); + m_imgui->text(format_double(Geometry::rad2deg(angle)) + "°"); m_imgui->end(); ImGui::PopStyleVar(); }; From 2678ccef40ca6f50daf41e77d755e50cee782688 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Oct 2022 13:53:14 +0200 Subject: [PATCH 086/103] Follow-up of a5a4fc4dcfb3e5f4ea267e137e70e49b89499d1f - Fixed arrows orientations --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index f55874071d..ff59ba0a5c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -940,9 +940,10 @@ void GLGizmoMeasure::render_dimensioning() const double angle = (endpoint_id == 1) ? 0.0 : step * double(resolution); const Vec3d position_model = Geometry::translation_transform(center) * (draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(angle, normal)) * e1_unit)); const Vec3d direction_model = (endpoint_id == 1) ? -normal.cross(position_model - center).normalized() : normal.cross(position_model - center).normalized(); + const auto qz = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); + const auto qx = Eigen::Quaternion::FromTwoVectors(qz * Vec3d::UnitX(), direction_model); const Transform3d view_model_matrix = camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(position_model) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), direction_model) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal) * - Geometry::scale_transform(camera.get_inv_zoom()); + qx * qz * Geometry::scale_transform(camera.get_inv_zoom()); shader->set_uniform("view_model_matrix", view_model_matrix); m_dimensioning.triangle.render(); }; From ccfce4db7b039a38e6ceca168f503b7142ee8cfd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Oct 2022 13:59:11 +0200 Subject: [PATCH 087/103] Fixed warnings --- src/libslic3r/Measure.cpp | 21 ++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 4 ++-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 529c6f1231..cce63e6465 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -568,7 +568,6 @@ static AngleAndEdges angle_plane_plane(const std::tuple& p1, // Calculate edges on planes const Vec3d edge_on_plane1_unit = (origin1 - origin1_proj).normalized(); const Vec3d edge_on_plane2_unit = (origin2 - origin2_proj).normalized(); - const double edges_angle = std::acos(std::clamp(edge_on_plane1_unit.dot(edge_on_plane2_unit), -1.0, 1.0)); const double radius = std::max(10.0, std::max((origin1 - origin1_proj).norm(), (origin2 - origin2_proj).norm())); const std::pair edge_on_plane1 = { origin1_proj + radius * edge_on_plane1_unit, origin1_proj + 2.0 * radius * edge_on_plane1_unit }; const std::pair edge_on_plane2 = { origin2_proj + radius * edge_on_plane2_unit, origin2_proj + 2.0 * radius * edge_on_plane2_unit }; @@ -645,16 +644,16 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& if (f2.get_type() == SurfaceFeatureType::Edge) { std::vector distances; - auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair& e) { - const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second)); - double distance = res.distance_strict->dist; - Vec3d v2 = res.distance_strict->to; - - const Vec3d e1e2 = e.second - e.first; - const Vec3d e1v2 = v2 - e.first; - if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) - distances.emplace_back(distance, v, v2); - }; +// auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair& e) { +// const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second)); +// double distance = res.distance_strict->dist; +// Vec3d v2 = res.distance_strict->to; +// +// const Vec3d e1e2 = e.second - e.first; +// const Vec3d e1v2 = v2 - e.first; +// if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) +// distances.emplace_back(distance, v, v2); +// }; std::pair e1 = f1.get_edge(); std::pair e2 = f2.get_edge(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index ff59ba0a5c..13415877f9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -993,7 +993,7 @@ void GLGizmoMeasure::render_dimensioning() ImGui::PopStyleVar(); }; - auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + auto arc_edge_plane = [arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Plane); const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); const std::pair e1 = res.angle->e1; @@ -1006,7 +1006,7 @@ void GLGizmoMeasure::render_dimensioning() Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e2.first, e2.second), calc_radius); }; - auto arc_plane_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + auto arc_plane_plane = [arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Plane && f2.get_type() == Measure::SurfaceFeatureType::Plane); const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); const std::pair e1 = res.angle->e1; From 99e239301ae7078793a0dfc3c71f025cd02fbd95 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 Oct 2022 08:17:43 +0200 Subject: [PATCH 088/103] Measuring - Fixes in plane-plane measurement - Measurements validation - Fixes in dimensioning rendering --- src/libslic3r/Measure.cpp | 20 +++++++++++--------- src/libslic3r/Measure.hpp | 4 ++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 20 ++++++++++++-------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index cce63e6465..2905f82553 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -719,21 +719,23 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& const auto [idx1, normal1, pt1] = f1.get_plane(); const auto [idx2, normal2, pt2] = f2.get_plane(); - double angle = 0.; if (are_parallel(normal1, normal2)) { // The planes are parallel, calculate distance. - Eigen::Hyperplane plane(normal1, pt1); - result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(pt2), Vec3d::Zero(), Vec3d::Zero()}); - } else { - // Planes are not parallel, calculate angle. - angle = std::acos(std::abs(normal1.dot(normal2))); + const Eigen::Hyperplane plane(normal1, pt1); + result.distance_infinite = std::make_optional(DistAndPoints{ plane.absDistance(pt2), pt2, plane.projection(pt2) }); // TODO } - result.angle = angle_plane_plane(f1.get_plane(), f2.get_plane()); - result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + else + result.angle = angle_plane_plane(f1.get_plane(), f2.get_plane()); } - + // validation + if (result.distance_infinite.has_value() && result.distance_infinite->dist < EPSILON) + result.distance_infinite.reset(); + if (result.distance_strict.has_value() && result.distance_strict->dist < EPSILON) + result.distance_strict.reset(); + if (result.angle.has_value() && std::abs(result.angle->angle) < EPSILON) + result.angle.reset(); return result; } diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index c59c9bbbc7..df148da761 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -136,6 +136,10 @@ struct MeasurementResult { std::optional distance_strict; std::optional distance_xyz; + bool has_distance_data() const { + return distance_infinite.has_value() || distance_strict.has_value(); + } + bool has_any_data() const { return angle.has_value() || distance_infinite.has_value() || distance_strict.has_value() || distance_xyz.has_value(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 13415877f9..5c467e0203 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -1062,11 +1062,14 @@ void GLGizmoMeasure::render_dimensioning() glsafe(::glDisable(GL_DEPTH_TEST)); if (m_selected_features.second.feature.has_value()) { - // Render the arrow between the points that the backend passed: - const Measure::DistAndPoints& dap = m_measurement_result.distance_infinite.has_value() - ? *m_measurement_result.distance_infinite - : *m_measurement_result.distance_strict; - point_point(dap.from, dap.to, dap.dist); + const bool has_distance = m_measurement_result.has_distance_data(); + if (has_distance) { + // Render the arrow between the points that the backend passed: + const Measure::DistAndPoints& dap = m_measurement_result.distance_infinite.has_value() + ? *m_measurement_result.distance_infinite + : *m_measurement_result.distance_strict; + point_point(dap.from, dap.to, dap.dist); + } const Measure::SurfaceFeature* f1 = &(*m_selected_features.first.feature); const Measure::SurfaceFeature* f2 = &(*m_selected_features.second.feature); @@ -1080,7 +1083,7 @@ void GLGizmoMeasure::render_dimensioning() } // Where needed, draw also the extension of the edge to where the dist is measured: - if (ft1 == Measure::SurfaceFeatureType::Point && ft2 == Measure::SurfaceFeatureType::Edge) + if (has_distance && ft1 == Measure::SurfaceFeatureType::Point && ft2 == Measure::SurfaceFeatureType::Edge) point_edge(*f1, *f2); // Now if there is an angle to show, draw the arc: @@ -1370,7 +1373,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } - if (measure.distance_infinite.has_value() && measure.distance_infinite->dist > 0.0) { + if (measure.distance_infinite.has_value()) { double distance = measure.distance_infinite->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; @@ -1379,7 +1382,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } - if (measure.distance_strict.has_value() && measure.distance_strict->dist > 0.0) { + if (measure.distance_strict.has_value() && + (!measure.distance_infinite.has_value() || std::abs(measure.distance_strict->dist - measure.distance_infinite->dist) > EPSILON)) { double distance = measure.distance_strict->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; From cba2113bbbab540c55574edef1372086338b8836 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 Oct 2022 08:45:39 +0200 Subject: [PATCH 089/103] Measuring - Refactoring in GLGizmoMeasure imgui dialog related to units --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 5c467e0203..84b4fc0a97 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -1232,7 +1232,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; - const std::string units = use_inches ? _u8L(" (in)") : _u8L(" (mm)"); + const std::string units = use_inches ? " " + _u8L("in") : " " + _u8L("mm"); if (m_curr_feature.has_value()) { const Measure::SurfaceFeatureType feature_type = m_curr_feature->get_type(); @@ -1263,7 +1263,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } add_strings_row_to_table(*m_imgui, _u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, _u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Length") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Length"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Circle: @@ -1276,7 +1276,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit radius = ObjectManipulation::mm_to_in * radius; } add_strings_row_to_table(*m_imgui, _u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Radius") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Radius"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } @@ -1369,7 +1369,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (ImGui::BeginTable("Measure", 3)) { if (measure.angle.has_value()) { ImGui::PushID((void*)(intptr_t)1); - add_measure_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)), + add_measure_row_to_table(_u8L("Angle"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)) + "°", ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } @@ -1378,7 +1378,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (use_inches) distance = ObjectManipulation::mm_to_in * distance; ImGui::PushID((void*)(intptr_t)2); - add_measure_row_to_table(_u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + add_measure_row_to_table(_u8L("Distance Infinite"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } @@ -1388,7 +1388,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (use_inches) distance = ObjectManipulation::mm_to_in * distance; ImGui::PushID((void*)(intptr_t)3); - add_measure_row_to_table(_u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + add_measure_row_to_table(_u8L("Distance Strict"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } @@ -1397,7 +1397,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (use_inches) distance = ObjectManipulation::mm_to_in * distance; ImGui::PushID((void*)(intptr_t)4); - add_measure_row_to_table(_u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), + add_measure_row_to_table(_u8L("Distance XYZ"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } From 8fd8bada373da8c823dbe2b47811b465856fe617 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 Oct 2022 11:26:11 +0200 Subject: [PATCH 090/103] Measuring - Gizmo measure shows dimensioning for distance plane-circle --- src/libslic3r/Measure.cpp | 58 +++++++++++++++++------- src/libslic3r/Measure.hpp | 5 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 2 +- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 2905f82553..852e9268cc 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -47,6 +47,7 @@ public: std::vector get_all_features() const; std::optional get_feature(size_t face_idx, const Vec3d& point) const; std::vector> get_planes_triangle_indices() const; + const std::vector& get_plane_features(unsigned int plane_id) const; private: void update_planes(); @@ -398,6 +399,11 @@ std::vector> MeasuringImpl::get_planes_triangle_indices() const return out; } +const std::vector& MeasuringImpl::get_plane_features(unsigned int plane_id) const +{ + assert(plane_id < m_planes.size()); + return m_planes[plane_id].surface_features; +} @@ -435,6 +441,10 @@ std::vector> Measuring::get_planes_triangle_indices() const return priv->get_planes_triangle_indices(); } +const std::vector& Measuring::get_plane_features(unsigned int plane_id) const +{ + return priv->get_plane_features(plane_id); +} const AngleAndEdges AngleAndEdges::Dummy = { 0.0, Vec3d::Zero(), { Vec3d::Zero(), Vec3d::Zero() }, { Vec3d::Zero(), Vec3d::Zero() }, 0.0, true }; @@ -583,7 +593,7 @@ static AngleAndEdges angle_plane_plane(const std::tuple& p1, -MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b) +MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring) { assert(a.get_type() != SurfaceFeatureType::Undef && b.get_type() != SurfaceFeatureType::Undef); @@ -604,25 +614,25 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Edge) { - const auto& [s,e] = f2.get_edge(); - Eigen::ParametrizedLine line(s, (e-s).normalized()); - double dist_inf = line.distance(f1.get_point()); - Vec3d proj = line.projection(f1.get_point()); - double len_sq = (e-s).squaredNorm(); - double dist_start_sq = (proj-s).squaredNorm(); - double dist_end_sq = (proj-e).squaredNorm(); + const auto [s,e] = f2.get_edge(); + const Eigen::ParametrizedLine line(s, (e-s).normalized()); + const double dist_inf = line.distance(f1.get_point()); + const Vec3d proj = line.projection(f1.get_point()); + const double len_sq = (e-s).squaredNorm(); + const double dist_start_sq = (proj-s).squaredNorm(); + const double dist_end_sq = (proj-e).squaredNorm(); if (dist_start_sq < len_sq && dist_end_sq < len_sq) { // projection falls on the line - the strict distance is the same as infinite result.distance_strict = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); } else { // the result is the closer of the endpoints - bool s_is_closer = dist_start_sq < dist_end_sq; - result.distance_strict = std::make_optional(DistAndPoints{std::sqrt(std::min(dist_start_sq, dist_end_sq) + dist_inf), f1.get_point(), s_is_closer ? s : e}); + const bool s_is_closer = dist_start_sq < dist_end_sq; + result.distance_strict = std::make_optional(DistAndPoints{std::sqrt(std::min(dist_start_sq, dist_end_sq) + sqr(dist_inf)), f1.get_point(), s_is_closer ? s : e}); } result.distance_infinite = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { // Find a plane containing normal, center and the point. - const auto& [c, radius, n] = f2.get_circle(); + const auto [c, radius, n] = f2.get_circle(); Eigen::Hyperplane circle_plane(n, c); Vec3d proj = circle_plane.projection(f1.get_point()); double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) + @@ -632,7 +642,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& result.distance_strict = std::make_optional(DistAndPoints{dist, f1.get_point(), p_on_circle}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { - const auto& [idx, normal, pt] = f2.get_plane(); + const auto [idx, normal, pt] = f2.get_plane(); Eigen::Hyperplane plane(normal, pt); result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(f1.get_point()), f1.get_point(), plane.projection(f1.get_point())}); // TODO // TODO: result.distance_strict = @@ -678,7 +688,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& const std::pair e = f1.get_edge(); const auto& [center, radius, normal] = f2.get_circle(); const Vec3d e1e2 = (e.second - e.first); - const Vec3d e1e2_unit = (e.second - e.first).normalized(); + const Vec3d e1e2_unit = e1e2.normalized(); std::vector distances; distances.emplace_back(*get_measurement(SurfaceFeature(e.first), f2).distance_strict); @@ -709,14 +719,30 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { - result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + assert(measuring != nullptr); + + const auto [center, radius, normal1] = f1.get_circle(); + const auto [idx2, normal2, origin2] = f2.get_plane(); + + const bool coplanar = are_parallel(normal1, normal2) && Eigen::Hyperplane(normal1, center).absDistance(origin2) < EPSILON; + if (!coplanar) { + const std::vector& plane_features = measuring->get_plane_features(idx2); + std::vector distances; + for (const SurfaceFeature& sf : plane_features) { + if (sf.get_type() == SurfaceFeatureType::Edge) + distances.push_back(*get_measurement(sf, f1).distance_infinite); + } + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); + } } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Plane) { - assert(f2.get_type() == SurfaceFeatureType::Plane); - const auto [idx1, normal1, pt1] = f1.get_plane(); const auto [idx2, normal2, pt2] = f2.get_plane(); diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index df148da761..2e17eea5c5 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -105,6 +105,9 @@ public: // call too often. std::vector> get_planes_triangle_indices() const; + // Returns the surface features of the plane with the given index + const std::vector& get_plane_features(unsigned int plane_id) const; + private: std::unique_ptr priv; }; @@ -146,7 +149,7 @@ struct MeasurementResult { }; // Returns distance/angle between two SurfaceFeatures. -MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b); +MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring = nullptr); inline Vec3d edge_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); } inline Vec3d edge_direction(const std::pair& e) { return edge_direction(e.first, e.second); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 84b4fc0a97..31ce08fa74 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -175,7 +175,7 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) } if (m_selected_features != selected_features_old && m_selected_features.second.feature.has_value()) - m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); + m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature, m_measuring.get()); return true; } From 836b45a2ed01ce07061685911efe3831d926cf32 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 Oct 2022 12:36:38 +0200 Subject: [PATCH 091/103] Measuring - Gizmo measure shows dimensioning for distance edge-plane --- src/libslic3r/Measure.cpp | 63 +++++++++++++++++++++--- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 39 ++++++++------- 2 files changed, 77 insertions(+), 25 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 852e9268cc..ea68503254 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -708,7 +708,45 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& result.distance_infinite = std::make_optional(DistAndPoints{it->dist, it->from, it->to}); /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { - result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + assert(measuring != nullptr); + + const auto [from, to] = f1.get_edge(); + const auto [idx, normal, origin] = f2.get_plane(); + + const Vec3d edge_unit = (to - from).normalized(); + if (are_perpendicular(edge_unit, normal)) { + std::vector distances; + const Eigen::Hyperplane plane(normal, origin); + distances.push_back(DistAndPoints{ plane.absDistance(from), from, plane.projection(from) }); + distances.push_back(DistAndPoints{ plane.absDistance(to), to, plane.projection(to) }); + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); + } + else { + const std::vector& plane_features = measuring->get_plane_features(idx); + std::vector distances; + for (const SurfaceFeature& sf : plane_features) { + if (sf.get_type() == SurfaceFeatureType::Edge) { + const auto m = get_measurement(sf, f1); + if (!m.distance_infinite.has_value()) { + distances.clear(); + break; + } + else + distances.push_back(*m.distance_infinite); + } + } + if (!distances.empty()) { + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); + } + } result.angle = angle_edge_plane(f1.get_edge(), f2.get_plane()); } /////////////////////////////////////////////////////////////////////////// @@ -729,14 +767,23 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& const std::vector& plane_features = measuring->get_plane_features(idx2); std::vector distances; for (const SurfaceFeature& sf : plane_features) { - if (sf.get_type() == SurfaceFeatureType::Edge) - distances.push_back(*get_measurement(sf, f1).distance_infinite); + if (sf.get_type() == SurfaceFeatureType::Edge) { + const auto m = get_measurement(sf, f1); + if (!m.distance_infinite.has_value()) { + distances.clear(); + break; + } + else + distances.push_back(*m.distance_infinite); + } + } + if (!distances.empty()) { + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); } - auto it = std::min_element(distances.begin(), distances.end(), - [](const DistAndPoints& item1, const DistAndPoints& item2) { - return item1.dist < item2.dist; - }); - result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); } } /////////////////////////////////////////////////////////////////////////// diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 31ce08fa74..051a7ee40a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -891,13 +891,15 @@ void GLGizmoMeasure::render_dimensioning() auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, double radius = 0.0) { assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Edge); - const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); - const double angle = res.angle->angle; - const Vec3d center = res.angle->center; - const std::pair e1 = res.angle->e1; - const std::pair e2 = res.angle->e2; - const double calc_radius = res.angle->radius; - const bool coplanar = res.angle->coplanar; + if (!m_measurement_result.angle.has_value()) + return; + + const double angle = m_measurement_result.angle->angle; + const Vec3d center = m_measurement_result.angle->center; + const std::pair e1 = m_measurement_result.angle->e1; + const std::pair e2 = m_measurement_result.angle->e2; + const double calc_radius = m_measurement_result.angle->radius; + const bool coplanar = m_measurement_result.angle->coplanar; if (std::abs(angle) < EPSILON || std::abs(calc_radius) < EPSILON) return; @@ -993,12 +995,15 @@ void GLGizmoMeasure::render_dimensioning() ImGui::PopStyleVar(); }; - auto arc_edge_plane = [arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Plane); - const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); - const std::pair e1 = res.angle->e1; - const std::pair e2 = res.angle->e2; - const double calc_radius = res.angle->radius; + if (!m_measurement_result.angle.has_value()) + return; + + const std::pair e1 = m_measurement_result.angle->e1; + const std::pair e2 = m_measurement_result.angle->e2; + const double calc_radius = m_measurement_result.angle->radius; + if (calc_radius == 0.0) return; @@ -1006,12 +1011,12 @@ void GLGizmoMeasure::render_dimensioning() Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e2.first, e2.second), calc_radius); }; - auto arc_plane_plane = [arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + auto arc_plane_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Plane && f2.get_type() == Measure::SurfaceFeatureType::Plane); - const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); - const std::pair e1 = res.angle->e1; - const std::pair e2 = res.angle->e2; - const double calc_radius = res.angle->radius; + const std::pair e1 = m_measurement_result.angle->e1; + const std::pair e2 = m_measurement_result.angle->e2; + const double calc_radius = m_measurement_result.angle->radius; + if (calc_radius == 0.0) return; From 19222137253a2ef453927820b15e3b646cfebc50 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 6 Oct 2022 14:23:45 +0200 Subject: [PATCH 092/103] Measuring: prototype for uniformly scale a volume by editing the value of the shown distance --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 111 ++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + src/slic3r/GUI/ImGuiWrapper.cpp | 63 ++++++------- src/slic3r/GUI/ImGuiWrapper.hpp | 4 +- 4 files changed, 110 insertions(+), 69 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 051a7ee40a..3e83418ef4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -220,6 +220,7 @@ void GLGizmoMeasure::data_changed() m_last_plane_idx = -1; m_selected_features.reset(); m_selection_raycasters.clear(); + m_editing_distance = false; } bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) @@ -256,6 +257,7 @@ void GLGizmoMeasure::on_set_state() m_curr_feature.reset(); m_curr_point_on_feature_position.reset(); restore_scene_raycasters_state(); + m_editing_distance = false; } else { m_mode = EMode::BasicSelection; @@ -840,18 +842,79 @@ void GLGizmoMeasure::render_dimensioning() ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q12ss); m_dimensioning.triangle.render(); + const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; + const double value = use_inches ? ObjectManipulation::mm_to_in * distance : distance; + const std::string value_str = format_double(value); + const std::string units = use_inches ? _u8L("in") : _u8L("mm"); + const float value_str_width = 20.0f + ImGui::CalcTextSize(value_str.c_str()).x; + static double edit_value = 0.0; + const Vec2d label_position = 0.5 * (v1ss + v2ss); m_imgui->set_next_window_pos(label_position.x(), viewport[3] - label_position.y(), ImGuiCond_Always, 0.0f, 1.0f); m_imgui->set_next_window_bg_alpha(0.0f); + + if (!m_editing_distance) { + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); + m_imgui->begin(std::string("distance"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); + ImGui::AlignTextToFramePadding(); + m_imgui->text(value_str + " " + units); + ImGui::SameLine(); + if (m_imgui->image_button(ImGui::SliderFloatEditBtnIcon, _L("Edit to scale"))) { + m_editing_distance = true; + edit_value = value; + m_imgui->requires_extra_frame(); + } + m_imgui->end(); + ImGui::PopStyleVar(3); + } + + auto perform_scale = [this, value](double new_value, double old_value) { + if (new_value == old_value || new_value <= 0.0) + return; + + const double ratio = new_value / old_value; + wxGetApp().plater()->take_snapshot(_L("Scale")); + + TransformationType type; + type.set_world(); + type.set_relative(); + type.set_joint(); + + // apply scale + Selection& selection = m_parent.get_selection(); + selection.setup_cache(); + selection.scale(ratio * Vec3d::Ones(), type); + wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot + wxGetApp().obj_manipul()->set_dirty(); + }; + + if (m_editing_distance && !ImGui::IsPopupOpen("distance_popup")) + ImGui::OpenPopup("distance_popup"); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); - m_imgui->begin(_L("##distance"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); - ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); - const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; - const std::string txt = use_inches ? format_double(ObjectManipulation::mm_to_in * distance) + " " + _u8L("in") : - format_double(distance) + " " + _u8L("mm"); - m_imgui->text(txt); - m_imgui->end(); - ImGui::PopStyleVar(); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 4.0f, 0.0f }); + if (ImGui::BeginPopupModal("distance_popup", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration)) { + ImGui::PushItemWidth(value_str_width); + if (ImGui::InputDouble("##distance", &edit_value, 0.0f, 0.0f, "%.3f")) { + } + ImGui::SameLine(); + if (m_imgui->button(_u8L("Scale"))) { + perform_scale(edit_value, value); + m_editing_distance = false; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (m_imgui->button(_u8L("Cancel"))) { + m_editing_distance = false; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::PopStyleVar(4); }; auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { @@ -1194,7 +1257,10 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit static float last_y = 0.0f; static float last_h = 0.0f; - m_imgui->begin(_L("Measure tool"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + if (m_editing_distance) + return; + + m_imgui->begin(_u8L("Measure tool"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); // adjust window position to avoid overlap the view toolbar const float win_h = ImGui::GetWindowHeight(); @@ -1341,28 +1407,11 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::TableSetColumnIndex(1); m_imgui->text_colored(col_2_color, col_2); ImGui::TableSetColumnIndex(2); - ImGuiIO& io = ImGui::GetIO(); - const ImTextureID tex_id = io.Fonts->TexID; - assert(io.Fonts->TexWidth > 0 && io.Fonts->TexHeight > 0); - float inv_tex_w = 1.0f / float(io.Fonts->TexWidth); - float inv_tex_h = 1.0f / float(io.Fonts->TexHeight); - const ImFontAtlasCustomRect* const rect = m_imgui->GetTextureCustomRect(ImGui::ClipboardBtnIcon); - const ImVec2 size = { float(rect->Width), float(rect->Height) }; - const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h); - const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h); - ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f }); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, 1.0f }); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.25f, 0.25f, 0.25f, 1.0f }); - if (m_imgui->image_button(tex_id, size, uv0, uv1)) { + if (m_imgui->image_button(ImGui::ClipboardBtnIcon, _L("Copy to clipboard"))) { wxTheClipboard->Open(); wxTheClipboard->SetData(new wxTextDataObject(col_1 + ": " + col_2)); wxTheClipboard->Close(); } - ImGui::PopStyleColor(3); - if (ImGui::IsItemHovered()) { - const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; - m_imgui->tooltip(into_u8(_L("Copy to clipboard")).c_str(), max_tooltip_width); - } }; if (m_selected_features.second.feature.has_value()) { @@ -1373,7 +1422,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit m_imgui->text(_u8L("Measure") + ":"); if (ImGui::BeginTable("Measure", 3)) { if (measure.angle.has_value()) { - ImGui::PushID((void*)(intptr_t)1); + ImGui::PushID("ClipboardAngle"); add_measure_row_to_table(_u8L("Angle"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)) + "°", ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); @@ -1382,7 +1431,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit double distance = measure.distance_infinite->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID((void*)(intptr_t)2); + ImGui::PushID("ClipboardDistanceInfinite"); add_measure_row_to_table(_u8L("Distance Infinite"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); @@ -1392,7 +1441,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit double distance = measure.distance_strict->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID((void*)(intptr_t)3); + ImGui::PushID("ClipboardDistanceStrict"); add_measure_row_to_table(_u8L("Distance Strict"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); @@ -1401,7 +1450,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit Vec3d distance = *measure.distance_xyz; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID((void*)(intptr_t)4); + ImGui::PushID("ClipboardDistanceXYZ"); add_measure_row_to_table(_u8L("Distance XYZ"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index a5312ebf57..bba23fb6b8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -118,6 +118,7 @@ class GLGizmoMeasure : public GLGizmoBase KeyAutoRepeatFilter m_ctrl_kar_filter; SelectedFeatures m_selected_features; + bool m_editing_distance{ false }; void update_if_needed(); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 4ce9dfefc0..74e125053e 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -431,36 +431,6 @@ bool ImGuiWrapper::draw_radio_button(const std::string& name, float size, bool a } #endif // ENABLE_PREVIEW_LAYOUT -bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format) -{ - return ImGui::InputDouble(label.c_str(), const_cast(&value), 0.0f, 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal); -} - -bool ImGuiWrapper::input_double(const wxString &label, const double &value, const std::string &format) -{ - auto label_utf8 = into_u8(label); - return input_double(label_utf8, value, format); -} - -bool ImGuiWrapper::input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format) -{ - bool value_changed = false; - - ImGui::BeginGroup(); - - for (int i = 0; i < 3; ++i) - { - std::string item_label = (i == 0) ? "X" : ((i == 1) ? "Y" : "Z"); - ImGui::PushID(i); - ImGui::PushItemWidth(width); - value_changed |= ImGui::InputDouble(item_label.c_str(), const_cast(&value(i)), 0.0f, 0.0f, format.c_str()); - ImGui::PopID(); - } - ImGui::EndGroup(); - - return value_changed; -} - bool ImGuiWrapper::checkbox(const wxString &label, bool &value) { auto label_utf8 = into_u8(label); @@ -519,20 +489,20 @@ void ImGuiWrapper::text_wrapped(const wxString &label, float wrap_width) void ImGuiWrapper::tooltip(const char *label, float wrap_width) { + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 4.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 8.0f, 8.0f }); ImGui::BeginTooltip(); ImGui::PushTextWrapPos(wrap_width); ImGui::TextUnformatted(label); ImGui::PopTextWrapPos(); ImGui::EndTooltip(); + ImGui::PopStyleVar(3); } void ImGuiWrapper::tooltip(const wxString &label, float wrap_width) { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(wrap_width); - ImGui::TextUnformatted(label.ToUTF8().data()); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); + tooltip(label.ToUTF8().data(), wrap_width); } ImVec2 ImGuiWrapper::get_slider_icon_size() const @@ -684,6 +654,29 @@ bool ImGuiWrapper::image_button(ImTextureID user_texture_id, const ImVec2& size, return image_button_ex(id, user_texture_id, size, uv0, uv1, padding, bg_col, tint_col, flags); } +bool ImGuiWrapper::image_button(const wchar_t icon, const wxString& tooltip) +{ + const ImGuiIO& io = ImGui::GetIO(); + const ImTextureID tex_id = io.Fonts->TexID; + assert(io.Fonts->TexWidth > 0 && io.Fonts->TexHeight > 0); + const float inv_tex_w = 1.0f / float(io.Fonts->TexWidth); + const float inv_tex_h = 1.0f / float(io.Fonts->TexHeight); + const ImFontAtlasCustomRect* const rect = GetTextureCustomRect(icon); + const ImVec2 size = { float(rect->Width), float(rect->Height) }; + const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h); + const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h); + ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f }); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, 1.0f }); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.25f, 0.25f, 0.25f, 1.0f }); + const bool res = image_button(tex_id, size, uv0, uv1); + ImGui::PopStyleColor(3); + + if (!tooltip.empty() && ImGui::IsItemHovered()) + this->tooltip(tooltip, ImGui::GetFontSize() * 20.0f); + + return res; +} + bool ImGuiWrapper::combo(const wxString& label, const std::vector& options, int& selection, ImGuiComboFlags flags) { // this is to force the label to the left of the widget: diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index f461bc970a..f75200b071 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -95,9 +95,6 @@ public: #if ENABLE_PREVIEW_LAYOUT bool draw_radio_button(const std::string& name, float size, bool active, std::function draw_callback); #endif // ENABLE_PREVIEW_LAYOUT - bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f"); - bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f"); - bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f"); bool checkbox(const wxString &label, bool &value); void text(const char *label); void text(const std::string &label); @@ -118,6 +115,7 @@ public: bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = {}, bool show_edit_btn = true); bool image_button(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0.0, 0.0), const ImVec2& uv1 = ImVec2(1.0, 1.0), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0.0, 0.0, 0.0, 0.0), const ImVec4& tint_col = ImVec4(1.0, 1.0, 1.0, 1.0), ImGuiButtonFlags flags = 0); + bool image_button(const wchar_t icon, const wxString& tooltip = L""); // Use selection = -1 to not mark any option as selected bool combo(const wxString& label, const std::vector& options, int& selection, ImGuiComboFlags flags = 0); From 8312dc24547df8e622bde814efb1b3e0b15bab40 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 6 Oct 2022 14:47:25 +0200 Subject: [PATCH 093/103] Measuring: Fixed rendering of point features when the object is scaled --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 157 ++++++++++++----------- 1 file changed, 79 insertions(+), 78 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 3e83418ef4..a6efc09e78 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -467,93 +467,94 @@ void GLGizmoMeasure::on_render() auto render_feature = [this, set_matrix_uniforms](const Measure::SurfaceFeature& feature, const std::vector& colors, const Transform3d& model_matrix, float inv_zoom, bool update_raycasters) { - switch (feature.get_type()) - { - default: { assert(false); break; } - case Measure::SurfaceFeatureType::Point: - { - const Vec3d& position = feature.get_point(); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(feature_matrix); - m_sphere.model.set_color(colors.front()); - m_sphere.model.render(); - if (update_raycasters) { - auto it = m_raycasters.find(POINT_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); - } - break; - } - case Measure::SurfaceFeatureType::Circle: - { - const auto& [center, radius, normal] = feature.get_circle(); - // render center - const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(center_matrix); - m_sphere.model.set_color(colors.front()); - m_sphere.model.render(); - if (update_raycasters) { - auto it = m_raycasters.find(POINT_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(center_matrix); - } - // render circle - const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); - set_matrix_uniforms(circle_matrix); - m_circle.model.set_color(colors.back()); - m_circle.model.render(); - if (update_raycasters) { - auto it = m_raycasters.find(CIRCLE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(circle_matrix); - } - break; - } - case Measure::SurfaceFeatureType::Edge: - { - const auto& [start, end] = feature.get_edge(); - // render extra point - const std::optional extra = feature.get_extra_point(); - if (extra.has_value()) { - const Transform3d point_matrix = model_matrix * Geometry::translation_transform(*extra) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(point_matrix); + const Transform3d model_matrix_scale_inverse = Geometry::Transformation(model_matrix).get_scaling_factor_matrix().inverse(); + switch (feature.get_type()) + { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + const Vec3d& position = feature.get_point(); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * model_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(feature_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); if (update_raycasters) { auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(point_matrix); + it->second->set_transform(feature_matrix); } + break; } - // render edge - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start) * - Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); - set_matrix_uniforms(feature_matrix); - m_cylinder.model.set_color(colors.back()); - m_cylinder.model.render(); - if (update_raycasters) { - auto it = m_raycasters.find(EDGE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, normal] = feature.get_circle(); + // render center + const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * model_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(center_matrix); + m_sphere.model.set_color(colors.front()); + m_sphere.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(center_matrix); + } + // render circle + const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); + set_matrix_uniforms(circle_matrix); + m_circle.model.set_color(colors.back()); + m_circle.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(CIRCLE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(circle_matrix); + } + break; + } + case Measure::SurfaceFeatureType::Edge: + { + const auto& [start, end] = feature.get_edge(); + // render extra point + const std::optional extra = feature.get_extra_point(); + if (extra.has_value()) { + const Transform3d point_matrix = model_matrix * Geometry::translation_transform(*extra) * model_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(point_matrix); + m_sphere.model.set_color(colors.front()); + m_sphere.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(point_matrix); + } + } + // render edge + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start) * + Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); + set_matrix_uniforms(feature_matrix); + m_cylinder.model.set_color(colors.back()); + m_cylinder.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(EDGE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); + } + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto& [idx, normal, pt] = feature.get_plane(); + assert(idx < m_plane_models_cache.size()); + set_matrix_uniforms(model_matrix); + m_plane_models_cache[idx].set_color(colors.front()); + m_plane_models_cache[idx].render(); + if (update_raycasters) { + auto it = m_raycasters.find(PLANE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(model_matrix); + } + break; } - break; - } - case Measure::SurfaceFeatureType::Plane: - { - const auto& [idx, normal, pt] = feature.get_plane(); - assert(idx < m_plane_models_cache.size()); - set_matrix_uniforms(model_matrix); - m_plane_models_cache[idx].set_color(colors.front()); - m_plane_models_cache[idx].render(); - if (update_raycasters) { - auto it = m_raycasters.find(PLANE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(model_matrix); } - break; - } - } }; auto hover_selection_color = [this]() { From 1a0294e892476324936907eb8028a43c93c12b7b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 7 Oct 2022 08:40:23 +0200 Subject: [PATCH 094/103] Measuring - Gizmo measure disabled for scaled volumes --- src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 903612f29b..1d13d92f10 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -78,6 +78,7 @@ #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) // Enable Measure Gizmo #define ENABLE_MEASURE_GIZMO (1 && ENABLE_RAYCAST_PICKING) +#define DISABLE_MEASURE_GIZMO_FOR_SCALED_VOLUMES (1 && ENABLE_MEASURE_GIZMO) #define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_MEASURE_GIZMO) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index a6efc09e78..f2638d0588 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -290,8 +290,12 @@ bool GLGizmoMeasure::on_is_activable() const bool res = (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) ? selection.is_single_full_instance() : selection.is_single_volume() || selection.is_single_volume_instance(); - if (res) + if (res) { res &= !selection.get_first_volume()->is_sinking(); +#if DISABLE_MEASURE_GIZMO_FOR_SCALED_VOLUMES + res &= Geometry::Transformation(selection.get_first_volume()->world_matrix()).get_scaling_factor().isApprox(Vec3d::Ones()); +#endif // DISABLE_MEASURE_GIZMO_FOR_SCALED_VOLUMES + } return res; } From 5ebfa73f374265ec1f035ac7d1c66d587b990530 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 7 Oct 2022 09:57:13 +0200 Subject: [PATCH 095/103] Measuring - Gizmo measure - Disable background fadeout animation when showing imgui modal dialog to edit distance --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 1 + src/slic3r/GUI/ImGuiWrapper.cpp | 7 +++++++ src/slic3r/GUI/ImGuiWrapper.hpp | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index f2638d0588..d18584a870 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -903,6 +903,7 @@ void GLGizmoMeasure::render_dimensioning() ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 4.0f, 0.0f }); if (ImGui::BeginPopupModal("distance_popup", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration)) { + m_imgui->disable_background_fadeout_animation(); ImGui::PushItemWidth(value_str_width); if (ImGui::InputDouble("##distance", &edit_value, 0.0f, 0.0f, "%.3f")) { } diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 74e125053e..7484724097 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1145,6 +1145,13 @@ ImFontAtlasCustomRect* ImGuiWrapper::GetTextureCustomRect(const wchar_t& tex_id) } #endif // ENABLE_LEGEND_TOOLBAR_ICONS +#if ENABLE_MEASURE_GIZMO +void ImGuiWrapper::disable_background_fadeout_animation() +{ + GImGui->DimBgRatio = 1.0f; +} +#endif // ENABLE_MEASURE_GIZMO + ImU32 ImGuiWrapper::to_ImU32(const ColorRGBA& color) { return ImGui::GetColorU32({ color.r(), color.g(), color.b(), color.a() }); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index f75200b071..bf4018fb15 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -136,6 +136,10 @@ public: void set_requires_extra_frame() { m_requires_extra_frame = true; } void reset_requires_extra_frame() { m_requires_extra_frame = false; } +#if ENABLE_MEASURE_GIZMO + void disable_background_fadeout_animation(); +#endif // ENABLE_MEASURE_GIZMO + static ImU32 to_ImU32(const ColorRGBA& color); static ImVec4 to_ImVec4(const ColorRGBA& color); static ColorRGBA from_ImU32(const ImU32& color); From 0fd01f36bdf3f3a6bd0b4a564f06b10e5cce9367 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 7 Oct 2022 10:42:01 +0200 Subject: [PATCH 096/103] Measuring - Gizmo measure - Handle [Enter] and [Esc] keys input events in imgui modal dialog to edit distance --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 78 +++++++++++++----------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index d18584a870..3e1d9824d4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -848,10 +848,10 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.triangle.render(); const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; - const double value = use_inches ? ObjectManipulation::mm_to_in * distance : distance; - const std::string value_str = format_double(value); + const double curr_value = use_inches ? ObjectManipulation::mm_to_in * distance : distance; + const std::string curr_value_str = format_double(curr_value); const std::string units = use_inches ? _u8L("in") : _u8L("mm"); - const float value_str_width = 20.0f + ImGui::CalcTextSize(value_str.c_str()).x; + const float value_str_width = 20.0f + ImGui::CalcTextSize(curr_value_str.c_str()).x; static double edit_value = 0.0; const Vec2d label_position = 0.5 * (v1ss + v2ss); @@ -864,37 +864,17 @@ void GLGizmoMeasure::render_dimensioning() ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); m_imgui->begin(std::string("distance"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); ImGui::AlignTextToFramePadding(); - m_imgui->text(value_str + " " + units); + m_imgui->text(curr_value_str + " " + units); ImGui::SameLine(); if (m_imgui->image_button(ImGui::SliderFloatEditBtnIcon, _L("Edit to scale"))) { m_editing_distance = true; - edit_value = value; + edit_value = curr_value; m_imgui->requires_extra_frame(); } m_imgui->end(); ImGui::PopStyleVar(3); } - auto perform_scale = [this, value](double new_value, double old_value) { - if (new_value == old_value || new_value <= 0.0) - return; - - const double ratio = new_value / old_value; - wxGetApp().plater()->take_snapshot(_L("Scale")); - - TransformationType type; - type.set_world(); - type.set_relative(); - type.set_joint(); - - // apply scale - Selection& selection = m_parent.get_selection(); - selection.setup_cache(); - selection.scale(ratio * Vec3d::Ones(), type); - wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot - wxGetApp().obj_manipul()->set_dirty(); - }; - if (m_editing_distance && !ImGui::IsPopupOpen("distance_popup")) ImGui::OpenPopup("distance_popup"); @@ -903,21 +883,51 @@ void GLGizmoMeasure::render_dimensioning() ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 4.0f, 0.0f }); if (ImGui::BeginPopupModal("distance_popup", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration)) { + auto perform_scale = [this](double new_value, double old_value) { + if (new_value == old_value || new_value <= 0.0) + return; + + const double ratio = new_value / old_value; + wxGetApp().plater()->take_snapshot(_L("Scale")); + + TransformationType type; + type.set_world(); + type.set_relative(); + type.set_joint(); + + // apply scale + Selection& selection = m_parent.get_selection(); + selection.setup_cache(); + selection.scale(ratio * Vec3d::Ones(), type); + wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot + wxGetApp().obj_manipul()->set_dirty(); + }; + auto action_exit = [this]() { + m_editing_distance = false; + ImGui::CloseCurrentPopup(); + }; + auto action_scale = [this, perform_scale, action_exit](double new_value, double old_value) { + perform_scale(new_value, old_value); + action_exit(); + }; + m_imgui->disable_background_fadeout_animation(); ImGui::PushItemWidth(value_str_width); if (ImGui::InputDouble("##distance", &edit_value, 0.0f, 0.0f, "%.3f")) { } + + // handle keys input + if (ImGui::IsKeyPressedMap(ImGuiKey_Enter) || ImGui::IsKeyPressedMap(ImGuiKey_KeyPadEnter)) + action_scale(edit_value, curr_value); + else if (ImGui::IsKeyPressedMap(ImGuiKey_Escape)) + action_exit(); + ImGui::SameLine(); - if (m_imgui->button(_u8L("Scale"))) { - perform_scale(edit_value, value); - m_editing_distance = false; - ImGui::CloseCurrentPopup(); - } + if (m_imgui->button(_u8L("Scale"))) + action_scale(edit_value, curr_value); ImGui::SameLine(); - if (m_imgui->button(_u8L("Cancel"))) { - m_editing_distance = false; - ImGui::CloseCurrentPopup(); - } + if (m_imgui->button(_u8L("Cancel"))) + action_exit(); ImGui::EndPopup(); } ImGui::PopStyleVar(4); From 2fb59e66c2b6f07a25cfa873fdeb1d820b2d78c4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 7 Oct 2022 12:42:30 +0200 Subject: [PATCH 097/103] easuring - Gizmo measure - Reworked imgui dialog layout to avoid change in size in dependence of hovered or selected features --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 266 +++++++++++++---------- 1 file changed, 151 insertions(+), 115 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 3e1d9824d4..15d6ea761f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -63,7 +63,7 @@ static std::string surface_feature_type_as_string(Measure::SurfaceFeatureType ty switch (type) { default: - case Measure::SurfaceFeatureType::Undef: { return _u8L("Undefined"); } + case Measure::SurfaceFeatureType::Undef: { return _u8L("No feature"); } case Measure::SurfaceFeatureType::Point: { return _u8L("Vertex"); } case Measure::SurfaceFeatureType::Edge: { return _u8L("Edge"); } case Measure::SurfaceFeatureType::Circle: { return _u8L("Circle"); } @@ -1292,6 +1292,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } if (ImGui::BeginTable("Commands", 2)) { + unsigned int row_count = 1; add_row_to_table( [this]() { m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Left mouse button")); @@ -1310,93 +1311,121 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } ); - if (m_selected_features.first.feature.has_value()) + if (m_selected_features.first.feature.has_value()) { add_strings_row_to_table(*m_imgui, CTRL_STR + "+" + _u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++row_count; + } - if (m_mode == EMode::BasicSelection && m_hover_id != -1) + if (m_mode == EMode::BasicSelection && m_hover_id != -1) { add_strings_row_to_table(*m_imgui, CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++row_count; + } + + // add dummy rows to keep dialog size fixed + for (unsigned int i = row_count; i < 3; ++i) { + add_strings_row_to_table(*m_imgui, " ", ImGuiWrapper::COL_ORANGE_LIGHT, " ", ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + ImGui::EndTable(); } const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; const std::string units = use_inches ? " " + _u8L("in") : " " + _u8L("mm"); - if (m_curr_feature.has_value()) { - const Measure::SurfaceFeatureType feature_type = m_curr_feature->get_type(); + const Measure::SurfaceFeatureType feature_type = m_curr_feature.has_value() ? m_curr_feature->get_type() : Measure::SurfaceFeatureType::Undef; + bool data_text_set = false; + ImGui::Separator(); + if (feature_type != Measure::SurfaceFeatureType::Undef) { if (m_mode == EMode::BasicSelection) { - if (feature_type != Measure::SurfaceFeatureType::Undef) { - ImGui::Separator(); - m_imgui->text(surface_feature_type_as_string(feature_type) + ":"); - if (ImGui::BeginTable("Data", 2)) { - switch (feature_type) - { - default: { assert(false); break; } - case Measure::SurfaceFeatureType::Point: - { - Vec3d position = m_volume_matrix * m_curr_feature->get_point(); - if (use_inches) - position = ObjectManipulation::mm_to_in * position; - add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - break; - } - case Measure::SurfaceFeatureType::Edge: - { - auto [from, to] = m_curr_feature->get_edge(); - from = m_volume_matrix * from; - to = m_volume_matrix * to; - if (use_inches) { - from = ObjectManipulation::mm_to_in * from; - to = ObjectManipulation::mm_to_in * to; - } - add_strings_row_to_table(*m_imgui, _u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Length"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - auto [center, radius, normal] = m_curr_feature->get_circle(); - center = m_volume_matrix * center; - normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - if (use_inches) { - center = ObjectManipulation::mm_to_in * center; - radius = ObjectManipulation::mm_to_in * radius; - } - add_strings_row_to_table(*m_imgui, _u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Radius"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - auto [idx, normal, origin] = m_curr_feature->get_plane(); - origin = m_volume_matrix * origin; - normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - if (use_inches) - origin = ObjectManipulation::mm_to_in * origin; - add_strings_row_to_table(*m_imgui, _u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - break; - } - } - ImGui::EndTable(); - } - } + m_imgui->text(surface_feature_type_as_string(feature_type)); + data_text_set = true; } else if (m_mode == EMode::ExtendedSelection) { if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) { - ImGui::Separator(); - m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id) + ":"); - if (ImGui::BeginTable("Data", 2)) { - Vec3d position = m_volume_matrix * *m_curr_point_on_feature_position; - if (use_inches) - position = ObjectManipulation::mm_to_in * position; - add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - ImGui::EndTable(); - } + m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id)); + data_text_set = true; } } } + if (!data_text_set) + m_imgui->text(_u8L("No feature")); + + const unsigned int max_data_row_count = 3; + unsigned int data_row_count = 0; + if (ImGui::BeginTable("Data", 2)) { + if (m_mode == EMode::BasicSelection) { + switch (feature_type) + { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + Vec3d position = m_volume_matrix * m_curr_feature->get_point(); + if (use_inches) + position = ObjectManipulation::mm_to_in * position; + add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + data_row_count = 1; + break; + } + case Measure::SurfaceFeatureType::Edge: + { + auto [from, to] = m_curr_feature->get_edge(); + from = m_volume_matrix * from; + to = m_volume_matrix * to; + if (use_inches) { + from = ObjectManipulation::mm_to_in * from; + to = ObjectManipulation::mm_to_in * to; + } + add_strings_row_to_table(*m_imgui, _u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Length"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + data_row_count = 3; + break; + } + case Measure::SurfaceFeatureType::Circle: + { + auto [center, radius, normal] = m_curr_feature->get_circle(); + center = m_volume_matrix * center; + normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + if (use_inches) { + center = ObjectManipulation::mm_to_in * center; + radius = ObjectManipulation::mm_to_in * radius; + } + add_strings_row_to_table(*m_imgui, _u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Radius"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + data_row_count = 3; + break; + } + case Measure::SurfaceFeatureType::Plane: + { + auto [idx, normal, origin] = m_curr_feature->get_plane(); + origin = m_volume_matrix * origin; + normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + if (use_inches) + origin = ObjectManipulation::mm_to_in * origin; + add_strings_row_to_table(*m_imgui, _u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + data_row_count = 2; + break; + } + } + } + else { + if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) { + Vec3d position = m_volume_matrix * *m_curr_point_on_feature_position; + if (use_inches) + position = ObjectManipulation::mm_to_in * position; + add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + data_row_count = 1; + } + } + + // add dummy rows to keep dialog size fixed + for (unsigned int i = data_row_count; i < max_data_row_count; ++i) { + add_strings_row_to_table(*m_imgui, " ", ImGuiWrapper::COL_ORANGE_LIGHT, " ", ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + ImGui::EndTable(); + } ImGui::Separator(); const ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersH; @@ -1430,52 +1459,59 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } }; - if (m_selected_features.second.feature.has_value()) { - const Measure::MeasurementResult& measure = m_measurement_result; + ImGui::Separator(); + m_imgui->text(_u8L("Measure")); - ImGui::Separator(); - if (measure.has_any_data()) { - m_imgui->text(_u8L("Measure") + ":"); - if (ImGui::BeginTable("Measure", 3)) { - if (measure.angle.has_value()) { - ImGui::PushID("ClipboardAngle"); - add_measure_row_to_table(_u8L("Angle"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)) + "°", - ImGui::GetStyleColorVec4(ImGuiCol_Text)); - ImGui::PopID(); - } - if (measure.distance_infinite.has_value()) { - double distance = measure.distance_infinite->dist; - if (use_inches) - distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID("ClipboardDistanceInfinite"); - add_measure_row_to_table(_u8L("Distance Infinite"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, - ImGui::GetStyleColorVec4(ImGuiCol_Text)); - ImGui::PopID(); - } - if (measure.distance_strict.has_value() && - (!measure.distance_infinite.has_value() || std::abs(measure.distance_strict->dist - measure.distance_infinite->dist) > EPSILON)) { - double distance = measure.distance_strict->dist; - if (use_inches) - distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID("ClipboardDistanceStrict"); - add_measure_row_to_table(_u8L("Distance Strict"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, - ImGui::GetStyleColorVec4(ImGuiCol_Text)); - ImGui::PopID(); - } - if (measure.distance_xyz.has_value() && measure.distance_xyz->norm() > EPSILON) { - Vec3d distance = *measure.distance_xyz; - if (use_inches) - distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID("ClipboardDistanceXYZ"); - add_measure_row_to_table(_u8L("Distance XYZ"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), - ImGui::GetStyleColorVec4(ImGuiCol_Text)); - ImGui::PopID(); - } - ImGui::EndTable(); + const unsigned int max_measure_row_count = 2; + unsigned int measure_row_count = 0; + if (ImGui::BeginTable("Measure", 4)) { + if (m_selected_features.second.feature.has_value()) { + const Measure::MeasurementResult& measure = m_measurement_result; + if (measure.angle.has_value()) { + ImGui::PushID("ClipboardAngle"); + add_measure_row_to_table(_u8L("Angle"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)) + "°", + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++measure_row_count; + ImGui::PopID(); + } + if (measure.distance_infinite.has_value()) { + double distance = measure.distance_infinite->dist; + if (use_inches) + distance = ObjectManipulation::mm_to_in * distance; + ImGui::PushID("ClipboardDistanceInfinite"); + add_measure_row_to_table(_u8L("Distance Infinite"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++measure_row_count; + ImGui::PopID(); + } + if (measure.distance_strict.has_value() && + (!measure.distance_infinite.has_value() || std::abs(measure.distance_strict->dist - measure.distance_infinite->dist) > EPSILON)) { + double distance = measure.distance_strict->dist; + if (use_inches) + distance = ObjectManipulation::mm_to_in * distance; + ImGui::PushID("ClipboardDistanceStrict"); + add_measure_row_to_table(_u8L("Distance Strict"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++measure_row_count; + ImGui::PopID(); + } + if (measure.distance_xyz.has_value() && measure.distance_xyz->norm() > EPSILON) { + Vec3d distance = *measure.distance_xyz; + if (use_inches) + distance = ObjectManipulation::mm_to_in * distance; + ImGui::PushID("ClipboardDistanceXYZ"); + add_measure_row_to_table(_u8L("Distance XYZ"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++measure_row_count; + ImGui::PopID(); } } - else - m_imgui->text(_u8L("No measure available")); + + // add dummy rows to keep dialog size fixed + for (unsigned int i = measure_row_count; i < max_measure_row_count; ++i) { + add_strings_row_to_table(*m_imgui, " ", ImGuiWrapper::COL_ORANGE_LIGHT, " ", ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + ImGui::EndTable(); } if (last_feature != m_curr_feature || last_mode != m_mode || last_selected_features != m_selected_features) { From f0925e74f720a5f8da61f63c08190294b2cb2c97 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 10 Oct 2022 08:29:51 +0200 Subject: [PATCH 098/103] Follow-up of 2fb59e66c2b6f07a25cfa873fdeb1d820b2d78c4 - Removed obsolete assert --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 15d6ea761f..79a800bf2e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -1356,7 +1356,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (m_mode == EMode::BasicSelection) { switch (feature_type) { - default: { assert(false); break; } + default: { break; } case Measure::SurfaceFeatureType::Point: { Vec3d position = m_volume_matrix * m_curr_feature->get_point(); From 614b1581a662952d5d282427e6fd353b007d610f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 10 Oct 2022 09:46:12 +0200 Subject: [PATCH 099/103] Measuring - Gizmo measure - Auto-select text when opening imgui modal dialog to edit distance --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 10 ++++++++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + 2 files changed, 11 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 79a800bf2e..b4c35b1563 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -221,6 +221,7 @@ void GLGizmoMeasure::data_changed() m_selected_features.reset(); m_selection_raycasters.clear(); m_editing_distance = false; + m_is_editing_distance_first_frame = true; } bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) @@ -258,6 +259,7 @@ void GLGizmoMeasure::on_set_state() m_curr_point_on_feature_position.reset(); restore_scene_raycasters_state(); m_editing_distance = false; + m_is_editing_distance_first_frame = true; } else { m_mode = EMode::BasicSelection; @@ -904,6 +906,7 @@ void GLGizmoMeasure::render_dimensioning() }; auto action_exit = [this]() { m_editing_distance = false; + m_is_editing_distance_first_frame = true; ImGui::CloseCurrentPopup(); }; auto action_scale = [this, perform_scale, action_exit](double new_value, double old_value) { @@ -916,6 +919,13 @@ void GLGizmoMeasure::render_dimensioning() if (ImGui::InputDouble("##distance", &edit_value, 0.0f, 0.0f, "%.3f")) { } + // trick to auto-select text in the input widgets on 1st frame + if (m_is_editing_distance_first_frame) { + ImGui::SetKeyboardFocusHere(0); + m_is_editing_distance_first_frame = false; + m_imgui->set_requires_extra_frame(); + } + // handle keys input if (ImGui::IsKeyPressedMap(ImGuiKey_Enter) || ImGui::IsKeyPressedMap(ImGuiKey_KeyPadEnter)) action_scale(edit_value, curr_value); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index bba23fb6b8..9db2a73aa3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -119,6 +119,7 @@ class GLGizmoMeasure : public GLGizmoBase SelectedFeatures m_selected_features; bool m_editing_distance{ false }; + bool m_is_editing_distance_first_frame{ true }; void update_if_needed(); From 6cee261f3969f0070ade5985940d92fad44cdd89 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 10 Oct 2022 10:12:14 +0200 Subject: [PATCH 100/103] Follow-up of 8312dc24547df8e622bde814efb1b3e0b15bab40 - Fixed rendering of point on locked features when the object is scaled --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 34 ++++++++++++------------ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index b4c35b1563..1976cbcdab 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -317,7 +317,6 @@ void GLGizmoMeasure::on_render() (selection.is_single_volume() || selection.is_single_volume_instance())) { update_if_needed(); - m_volume_matrix = selection.get_first_volume()->world_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); const float inv_zoom = (float)camera.get_inv_zoom(); @@ -397,7 +396,6 @@ void GLGizmoMeasure::on_render() Vec3f n; const Transform3d& trafo = it->second->get_transform(); bool res = it->second->get_raycaster()->closest_hit(m_mouse_pos, trafo, camera, p, n); - assert(res); if (res) { if (callback) p = callback(p); @@ -472,15 +470,14 @@ void GLGizmoMeasure::on_render() }; auto render_feature = [this, set_matrix_uniforms](const Measure::SurfaceFeature& feature, const std::vector& colors, - const Transform3d& model_matrix, float inv_zoom, bool update_raycasters) { - const Transform3d model_matrix_scale_inverse = Geometry::Transformation(model_matrix).get_scaling_factor_matrix().inverse(); + float inv_zoom, bool update_raycasters) { switch (feature.get_type()) { default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { const Vec3d& position = feature.get_point(); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * model_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + const Transform3d feature_matrix = m_volume_matrix * Geometry::translation_transform(position) * m_volume_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(feature_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); @@ -495,7 +492,7 @@ void GLGizmoMeasure::on_render() { const auto& [center, radius, normal] = feature.get_circle(); // render center - const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * model_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + const Transform3d center_matrix = m_volume_matrix * Geometry::translation_transform(center) * m_volume_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(center_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); @@ -505,7 +502,7 @@ void GLGizmoMeasure::on_render() it->second->set_transform(center_matrix); } // render circle - const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); + const Transform3d circle_matrix = m_volume_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); set_matrix_uniforms(circle_matrix); m_circle.model.set_color(colors.back()); m_circle.model.render(); @@ -522,7 +519,7 @@ void GLGizmoMeasure::on_render() // render extra point const std::optional extra = feature.get_extra_point(); if (extra.has_value()) { - const Transform3d point_matrix = model_matrix * Geometry::translation_transform(*extra) * model_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + const Transform3d point_matrix = m_volume_matrix * Geometry::translation_transform(*extra) * m_volume_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(point_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); @@ -533,16 +530,16 @@ void GLGizmoMeasure::on_render() } } // render edge - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * + const Transform3d edge_matrix = m_volume_matrix * Geometry::translation_transform(start) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start) * Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); - set_matrix_uniforms(feature_matrix); + set_matrix_uniforms(edge_matrix); m_cylinder.model.set_color(colors.back()); m_cylinder.model.render(); if (update_raycasters) { auto it = m_raycasters.find(EDGE_ID); if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); + it->second->set_transform(edge_matrix); } break; } @@ -550,13 +547,13 @@ void GLGizmoMeasure::on_render() { const auto& [idx, normal, pt] = feature.get_plane(); assert(idx < m_plane_models_cache.size()); - set_matrix_uniforms(model_matrix); + set_matrix_uniforms(m_volume_matrix); m_plane_models_cache[idx].set_color(colors.front()); m_plane_models_cache[idx].render(); if (update_raycasters) { auto it = m_raycasters.find(PLANE_ID); if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(model_matrix); + it->second->set_transform(m_volume_matrix); } break; } @@ -601,13 +598,13 @@ void GLGizmoMeasure::on_render() } } - render_feature(*m_curr_feature, colors, m_volume_matrix, inv_zoom, true); + render_feature(*m_curr_feature, colors, inv_zoom, true); } if (m_selected_features.first.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.first.feature)) { std::vector colors; colors.emplace_back(SELECTED_1ST_COLOR); - render_feature(*m_selected_features.first.feature, colors, m_volume_matrix, inv_zoom, false); + render_feature(*m_selected_features.first.feature, colors, inv_zoom, false); if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point) { auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_1_ID; }); @@ -618,7 +615,7 @@ void GLGizmoMeasure::on_render() if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) { std::vector colors; colors.emplace_back(SELECTED_2ND_COLOR); - render_feature(*m_selected_features.second.feature, colors, m_volume_matrix, inv_zoom, false); + render_feature(*m_selected_features.second.feature, colors, inv_zoom, false); if (m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_2_ID; }); @@ -629,7 +626,7 @@ void GLGizmoMeasure::on_render() if (is_hovering_on_locked_feature && m_curr_point_on_feature_position.has_value()) { if (m_hover_id != POINT_ID) { - const Transform3d matrix = m_volume_matrix * Geometry::translation_transform(*m_curr_point_on_feature_position) * Geometry::scale_transform(inv_zoom); + const Transform3d matrix = m_volume_matrix * Geometry::translation_transform(*m_curr_point_on_feature_position) * m_volume_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); m_sphere.model.set_color(hover_selection_color()); m_sphere.model.render(); @@ -675,6 +672,9 @@ void GLGizmoMeasure::update_if_needed() } m_old_model_object = object; m_old_model_volume = volume; + + m_volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); + m_volume_matrix_scale_inverse = Geometry::Transformation(m_volume_matrix).get_scaling_factor_matrix().inverse(); }; const ModelObject* mo = m_c->selection_info()->model_object(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 9db2a73aa3..fd93ba4aac 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -87,6 +87,7 @@ class GLGizmoMeasure : public GLGizmoBase Dimensioning m_dimensioning; Transform3d m_volume_matrix{ Transform3d::Identity() }; + Transform3d m_volume_matrix_scale_inverse{ Transform3d::Identity() }; std::vector m_plane_models_cache; std::map> m_raycasters; std::vector> m_selection_raycasters; From 2972493f5fb66a418c61ca5c3d9f16901187a0b1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 11 Oct 2022 10:47:20 +0200 Subject: [PATCH 101/103] Measuring: Gizmo measure shows dimensioning for distance circle-circle --- src/libslic3r/CMakeLists.txt | 1 + src/libslic3r/Measure.cpp | 241 +++++++++++++++++++- src/libslic3r/Measure.hpp | 4 + src/libslic3r/MeasureUtils.hpp | 390 +++++++++++++++++++++++++++++++++ 4 files changed, 632 insertions(+), 4 deletions(-) create mode 100644 src/libslic3r/MeasureUtils.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index cdf61407f2..e91a2186c0 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -182,6 +182,7 @@ set(SLIC3R_SOURCES MeshNormals.cpp Measure.hpp Measure.cpp + MeasureUtils.hpp CustomGCode.cpp CustomGCode.hpp Arrange.hpp diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index ea68503254..27a4f8a90d 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -1,10 +1,11 @@ #include "libslic3r/libslic3r.h" #include "Measure.hpp" +#include "MeasureUtils.hpp" #include "libslic3r/Geometry/Circle.hpp" #include "libslic3r/SurfaceMesh.hpp" - +#if ENABLE_MEASURE_GIZMO namespace Slic3r { namespace Measure { @@ -13,8 +14,6 @@ namespace Measure { constexpr double feature_hover_limit = 0.5; // how close to a feature the mouse must be to highlight it constexpr double edge_endpoint_limit = 0.5; // how close to an edge endpoint the mouse ... - - static std::pair get_center_and_radius(const std::vector& border, int start_idx, int end_idx, const Transform3d& trafo) { Vec2ds pts; @@ -754,7 +753,238 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Circle) { if (f2.get_type() == SurfaceFeatureType::Circle) { - result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + const auto [c0, r0, n0] = f1.get_circle(); + const auto [c1, r1, n1] = f2.get_circle(); + + // The following code is an adaptation of the algorithm found in: + // https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/DistCircle3Circle3.h + // and described in: + // https://www.geometrictools.com/Documentation/DistanceToCircle3.pdf + + struct ClosestInfo + { + double sqrDistance{ 0.0 }; + Vec3d circle0Closest{ Vec3d::Zero() }; + Vec3d circle1Closest{ Vec3d::Zero() }; + + inline bool operator < (const ClosestInfo& other) const { return sqrDistance < other.sqrDistance; } + }; + std::array candidates{}; + + const double zero = 0.0; + + const Vec3d D = c1 - c0; + + if (!are_parallel(n0, n1)) { + auto orthonormal_basis = [](const Vec3d& v) { + std::array ret; + ret[2] = v.normalized(); + int index; + ret[2].maxCoeff(&index); + switch (index) + { + case 0: { ret[0] = Vec3d(ret[2].y(), -ret[2].x(), 0.0).normalized(); break; } + case 1: { ret[0] = Vec3d(0.0, ret[2].z(), -ret[2].y()).normalized(); break; } + case 2: { ret[0] = Vec3d(-ret[2].z(), 0.0, ret[2].x()).normalized(); break; } + } + ret[1] = ret[2].cross(ret[0]).normalized(); + return ret; + }; + + // Get parameters for constructing the degree-8 polynomial phi. + const double one = 1.0; + const double two = 2.0; + const double r0sqr = sqr(r0); + const double r1sqr = sqr(r1); + + // Compute U1 and V1 for the plane of circle1. + const std::array basis = orthonormal_basis(n1); + const Vec3d U1 = basis[0]; + const Vec3d V1 = basis[1]; + + // Construct the polynomial phi(cos(theta)). + const Vec3d N0xD = n0.cross(D); + const Vec3d N0xU1 = n0.cross(U1); + const Vec3d N0xV1 = n0.cross(V1); + const double a0 = r1 * D.dot(U1); + const double a1 = r1 * D.dot(V1); + const double a2 = N0xD.dot(N0xD); + const double a3 = r1 * N0xD.dot(N0xU1); + const double a4 = r1 * N0xD.dot(N0xV1); + const double a5 = r1sqr * N0xU1.dot(N0xU1); + const double a6 = r1sqr * N0xU1.dot(N0xV1); + const double a7 = r1sqr * N0xV1.dot(N0xV1); + Polynomial1 p0{ a2 + a7, two * a3, a5 - a7 }; + Polynomial1 p1{ two * a4, two * a6 }; + Polynomial1 p2{ zero, a1 }; + Polynomial1 p3{ -a0 }; + Polynomial1 p4{ -a6, a4, two * a6 }; + Polynomial1 p5{ -a3, a7 - a5 }; + Polynomial1 tmp0{ one, zero, -one }; + Polynomial1 tmp1 = p2 * p2 + tmp0 * p3 * p3; + Polynomial1 tmp2 = two * p2 * p3; + Polynomial1 tmp3 = p4 * p4 + tmp0 * p5 * p5; + Polynomial1 tmp4 = two * p4 * p5; + Polynomial1 p6 = p0 * tmp1 + tmp0 * p1 * tmp2 - r0sqr * tmp3; + Polynomial1 p7 = p0 * tmp2 + p1 * tmp1 - r0sqr * tmp4; + + // Parameters for polynomial root finding. The roots[] array + // stores the roots. We need only the unique ones, which is + // the responsibility of the set uniqueRoots. The pairs[] + // array stores the (cosine,sine) information mentioned in the + // PDF. TODO: Choose the maximum number of iterations for root + // finding based on specific polynomial data? + const uint32_t maxIterations = 128; + int32_t degree = 0; + size_t numRoots = 0; + std::array roots{}; + std::set uniqueRoots{}; + size_t numPairs = 0; + std::array, 16> pairs{}; + double temp = zero; + double sn = zero; + + if (p7.GetDegree() > 0 || p7[0] != zero) { + // H(cs,sn) = p6(cs) + sn * p7(cs) + Polynomial1 phi = p6 * p6 - tmp0 * p7 * p7; + degree = static_cast(phi.GetDegree()); + assert(degree > 0); + numRoots = RootsPolynomial::Find(degree, &phi[0], maxIterations, roots.data()); + for (size_t i = 0; i < numRoots; ++i) { + uniqueRoots.insert(roots[i]); + } + + for (auto const& cs : uniqueRoots) { + if (std::fabs(cs) <= one) { + temp = p7(cs); + if (temp != zero) { + sn = -p6(cs) / temp; + pairs[numPairs++] = std::make_pair(cs, sn); + } + else { + temp = std::max(one - sqr(cs), zero); + sn = std::sqrt(temp); + pairs[numPairs++] = std::make_pair(cs, sn); + if (sn != zero) + pairs[numPairs++] = std::make_pair(cs, -sn); + } + } + } + } + else { + // H(cs,sn) = p6(cs) + degree = static_cast(p6.GetDegree()); + assert(degree > 0); + numRoots = RootsPolynomial::Find(degree, &p6[0], maxIterations, roots.data()); + for (size_t i = 0; i < numRoots; ++i) { + uniqueRoots.insert(roots[i]); + } + + for (auto const& cs : uniqueRoots) { + if (std::fabs(cs) <= one) { + temp = std::max(one - sqr(cs), zero); + sn = std::sqrt(temp); + pairs[numPairs++] = std::make_pair(cs, sn); + if (sn != zero) + pairs[numPairs++] = std::make_pair(cs, -sn); + } + } + } + + for (size_t i = 0; i < numPairs; ++i) { + ClosestInfo& info = candidates[i]; + Vec3d delta = D + r1 * (pairs[i].first * U1 + pairs[i].second * V1); + info.circle1Closest = c0 + delta; + const double N0dDelta = n0.dot(delta); + const double lenN0xDelta = n0.cross(delta).norm(); + if (lenN0xDelta > 0.0) { + const double diff = lenN0xDelta - r0; + info.sqrDistance = sqr(N0dDelta) + sqr(diff); + delta -= N0dDelta * n0; + delta.normalize(); + info.circle0Closest = c0 + r0 * delta; + } + else { + const Vec3d r0U0 = r0 * get_orthogonal(n0, true); + const Vec3d diff = delta - r0U0; + info.sqrDistance = diff.dot(diff); + info.circle0Closest = c0 + r0U0; + } + } + + std::sort(candidates.begin(), candidates.begin() + numPairs); + } + else { + ClosestInfo& info = candidates[0]; + + const double N0dD = n0.dot(D); + const Vec3d normProj = N0dD * n0; + const Vec3d compProj = D - normProj; + Vec3d U = compProj; + const double d = U.norm(); + U.normalize(); + + // The configuration is determined by the relative location of the + // intervals of projection of the circles on to the D-line. + // Circle0 projects to [-r0,r0] and circle1 projects to + // [d-r1,d+r1]. + const double dmr1 = d - r1; + double distance; + if (dmr1 >= r0) { + // d >= r0 + r1 + // The circles are separated (d > r0 + r1) or tangent with one + // outside the other (d = r0 + r1). + distance = dmr1 - r0; + info.circle0Closest = c0 + r0 * U; + info.circle1Closest = c1 - r1 * U; + } + else { + // d < r0 + r1 + // The cases implicitly use the knowledge that d >= 0. + const double dpr1 = d + r1; + if (dpr1 <= r0) { + // Circle1 is inside circle0. + distance = r0 - dpr1; + if (d > 0.0) { + info.circle0Closest = c0 + r0 * U; + info.circle1Closest = c1 + r1 * U; + } + else { + // The circles are concentric, so U = (0,0,0). + // Construct a vector perpendicular to N0 to use for + // closest points. + U = get_orthogonal(n0, true); + info.circle0Closest = c0 + r0 * U; + info.circle1Closest = c1 + r1 * U; + } + } + else if (dmr1 <= -r0) { + // Circle0 is inside circle1. + distance = -r0 - dmr1; + if (d > 0.0) { + info.circle0Closest = c0 - r0 * U; + info.circle1Closest = c1 - r1 * U; + } + else { + // The circles are concentric, so U = (0,0,0). + // Construct a vector perpendicular to N0 to use for + // closest points. + U = get_orthogonal(n0, true); + info.circle0Closest = c0 + r0 * U; + info.circle1Closest = c1 + r1 * U; + } + } + else { + distance = (c1 - c0).norm(); + info.circle0Closest = c0; + info.circle1Closest = c1; + } + } + + info.sqrDistance = distance * distance + N0dD * N0dD; + } + + result.distance_infinite = std::make_optional(DistAndPoints{ std::sqrt(candidates[0].sqrDistance), candidates[0].circle0Closest, candidates[0].circle1Closest }); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { assert(measuring != nullptr); @@ -823,3 +1053,6 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& } // namespace Measure } // namespace Slic3r + + +#endif // ENABLE_MEASURE_GIZMO diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 2e17eea5c5..f310d8f9fa 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -1,6 +1,8 @@ #ifndef Slic3r_Measure_hpp_ #define Slic3r_Measure_hpp_ +#if ENABLE_MEASURE_GIZMO + #include #include @@ -191,3 +193,5 @@ inline bool are_perpendicular(const SurfaceFeature& f1, const SurfaceFeature& f2 } // namespace Slic3r #endif // Slic3r_Measure_hpp_ + +#endif // ENABLE_MEASURE_GIZMO diff --git a/src/libslic3r/MeasureUtils.hpp b/src/libslic3r/MeasureUtils.hpp new file mode 100644 index 0000000000..4f84624eac --- /dev/null +++ b/src/libslic3r/MeasureUtils.hpp @@ -0,0 +1,390 @@ +#ifndef Slic3r_MeasureUtils_hpp_ +#define Slic3r_MeasureUtils_hpp_ + +#if ENABLE_MEASURE_GIZMO + +#include + +namespace Slic3r { +namespace Measure { + +// Utility class used to calculate distance circle-circle +// Adaptation of code found in: +// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Polynomial1.h + +class Polynomial1 +{ +public: + Polynomial1(std::initializer_list values) + { + // C++ 11 will call the default constructor for + // Polynomial1 p{}, so it is guaranteed that + // values.size() > 0. + m_coefficient.resize(values.size()); + std::copy(values.begin(), values.end(), m_coefficient.begin()); + EliminateLeadingZeros(); + } + + // Construction and destruction. The first constructor creates a + // polynomial of the specified degree but sets all coefficients to + // zero (to ensure initialization). You are responsible for setting + // the coefficients, presumably with the degree-term set to a nonzero + // number. In the second constructor, the degree is the number of + // initializers plus 1, but then adjusted so that coefficient[degree] + // is not zero (unless all initializer values are zero). + explicit Polynomial1(uint32_t degree) + : m_coefficient(static_cast(degree) + 1, 0.0) + {} + + // Eliminate any leading zeros in the polynomial, except in the case + // the degree is 0 and the coefficient is 0. The elimination is + // necessary when arithmetic operations cause a decrease in the degree + // of the result. For example, (1 + x + x^2) + (1 + 2*x - x^2) = + // (2 + 3*x). The inputs both have degree 2, so the result is created + // with degree 2. After the addition we find that the degree is in + // fact 1 and resize the array of coefficients. This function is + // called internally by the arithmetic operators, but it is exposed in + // the public interface in case you need it for your own purposes. + void EliminateLeadingZeros() + { + const size_t size = m_coefficient.size(); + if (size > 1) { + const double zero = 0.0; + int32_t leading; + for (leading = static_cast(size) - 1; leading > 0; --leading) { + if (m_coefficient[leading] != zero) + break; + } + + m_coefficient.resize(++leading); + } + } + + // Set all coefficients to the specified value. + void SetCoefficients(double value) + { + std::fill(m_coefficient.begin(), m_coefficient.end(), value); + } + + inline uint32_t GetDegree() const + { + // By design, m_coefficient.size() > 0. + return static_cast(m_coefficient.size() - 1); + } + + inline const double& operator[](uint32_t i) const { return m_coefficient[i]; } + inline double& operator[](uint32_t i) { return m_coefficient[i]; } + + // Evaluate the polynomial. If the polynomial is invalid, the + // function returns zero. + double operator()(double t) const + { + int32_t i = static_cast(m_coefficient.size()); + double result = m_coefficient[--i]; + for (--i; i >= 0; --i) { + result *= t; + result += m_coefficient[i]; + } + return result; + } + +protected: + // The class is designed so that m_coefficient.size() >= 1. + std::vector m_coefficient; +}; + +Polynomial1 operator * (const Polynomial1& p0, const Polynomial1& p1) +{ + const uint32_t p0Degree = p0.GetDegree(); + const uint32_t p1Degree = p1.GetDegree(); + Polynomial1 result(p0Degree + p1Degree); + result.SetCoefficients(0.0); + for (uint32_t i0 = 0; i0 <= p0Degree; ++i0) { + for (uint32_t i1 = 0; i1 <= p1Degree; ++i1) { + result[i0 + i1] += p0[i0] * p1[i1]; + } + } + return result; +} + +Polynomial1 operator + (const Polynomial1& p0, const Polynomial1& p1) +{ + const uint32_t p0Degree = p0.GetDegree(); + const uint32_t p1Degree = p1.GetDegree(); + uint32_t i; + if (p0Degree >= p1Degree) { + Polynomial1 result(p0Degree); + for (i = 0; i <= p1Degree; ++i) { + result[i] = p0[i] + p1[i]; + } + for (/**/; i <= p0Degree; ++i) { + result[i] = p0[i]; + } + result.EliminateLeadingZeros(); + return result; + } + else { + Polynomial1 result(p1Degree); + for (i = 0; i <= p0Degree; ++i) { + result[i] = p0[i] + p1[i]; + } + for (/**/; i <= p1Degree; ++i) { + result[i] = p1[i]; + } + result.EliminateLeadingZeros(); + return result; + } +} + +Polynomial1 operator - (const Polynomial1& p0, const Polynomial1& p1) +{ + const uint32_t p0Degree = p0.GetDegree(); + const uint32_t p1Degree = p1.GetDegree(); + uint32_t i; + if (p0Degree >= p1Degree) { + Polynomial1 result(p0Degree); + for (i = 0; i <= p1Degree; ++i) { + result[i] = p0[i] - p1[i]; + } + for (/**/; i <= p0Degree; ++i) { + result[i] = p0[i]; + } + result.EliminateLeadingZeros(); + return result; + } + else { + Polynomial1 result(p1Degree); + for (i = 0; i <= p0Degree; ++i) { + result[i] = p0[i] - p1[i]; + } + for (/**/; i <= p1Degree; ++i) { + result[i] = -p1[i]; + } + result.EliminateLeadingZeros(); + return result; + } +} + +Polynomial1 operator * (double scalar, const Polynomial1& p) +{ + const uint32_t degree = p.GetDegree(); + Polynomial1 result(degree); + for (uint32_t i = 0; i <= degree; ++i) { + result[i] = scalar * p[i]; + } + return result; +} + +// Utility class used to calculate distance circle-circle +// Adaptation of code found in: +// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/RootsPolynomial.h + +class RootsPolynomial +{ +public: + // General equations: sum_{i=0}^{d} c(i)*t^i = 0. The input array 'c' + // must have at least d+1 elements and the output array 'root' must + // have at least d elements. + + // Find the roots on (-infinity,+infinity). + static int32_t Find(int32_t degree, const double* c, uint32_t maxIterations, double* roots) + { + if (degree >= 0 && c != nullptr) { + const double zero = 0.0; + while (degree >= 0 && c[degree] == zero) { + --degree; + } + + if (degree > 0) { + // Compute the Cauchy bound. + const double one = 1.0; + const double invLeading = one / c[degree]; + double maxValue = zero; + for (int32_t i = 0; i < degree; ++i) { + const double value = std::fabs(c[i] * invLeading); + if (value > maxValue) + maxValue = value; + } + const double bound = one + maxValue; + + return FindRecursive(degree, c, -bound, bound, maxIterations, roots); + } + else if (degree == 0) + // The polynomial is a nonzero constant. + return 0; + else { + // The polynomial is identically zero. + roots[0] = zero; + return 1; + } + } + else + // Invalid degree or c. + return 0; + } + + // If you know that p(tmin) * p(tmax) <= 0, then there must be at + // least one root in [tmin, tmax]. Compute it using bisection. + static bool Find(int32_t degree, const double* c, double tmin, double tmax, uint32_t maxIterations, double& root) + { + const double zero = 0.0; + double pmin = Evaluate(degree, c, tmin); + if (pmin == zero) { + root = tmin; + return true; + } + double pmax = Evaluate(degree, c, tmax); + if (pmax == zero) { + root = tmax; + return true; + } + + if (pmin * pmax > zero) + // It is not known whether the interval bounds a root. + return false; + + if (tmin >= tmax) + // Invalid ordering of interval endpoitns. + return false; + + for (uint32_t i = 1; i <= maxIterations; ++i) { + root = 0.5 * (tmin + tmax); + + // This test is designed for 'float' or 'double' when tmin + // and tmax are consecutive floating-point numbers. + if (root == tmin || root == tmax) + break; + + const double p = Evaluate(degree, c, root); + const double product = p * pmin; + if (product < zero) { + tmax = root; + pmax = p; + } + else if (product > zero) { + tmin = root; + pmin = p; + } + else + break; + } + + return true; + } + + // Support for the Find functions. + static int32_t FindRecursive(int32_t degree, double const* c, double tmin, double tmax, uint32_t maxIterations, double* roots) + { + // The base of the recursion. + const double zero = 0.0; + double root = zero; + if (degree == 1) { + int32_t numRoots; + if (c[1] != zero) { + root = -c[0] / c[1]; + numRoots = 1; + } + else if (c[0] == zero) { + root = zero; + numRoots = 1; + } + else + numRoots = 0; + + if (numRoots > 0 && tmin <= root && root <= tmax) { + roots[0] = root; + return 1; + } + return 0; + } + + // Find the roots of the derivative polynomial scaled by 1/degree. + // The scaling avoids the factorial growth in the coefficients; + // for example, without the scaling, the high-order term x^d + // becomes (d!)*x through multiple differentiations. With the + // scaling we instead get x. This leads to better numerical + // behavior of the root finder. + const int32_t derivDegree = degree - 1; + std::vector derivCoeff(static_cast(derivDegree) + 1); + std::vector derivRoots(derivDegree); + for (int32_t i = 0, ip1 = 1; i <= derivDegree; ++i, ++ip1) { + derivCoeff[i] = c[ip1] * (double)(ip1) / (double)degree; + } + const int32_t numDerivRoots = FindRecursive(degree - 1, &derivCoeff[0], tmin, tmax, maxIterations, &derivRoots[0]); + + int32_t numRoots = 0; + if (numDerivRoots > 0) { + // Find root on [tmin,derivRoots[0]]. + if (Find(degree, c, tmin, derivRoots[0], maxIterations, root)) + roots[numRoots++] = root; + + // Find root on [derivRoots[i],derivRoots[i+1]]. + for (int32_t i = 0, ip1 = 1; i <= numDerivRoots - 2; ++i, ++ip1) { + if (Find(degree, c, derivRoots[i], derivRoots[ip1], maxIterations, root)) + roots[numRoots++] = root; + } + + // Find root on [derivRoots[numDerivRoots-1],tmax]. + if (Find(degree, c, derivRoots[static_cast(numDerivRoots) - 1], tmax, maxIterations, root)) + roots[numRoots++] = root; + } + else { + // The polynomial is monotone on [tmin,tmax], so has at most one root. + if (Find(degree, c, tmin, tmax, maxIterations, root)) + roots[numRoots++] = root; + } + return numRoots; + } + + static double Evaluate(int32_t degree, const double* c, double t) + { + int32_t i = degree; + double result = c[i]; + while (--i >= 0) { + result = t * result + c[i]; + } + return result; + } +}; + +// Adaptation of code found in: +// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Vector.h + +// Construct a single vector orthogonal to the nonzero input vector. If +// the maximum absolute component occurs at index i, then the orthogonal +// vector U has u[i] = v[i+1], u[i+1] = -v[i], and all other components +// zero. The index addition i+1 is computed modulo N. +Vec3d get_orthogonal(const Vec3d& v, bool unitLength) +{ + double cmax = std::fabs(v[0]); + int32_t imax = 0; + for (int32_t i = 1; i < 3; ++i) { + double c = std::fabs(v[i]); + if (c > cmax) { + cmax = c; + imax = i; + } + } + + Vec3d result = Vec3d::Zero(); + int32_t inext = imax + 1; + if (inext == 3) + inext = 0; + + result[imax] = v[inext]; + result[inext] = -v[imax]; + if (unitLength) { + const double sqrDistance = result[imax] * result[imax] + result[inext] * result[inext]; + const double invLength = 1.0 / std::sqrt(sqrDistance); + result[imax] *= invLength; + result[inext] *= invLength; + } + return result; +} + +} // namespace Slic3r +} // namespace Measure + +#endif // Slic3r_MeasureUtils_hpp_ + +#endif // ENABLE_MEASURE_GIZMO From e3fd2ca48413dbcf746de4294315e955c320d944 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 11 Oct 2022 11:41:15 +0200 Subject: [PATCH 102/103] Fixed warning --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 1976cbcdab..0d53c69385 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -909,7 +909,7 @@ void GLGizmoMeasure::render_dimensioning() m_is_editing_distance_first_frame = true; ImGui::CloseCurrentPopup(); }; - auto action_scale = [this, perform_scale, action_exit](double new_value, double old_value) { + auto action_scale = [perform_scale, action_exit](double new_value, double old_value) { perform_scale(new_value, old_value); action_exit(); }; From 2a7611ebafadde4d8366ef86f77465fad14891d1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 11 Oct 2022 13:38:07 +0200 Subject: [PATCH 103/103] Measuring: Gizmo measure - Fixed rendering of selected circle features --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 34 ++++++++++++++---------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 0d53c69385..db33135dda 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -329,7 +329,8 @@ void GLGizmoMeasure::on_render() if (m_mode == EMode::BasicSelection) { std::optional curr_feature = mouse_on_object ? m_measuring->get_feature(model_facet_idx, position_on_model.cast()) : std::nullopt; m_curr_point_on_feature_position.reset(); - if (m_curr_feature != curr_feature) { + if (m_curr_feature != curr_feature || + (curr_feature.has_value() && curr_feature->get_type() == Measure::SurfaceFeatureType::Circle && (m_curr_feature != curr_feature || m_last_inv_zoom != inv_zoom))) { m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID); m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID); m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID); @@ -470,7 +471,7 @@ void GLGizmoMeasure::on_render() }; auto render_feature = [this, set_matrix_uniforms](const Measure::SurfaceFeature& feature, const std::vector& colors, - float inv_zoom, bool update_raycasters) { + float inv_zoom, bool update_raycasters_transform) { switch (feature.get_type()) { default: { assert(false); break; } @@ -481,7 +482,7 @@ void GLGizmoMeasure::on_render() set_matrix_uniforms(feature_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); - if (update_raycasters) { + if (update_raycasters_transform) { auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(feature_matrix); @@ -496,7 +497,7 @@ void GLGizmoMeasure::on_render() set_matrix_uniforms(center_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); - if (update_raycasters) { + if (update_raycasters_transform) { auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(center_matrix); @@ -504,13 +505,20 @@ void GLGizmoMeasure::on_render() // render circle const Transform3d circle_matrix = m_volume_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); set_matrix_uniforms(circle_matrix); - m_circle.model.set_color(colors.back()); - m_circle.model.render(); - if (update_raycasters) { + if (update_raycasters_transform) { + m_circle.model.set_color(colors.back()); + m_circle.model.render(); auto it = m_raycasters.find(CIRCLE_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(circle_matrix); } + else { + GLModel circle; + GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); + circle.init_from(std::move(circle_geometry)); + circle.set_color(colors.back()); + circle.render(); + } break; } case Measure::SurfaceFeatureType::Edge: @@ -523,7 +531,7 @@ void GLGizmoMeasure::on_render() set_matrix_uniforms(point_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); - if (update_raycasters) { + if (update_raycasters_transform) { auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(point_matrix); @@ -536,7 +544,7 @@ void GLGizmoMeasure::on_render() set_matrix_uniforms(edge_matrix); m_cylinder.model.set_color(colors.back()); m_cylinder.model.render(); - if (update_raycasters) { + if (update_raycasters_transform) { auto it = m_raycasters.find(EDGE_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(edge_matrix); @@ -550,7 +558,7 @@ void GLGizmoMeasure::on_render() set_matrix_uniforms(m_volume_matrix); m_plane_models_cache[idx].set_color(colors.front()); m_plane_models_cache[idx].render(); - if (update_raycasters) { + if (update_raycasters_transform) { auto it = m_raycasters.find(PLANE_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(m_volume_matrix); @@ -602,8 +610,7 @@ void GLGizmoMeasure::on_render() } if (m_selected_features.first.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.first.feature)) { - std::vector colors; - colors.emplace_back(SELECTED_1ST_COLOR); + const std::vector colors = { SELECTED_1ST_COLOR }; render_feature(*m_selected_features.first.feature, colors, inv_zoom, false); if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point) { auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), @@ -613,8 +620,7 @@ void GLGizmoMeasure::on_render() } } if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) { - std::vector colors; - colors.emplace_back(SELECTED_2ND_COLOR); + const std::vector colors = { SELECTED_2ND_COLOR }; render_feature(*m_selected_features.second.feature, colors, inv_zoom, false); if (m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(),