diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index ac1abe537..a52848b22 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -180,6 +180,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 000000000..d5cb9c24b --- /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 000000000..bbc7d9e1e --- /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 e00d58781..043adba97 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 9534796a7..2781d2b35 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: