From 4349e82d27b124392b5ed8c1672a54d7c4b02422 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Wed, 13 Jul 2022 13:15:07 +0200 Subject: [PATCH] Clean up cut surface --- src/libslic3r/CutSurface.cpp | 962 +++++++++------------------ src/libslic3r/CutSurface.hpp | 32 +- tests/libslic3r/test_cut_surface.cpp | 16 - 3 files changed, 335 insertions(+), 675 deletions(-) diff --git a/src/libslic3r/CutSurface.cpp b/src/libslic3r/CutSurface.cpp index 6dceb2dd8..ca482b4ac 100644 --- a/src/libslic3r/CutSurface.cpp +++ b/src/libslic3r/CutSurface.cpp @@ -24,33 +24,6 @@ using namespace Slic3r; -void Slic3r::append(SurfaceCut &sc, SurfaceCut &&sc_add) -{ - if (sc.empty()) { - sc = std::move(sc_add); - return; - } - - if (!sc_add.contours.empty()) { - SurfaceCut::Index offset = static_cast( - sc.vertices.size()); - size_t require = sc.contours.size() + sc_add.contours.size(); - if (sc.contours.capacity() < require) sc.contours.reserve(require); - for (std::vector &cut : sc_add.contours) - for (SurfaceCut::Index &i : cut) i += offset; - append(sc.contours, std::move(sc_add.contours)); - } - its_merge(sc, std::move(sc_add)); -} - -SurfaceCut Slic3r::merge(SurfaceCuts &&cuts) -{ - SurfaceCut result; - for (SurfaceCut &cut : cuts) - append(result, std::move(cut)); - return result; -} - #include #include #include @@ -242,29 +215,13 @@ enum class FaceType { using FaceTypeMap = CutMesh::Property_map; const std::string face_type_map_name = "f:side"; -/// -/// Keep conversion from ShapePointId to Index and vice versa -/// ShapePoint .. contour(or hole) poin from ExPolygons -/// Index .. continous number -/// -class ShapePoint2index -{ - std::vector> m_offsets; - // for check range of index - uint32_t m_count; - -public: - ShapePoint2index(const ExPolygons &shapes); - uint32_t calc_index(const ShapePointId &id) const; - ShapePointId calc_id(uint32_t index) const; - uint32_t get_count() const; -}; - // Conversion one vertex index to another using CvtVI2VI = CutMesh::Property_map; // Each Patch track outline vertex conversion to tource model const std::string patch_source_name = "v:patch_source"; +// For VI that should be reduced, contain VI to use instead of reduced +// Other VI are invalid using ReductionMap = CvtVI2VI; const std::string vertex_reduction_map_name = "v:reduction"; @@ -272,7 +229,6 @@ const std::string vertex_reduction_map_name = "v:reduction"; using EdgeBoolMap = CutMesh::Property_map; const std::string is_constrained_edge_name = "e:is_constrained"; - /// /// Create map to reduce unnecesary triangles, /// Triangles are made by divided quad to two triangles @@ -295,14 +251,23 @@ using CutAOIs = std::vector; // vector of CutAOIs for each model using VCutAOIs = std::vector; -struct ModelCutId +/// +/// Keep conversion from ShapePointId to Index and vice versa +/// ShapePoint .. contour(or hole) poin from ExPolygons +/// Index .. continous number +/// +class ShapePoint2index { - // index of model - uint32_t model_index; - // index of cut inside model - uint32_t cut_index; -}; + std::vector> m_offsets; + // for check range of index + uint32_t m_count; +public: + ShapePoint2index(const ExPolygons &shapes); + uint32_t calc_index(const ShapePointId &id) const; + ShapePointId calc_id(uint32_t index) const; + uint32_t get_count() const; +}; /// /// Create AOIs(area of interest) on model surface @@ -353,6 +318,14 @@ struct SurfacePatch }; using SurfacePatches = std::vector; +struct ModelCutId +{ + // index of model + uint32_t model_index; + // index of cut inside model + uint32_t cut_index; +}; + /// /// Keep conversion from VCutAOIs to Index and vice versa /// Model_index .. contour(or hole) poin from ExPolygons @@ -368,7 +341,8 @@ public: ModelCut2index(const VCutAOIs &cuts); uint32_t calc_index(const ModelCutId &id) const; ModelCutId calc_id(uint32_t index) const; - uint32_t get_count() const; + uint32_t get_count() const { return m_count; }; + const std::vector &get_offsets() const { return m_offsets; } }; /// @@ -383,14 +357,14 @@ public: /// NOTE: Clip function modify Mesh /// Define projection direction /// Cuts differenciate by models - Patch -SurfacePatches diff_models(VCutAOIs &cuts, - const ModelCut2index &m2i, - /*const*/ CutMeshes &cut_models, - /*const*/ CutMeshes &models, - const Emboss::IProject3f &projection); +SurfacePatches diff_models(VCutAOIs &cuts, + const ModelCut2index &m2i, + /*const*/ CutMeshes &cut_models, + /*const*/ CutMeshes &models, + const Project3f &projection); /// -/// To select correct area +/// To select surface near projection distance /// struct ProjectionDistance { @@ -459,86 +433,11 @@ std::vector select_patches(const ProjectionDistances &best_distances, /// Merge masked patches to one surface cut /// /// All patches -/// NOTE: need add property for transform +/// NOTE: Not const because One needs to add property for Convert indices /// Mash for using patch /// Result surface cut SurfaceCut merge_patches(/*const*/ SurfacePatches &patches, - std::vector mask); - -using ConvertMap = CutMesh::Property_map; -/// -/// Create surface cuts from mesh model -/// -/// Areas of interests from model surface -/// Model - can't be const because of create temporary property map -/// Reduction of vertices -/// Created surface cuts -SurfaceCuts create_surface_cuts(const CutAOIs &cutAOIs, - CutMesh &mesh, - const ReductionMap &reduction_map); - -/// -/// Collect connected inside faces -/// Collect outline half edges -/// -/// Queue of face to process - find connected -/// [Output] collected Face indices from mesh -/// [Output] collected Halfedge indices from mesh -/// Use flag inside / outside -/// NOTE: Modify in function: inside -> inside_processed -/// mesh to process -void collect_surface_data(std::queue &process, - std::vector &faces, - std::vector &outlines, - FaceTypeMap &face_type_map, - const CutMesh &mesh); - -/// -/// Copy triangles from CGAL mesh into index triangle set -/// NOTE: Skip vertices created by edge in center of Quad. -/// -/// Faces to copy -/// Count of outlines -/// Source CGAL mesh -/// Reduction of vertices -/// [Output] map to convert CGAL vertex to its::vertex index -/// Surface cut (Partialy filled - only index triangle set) -SurfaceCut create_index_triangle_set(const std::vector &faces, - size_t count_outlines, - const CutMesh &mesh, - const ReductionMap &reduction_map, - ConvertMap &v2v); - -/// -/// Connect outlines into closed loops -/// -/// Half edges from border of cut - Oriented -/// Source CGAL mesh -/// Reduction of vertices -/// Map to convert CGAL vertex to its::vertex -/// Cuts - outlines of surface -SurfaceCut::CutContour create_cut(const std::vector &outlines, - const CutMesh &mesh, - const ReductionMap &reduction_map, - const ConvertMap &v2v); - -/// -/// Self Intersection of surface cuts are made by -/// damaged models OR multi volumes emboss -/// -/// Surface cuts to merge -/// NOTE: Merge process move data from cuts to result -/// Mask for wanted cuts [Same size as cuts] -/// Merged all surface cuts into one -SurfaceCut merge_intersections(SurfaceCuts &cuts, const CutAOIs& cutAOIs, const std::vector& use_cut); - -/// -/// Merge 2 Cuts when has intersection -/// -/// In/Out cut to merge into -/// Cut to merge from -/// Has intersection -bool merge_intersection(SurfaceCut &cut1, const SurfaceCut &cut2); + const std::vector &mask); #ifdef DEBUG_OUTPUT_DIR void initialize_store(const std::string& dir_to_clear); @@ -654,7 +553,7 @@ SurfaceCut Slic3r::cut_surface(const ExPolygons &shapes, } indexed_triangle_set Slic3r::cut2model(const SurfaceCut &cut, - const priv::Project3f &projection) + const Emboss::IProject3f &projection) { assert(!cut.empty()); size_t count_vertices = cut.vertices.size() * 2; @@ -1105,8 +1004,6 @@ priv::ModelCutId priv::ModelCut2index::calc_id(uint32_t index) const return result; } -uint32_t priv::ModelCut2index::get_count() const { return m_count; } - // cut_from_model help functions namespace priv { @@ -1230,6 +1127,22 @@ void set_face_type(FaceTypeMap &face_type_map, /// In/Out map with faces type void flood_fill_inner(const CutMesh &mesh, FaceTypeMap &face_type_map); +/// +/// Collect connected inside faces +/// Collect outline half edges +/// +/// Queue of face to process - find connected +/// [Output] collected Face indices from mesh +/// [Output] collected Halfedge indices from mesh +/// Use flag inside / outside +/// NOTE: Modify in function: inside -> inside_processed +/// mesh to process +void collect_surface_data(std::queue &process, + std::vector &faces, + std::vector &outlines, + FaceTypeMap &face_type_map, + const CutMesh &mesh); + /// /// Create areas from mesh surface /// @@ -1379,10 +1292,6 @@ void priv::set_face_type(FaceTypeMap &face_type_map, const CutMesh &shape_mesh, const ShapePoint2index &shape2index) { - // clean types - for (FI fi : mesh.faces()) - face_type_map[fi] = FaceType::not_constrained; - for (EI ei : mesh.edges()) { if (!ecm[ei]) continue; HI hi = mesh.halfedge(ei); @@ -1406,7 +1315,7 @@ priv::CutAOIs priv::cut_from_model(CutMesh &cgal_model, const ShapePoint2index &s2i) { // pointer to edge or face shape_map - VertexShapeMap vert_shape_map = cgal_model.add_property_map(vert_shape_map_name).first; + VertexShapeMap vert_shape_map = cgal_model.add_property_map(vert_shape_map_name, nullptr).first; // detect anomalities in visitor. bool is_valid = true; @@ -1416,7 +1325,7 @@ priv::CutAOIs priv::cut_from_model(CutMesh &cgal_model, Visitor visitor{cgal_model, cgal_shape, edge_shape_map, face_shape_map, vert_shape_map, &is_valid}; // a property map containing the constrained-or-not status of each edge - EdgeBoolMap ecm = cgal_model.add_property_map(is_constrained_edge_name).first; + EdgeBoolMap ecm = cgal_model.add_property_map(is_constrained_edge_name, false).first; const auto &p = CGAL::parameters::visitor(visitor) .edge_is_constrained_map(ecm) .throw_on_self_intersection(false); @@ -1425,7 +1334,7 @@ priv::CutAOIs priv::cut_from_model(CutMesh &cgal_model, if (!is_valid) return {}; - FaceTypeMap face_type_map = cgal_model.add_property_map(face_type_map_name).first; + FaceTypeMap face_type_map = cgal_model.add_property_map(face_type_map_name, FaceType::not_constrained).first; // Select inside and outside face in model set_face_type(face_type_map, cgal_model, vert_shape_map, ecm, cgal_shape, s2i); @@ -1564,11 +1473,6 @@ void priv::create_reduce_map(ReductionMap &reduction_map, const CutMesh &mesh) const VertexShapeMap &vert_shape_map = mesh.property_map(vert_shape_map_name).first; const EdgeBoolMap &ecm = mesh.property_map(is_constrained_edge_name).first; - // IMPROVE: find better way to initialize - // initialize reduction map - for (VI reduction_from : mesh.vertices()) - reduction_map[reduction_from] = reduction_from; - // check if vertex was made by edge_2 which is diagonal of quad auto is_reducible_vertex = [&vert_shape_map](VI reduction_from) -> bool { const IntersectingElement *ie = vert_shape_map[reduction_from]; @@ -1589,8 +1493,9 @@ void priv::create_reduce_map(ReductionMap &reduction_map, const CutMesh &mesh) VI left = mesh.source(hi); assert(is_reducible_vertex(erase)); assert(!is_reducible_vertex(left)); - bool is_first = reduction_map[erase] == erase; - if (is_first) + VI &vi = reduction_map[erase]; + // check if it is first add + if (!vi.is_valid()) reduction_map[erase] = left; // I have no better rule than take the first // for decide which reduction will be better @@ -1612,156 +1517,6 @@ void priv::create_reduce_map(ReductionMap &reduction_map, const CutMesh &mesh) #endif // DEBUG_OUTPUT_DIR } -SurfaceCut priv::create_index_triangle_set(const std::vector &faces, - size_t count_outlines, - const CutMesh &mesh, - const ReductionMap &reduction_map, - ConvertMap &v2v) -{ - // clear v2v - // more than one cut can share vertex and each cut need its own conversion - for (FI fi : faces) { - HI hi = mesh.halfedge(fi); - for (VI vi : {mesh.source(hi), mesh.target(hi), mesh.target(mesh.next(hi))}) - v2v[vi] = std::numeric_limits::max(); - } - - // IMPROVE: use reduced count of faces and outlines - size_t indices_size = faces.size(); - size_t vertices_size = (indices_size * 3 - count_outlines / 2) / 2; - - SurfaceCut sc; - sc.indices.reserve(indices_size); - sc.vertices.reserve(vertices_size); - - for (FI fi : faces) { - //auto reduce = get_reduce_vertex(fi); - HI hi = mesh.halfedge(fi); - HI hi_end = hi; - - Vec3i its_face; - // index into its_face - int its_face_id = 0; - bool exist_reduction = false; - do { - VI vi = mesh.source(hi); - VI vi_r = reduction_map[vi]; - if (vi_r != vi) { - exist_reduction = true; - vi = vi_r; - } - - size_t index = v2v[vi]; - if (index == std::numeric_limits::max()) { - index = sc.vertices.size(); - const auto &p = mesh.point(vi); - // create vertex in result - sc.vertices.emplace_back(p.x(), p.y(), p.z()); - v2v[vi] = index; - } - assert(index != std::numeric_limits::max()); - its_face[its_face_id++] = index; - hi = mesh.next(hi); - } while (hi != hi_end); - - // prevent add reduced triangle - if (exist_reduction && ( - its_face[0] == its_face[1] || - its_face[1] == its_face[2] || - its_face[2] == its_face[0] - )) - continue; - - sc.indices.emplace_back(std::move(its_face)); - } - - // reduce size with respect to reduction triangles - sc.indices.shrink_to_fit(); - sc.vertices.shrink_to_fit(); - return sc; -} - -SurfaceCut::CutContour priv::create_cut(const std::vector &outlines, - const CutMesh &mesh, - const ReductionMap &reduction_map, - const ConvertMap &v2v) -{ - using Index = SurfaceCut::Index; - SurfaceCut::CutContour cut; - SurfaceCut::CutContour unclosed_cut; - for (HI hi : outlines) { - VI vi_s = mesh.source(hi); - VI vi_t = mesh.target(hi); - // reduced vertex - VI vi_s_r = reduction_map[vi_s]; - VI vi_t_r = reduction_map[vi_t]; - // is reduced edge? - if (vi_s_r == vi_t || vi_t_r == vi_s) continue; - - // source vertex (from) - Index vi_from = v2v[vi_s_r]; - assert(vi_from != std::numeric_limits::max()); - - // target vertex (to) - Index vi_to = v2v[vi_t_r]; - assert(vi_to != std::numeric_limits::max()); - - std::vector *cut_move = nullptr; - std::vector *cut_connect = nullptr; - for (std::vector &cut : unclosed_cut) { - if (cut.back() != vi_from) continue; - if (cut.front() == vi_to) { - // cut closing - cut_move = &cut; - } else { - cut_connect = &cut; - } - break; - } - if (cut_move != nullptr) { - // index of closed cut - size_t index = cut_move - &unclosed_cut.front(); - // move cut to result - cut.emplace_back(std::move(*cut_move)); - // remove it from unclosed cut - unclosed_cut.erase(unclosed_cut.begin() + index); - } else if (cut_connect != nullptr) { - // try find tail to connect cut - std::vector *cut_tail = nullptr; - for (std::vector &cut : unclosed_cut) { - if (cut.front() != vi_to) continue; - cut_tail = &cut; - break; - } - if (cut_tail != nullptr) { - // index of tail - size_t index = cut_tail - &unclosed_cut.front(); - // move to connect vector - cut_connect->insert(cut_connect->end(), - make_move_iterator(cut_tail->begin()), - make_move_iterator(cut_tail->end())); - // remove tail from unclosed cut - unclosed_cut.erase(unclosed_cut.begin() + index); - } else { - cut_connect->push_back(vi_to); - } - } else { // not found - bool create_cut = true; - // try to insert to front of cut - for (std::vector &cut : unclosed_cut) { - if (cut.front() != vi_to) continue; - cut.insert(cut.begin(), vi_from); - create_cut = false; - break; - } - if (create_cut) - unclosed_cut.emplace_back(std::vector{vi_from, vi_to}); - } - } - assert(unclosed_cut.empty()); - return cut; -} - priv::CutAOIs priv::create_cut_area_of_interests(const CutMesh &mesh, const ExPolygons &shapes, FaceTypeMap &face_type_map) @@ -2347,26 +2102,20 @@ BoundingBoxf3 bounding_box(const CutAOI &cut, const CutMesh &mesh); BoundingBoxf3 bounding_box(const CutMesh &mesh); BoundingBoxf3 bounding_box(const SurfacePatch &ecut); -SurfacePatch create_surface_patch(const std::vector &fis, const CutMesh &mesh); - /// -/// Create patch with no use of vertices made by diagonal edge in rectangle of -/// shape side +/// Create patch /// -/// Triangles and outline edges of patch -/// Source of triangles -/// Map for reduction of vertices -/// -SurfacePatch create_reduced_patch(CutAOI &cut, - const CutMesh &mesh, - const ReductionMap &rmap); +/// Define patch faces +/// Source of fis +/// Options to reduce vertices from fis. +/// NOTE: Used for skip vertices made by diagonal edge in rectangle of shape side +/// Patch +SurfacePatch create_surface_patch(const std::vector &fis, + const CutMesh &mesh, + const ReductionMap *rmap = nullptr); } // namespace priv -bool priv::merge_intersection(SurfaceCut &cut1, const SurfaceCut &cut2) { - return false; -} - void priv::create_face_types(FaceTypeMap &map, const CutMesh &tm1, const CutMesh &tm2, @@ -2541,92 +2290,29 @@ BoundingBoxf3 priv::bounding_box(const SurfacePatch &ecut) { return bounding_box(ecut.mesh); } -priv::SurfacePatch priv::create_reduced_patch(CutAOI &cut, - const CutMesh &mesh, - const ReductionMap &rmap) -{ - std::vector is_counted(mesh.vertices().size(), {false}); - uint32_t count_vertices = 0; - for (FI fi : cut.first) { - for (VI vi : mesh.vertices_around_face(mesh.halfedge(fi))) { - // Will vertex be reduced? - if (vi != rmap[vi]) continue; - if (!is_counted[vi.idx()]) { - is_counted[vi.idx()] = true; - ++count_vertices; - } - } - } - - uint32_t count_faces = cut.first.size(); - // IMPROVE: Value is greater than neccessary, guess it better - uint32_t count_edges = count_faces * 3; - - CutMesh cm; - cm.reserve(count_vertices, count_edges, count_faces); - - // vertex conversion function - constexpr uint32_t def_val = std::numeric_limits::max(); - std::vector v_cvt(mesh.vertices().size(), {def_val}); - for (FI fi : cut.first) { - std::array t; - int index = 0; - bool exist_reduction = false; - for (VI vi : mesh.vertices_around_face(mesh.halfedge(fi))) { - VI vi_r = rmap[vi]; - if (vi != vi_r) { - exist_reduction = true; - vi = vi_r; - } - - assert(vi.idx() < v_cvt.size()); - uint32_t &cvt = v_cvt[vi.idx()]; - if (cvt == def_val) { - cvt = cm.vertices().size(); - cm.add_vertex(mesh.point(vi)); - } - t[index++] = VI(cvt); - } - - // prevent add reduced triangle - if (exist_reduction && - (t[0] == t[1] || - t[1] == t[2] || - t[2] == t[0])) - continue; - - cm.add_face(t[0], t[1], t[2]); - } - assert(count_vertices == cm.vertices().size()); - assert(count_faces >= cm.faces().size()); - assert(count_edges >= cm.edges().size()); - - // convert VI from this patch to source VI, when exist - CvtVI2VI cvt = cm.add_property_map(patch_source_name).first; - // vi_s .. VertexIndex into mesh (source) - // vi_d .. new VertexIndex in cm (destination) - for (uint32_t vi_s = 0; vi_s < v_cvt.size(); ++vi_s) { - uint32_t vi_d = v_cvt[vi_s]; - if (vi_d == def_val) continue; - // check only one conversion - assert(!cvt[VI(vi_d)].is_valid()); - cvt[VI(vi_d)] = VI(vi_s); - } - - return {std::move(cm)}; -} - -priv::SurfacePatch priv::create_surface_patch(const std::vector &fis, const CutMesh &mesh) +priv::SurfacePatch priv::create_surface_patch(const std::vector &fis, + const CutMesh &mesh, + const ReductionMap *rmap) { std::vector is_counted(mesh.vertices().size(), {false}); uint32_t count_vertices = 0; - for (FI fi : fis) { - for (VI vi : mesh.vertices_around_face(mesh.halfedge(fi))) { - if (!is_counted[vi.idx()]) { - is_counted[vi.idx()] = true; - ++count_vertices; - } - } + if (rmap == nullptr) { + for (FI fi : fis) + for (VI vi : mesh.vertices_around_face(mesh.halfedge(fi))) + if (!is_counted[vi.idx()]) { + is_counted[vi.idx()] = true; + ++count_vertices; + } + } else { + for (FI fi : fis) + for (VI vi : mesh.vertices_around_face(mesh.halfedge(fi))) { + // Will vertex be reduced? + if ((*rmap)[vi].is_valid()) continue; + if (!is_counted[vi.idx()]) { + is_counted[vi.idx()] = true; + ++count_vertices; + } + } } uint32_t count_faces = fis.size(); @@ -2639,22 +2325,57 @@ priv::SurfacePatch priv::create_surface_patch(const std::vector &fis, const // vertex conversion function constexpr uint32_t def_val = std::numeric_limits::max(); std::vector v_cvt(mesh.vertices().size(), {def_val}); - for (FI fi : fis) { - std::array t; - int index = 0; - for (VI vi : mesh.vertices_around_face(mesh.halfedge(fi))) { - uint32_t &cvt = v_cvt[vi.idx()]; - if (cvt == def_val) { - cvt = cm.vertices().size(); - cm.add_vertex(mesh.point(vi)); + + if (rmap == nullptr) { + for (FI fi : fis) { + std::array t; + int index = 0; + for (VI vi : mesh.vertices_around_face(mesh.halfedge(fi))) { + uint32_t &cvt = v_cvt[vi.idx()]; + if (cvt == def_val) { + cvt = cm.vertices().size(); + cm.add_vertex(mesh.point(vi)); + } + t[index++] = VI(cvt); } - t[index++] = VI(cvt); + cm.add_face(t[0], t[1], t[2]); + } + } else { + for (FI fi :fis) { + std::array t; + int index = 0; + bool exist_reduction = false; + for (VI vi : mesh.vertices_around_face(mesh.halfedge(fi))) { + VI vi_r = (*rmap)[vi]; + if (vi_r.is_valid()) { + exist_reduction = true; + vi = vi_r; + } + + assert(vi.idx() < v_cvt.size()); + uint32_t &cvt = v_cvt[vi.idx()]; + if (cvt == def_val) { + cvt = cm.vertices().size(); + cm.add_vertex(mesh.point(vi)); + } + t[index++] = VI(cvt); + } + + // prevent add reduced triangle + if (exist_reduction && + (t[0] == t[1] || + t[1] == t[2] || + t[2] == t[0])) + continue; + + cm.add_face(t[0], t[1], t[2]); } - cm.add_face(t[0], t[1], t[2]); } assert(count_vertices == cm.vertices().size()); - assert(count_faces == cm.faces().size()); + assert((rmap == nullptr && count_faces == cm.faces().size()) || + (rmap != nullptr && count_faces >= cm.faces().size())); assert(count_edges >= cm.edges().size()); + // convert VI from this patch to source VI, when exist CvtVI2VI cvt = cm.add_property_map(patch_source_name).first; @@ -2671,15 +2392,65 @@ priv::SurfacePatch priv::create_surface_patch(const std::vector &fis, const return {std::move(cm)}; } +// diff_models help functions namespace priv { + +using BBS = std::vector; /// /// Create bounding boxes for AOI /// /// Cutted AOI from models /// Source points of cuts /// Bounding boxes -std::vector create_bbs(const VCutAOIs &cuts, - const CutMeshes &cut_models); +BBS create_bbs(const VCutAOIs &cuts, const CutMeshes &cut_models); + +using Primitive = CGAL::AABB_face_graph_triangle_primitive; +using Traits = CGAL::AABB_traits; +using Ray = EpicKernel::Ray_3; +using Tree = CGAL::AABB_tree; +using Trees = std::vector; +/// +/// Create AABB trees for check when patch is whole inside of model +/// +/// Source for trees +/// trees +Trees create_trees(const CutMeshes &models); + +/// +/// Check whether bounding box has intersection with model +/// +/// Bounding box to check +/// Model to check with +/// All bounding boxes from VCutAOIs +/// Help index into VCutAOIs +/// True when exist bounding boxes intersection +bool has_bb_intersection(const BoundingBoxf3 &bb, + size_t model_index, + const BBS &bbs, + const ModelCut2index &m2i); + +/// +/// Only for model without intersection +/// Use ray (in projection direction) from a point from patch +/// and count intersections: pair .. outside | odd .. inside +/// +/// Patch to check +/// Model converted to AABB tree +/// Define direction of projection +/// True when patch point lay inside of model defined by tree, +/// otherwise FALSE +bool is_patch_inside_of_model(const SurfacePatch &patch, + const Tree &tree, + const Project3f &projection); + +/// +/// Return some shape point index which identify shape +/// NOTE: Used to find expolygon index +/// +/// Used to search source shapes poin +/// +/// shape point index +uint32_t get_shape_point_index(const CutAOI &cut, const CutMesh &model); using PatchNumber = CutMesh::Property_map; /// @@ -2703,7 +2474,13 @@ SurfacePatch separate_patch(size_t n, /// In/Out Patches void divide_patch(size_t i, SurfacePatches &patches); -} +/// +/// Fill outline in patches by open edges +/// +/// Input/Output meshes with open edges +void collect_open_edges(SurfacePatches &patches); + +} // namespace priv std::vector priv::create_bbs(const VCutAOIs &cuts, const CutMeshes &cut_models) @@ -2724,6 +2501,79 @@ std::vector priv::create_bbs(const VCutAOIs &cuts, return bbs; } + +priv::Trees priv::create_trees(const CutMeshes &models) { + Trees result; + result.reserve(models.size()); + for (const CutMesh &model : models) { + Tree tree; + tree.insert(faces(model).first, faces(model).second, model); + tree.build(); + result.emplace_back(std::move(tree)); + } + return result; +} + +bool priv::has_bb_intersection(const BoundingBoxf3 &bb, + size_t model_index, + const BBS &bbs, + const ModelCut2index &m2i) +{ + const auto&offsets = m2i.get_offsets(); + // for cut index with model_index2 + size_t start = offsets[model_index]; + size_t next = model_index + 1; + size_t end = (next < offsets.size()) ? offsets[next] : m2i.get_count(); + for (size_t bb_index = start; bb_index < end; bb_index++) + if (bb.intersects(bbs[bb_index])) return true; + return false; +} + +bool priv::is_patch_inside_of_model(const SurfacePatch &patch, + const Tree &tree, + const Project3f &projection) +{ + // TODO: Solve model with hole in projection direction !!! + const P3 &a = patch.mesh.point(VI(0)); + Vec3f a_(a.x(), a.y(), a.z()); + Vec3f b_ = projection.project(a_); + P3 b(b_.x(), b_.y(), b_.z()); + + Ray ray_query(a, b); + size_t count = tree.number_of_intersected_primitives(ray_query); + bool is_in = (count % 2) == 1; + + // try opposit direction result should be same, otherwise open model is used + //Vec3f c_ = a_ - (b_ - a_); // opposit direction + //P3 c(c_.x(), c_.y(), c_.z()); + //Ray ray_query2(a, b); + //size_t count2 = tree.number_of_intersected_primitives(ray_query2); + //bool is_in2 = (count2 % 2) == 1; + assert(((tree.number_of_intersected_primitives( + Ray(a, P3(2 * a.x() - b.x(), + 2 * a.y() - b.y(), + 2 * a.z() - b.z()))) % + 2) == 1) == is_in); + return is_in; +} + +uint32_t priv::get_shape_point_index(const CutAOI &cut, const CutMesh &model) +{ + // map is created during intersection by corefine visitor + const VertexShapeMap &vert_shape_map = model.property_map(vert_shape_map_name).first; + // for each half edge of outline + for (HI hi : cut.second) { + VI vi = model.source(hi); + const IntersectingElement *ie = vert_shape_map[vi]; + if (ie == nullptr) continue; + assert(ie->shape_point_index != std::numeric_limits::max()); + return ie->shape_point_index; + } + // can't found any intersecting element in cut + assert(false); + return 0; +} + priv::SurfacePatch priv::separate_patch(size_t n, const PatchNumber &patch_number, const SurfacePatch &patch, @@ -2796,88 +2646,39 @@ void priv::divide_patch(size_t i, SurfacePatches &patches) { patch = separate_patch(0, patch_number, patch, cvt_from); } -priv::SurfacePatches priv::diff_models(VCutAOIs &cuts, - const ModelCut2index &m2i, - /*const*/ CutMeshes &cut_models, - /*const*/ CutMeshes &models, - const Emboss::IProject3f &projection) +void priv::collect_open_edges(SurfacePatches &patches) { + for (SurfacePatch &patch : patches) { + patch.outline.clear(); + const CutMesh &mesh = patch.mesh; + for (FI fi : mesh.faces()) { + HI hi1 = mesh.halfedge(fi); + assert(hi1.is_valid()); + HI hi2 = mesh.next(hi1); + assert(hi2.is_valid()); + HI hi3 = mesh.next(hi2); + assert(hi3.is_valid()); + // Is fi triangle? + assert(mesh.next(hi3) == hi1); + for (HI hi : {hi1, hi2, hi3}) { + HI hi_op = mesh.opposite(hi); + FI fi_op = mesh.face(hi_op); + if (!fi_op.is_valid()) patch.outline.push_back(hi); + } + } + } +} + +priv::SurfacePatches priv::diff_models(VCutAOIs &cuts, + const ModelCut2index &m2i, + /*const*/ CutMeshes &cut_models, + /*const*/ CutMeshes &models, + const Project3f &projection) { // create bounding boxes for cuts std::vector bbs = create_bbs(cuts, cut_models); - // check whether cut has intersection with model - auto has_bb_intersection = [&bbs, &m2i, &cuts] - (const BoundingBoxf3 &bb, size_t model_index) -> bool { - // for cut index with model_index2 - size_t start = m2i.calc_index({uint32_t(model_index), 0}); - size_t end = start + cuts[model_index].size(); - for (size_t bb_index = start; bb_index < end; bb_index++) - if (bb.intersects(bbs[bb_index])) return true; - return false; - }; - // IMPROVE: Do not make Tree twice, when exist out of cut function - using Primitive = CGAL::AABB_face_graph_triangle_primitive; - using Traits = CGAL::AABB_traits; - using Ray = EpicKernel::Ray_3; - using Tree = CGAL::AABB_tree; - using Trees = std::vector; - Trees trees; - trees.reserve(models.size()); - for (CutMesh &model: models) { - Tree tree; - tree.insert(faces(model).first, faces(model).second, model); - tree.build(); - trees.emplace_back(std::move(tree)); - } - - // only for model without intersection - // use ray (in projection direction) from a point from patch - // and count intersections: pair .. outside | odd .. inside - auto is_patch_inside_of_model = [&trees, &projection] - (SurfacePatch &patch, size_t model_index) { - // TODO: Solve model with hole in projection direction !!! - const P3 &a = patch.mesh.point(VI(0)); - Vec3f a_(a.x(), a.y(), a.z()); - Vec3f b_ = projection.project(a_); - P3 b(b_.x(), b_.y(), b_.z()); - Tree &tree = trees[model_index]; - - Ray ray_query(a, b); - size_t count = tree.number_of_intersected_primitives(ray_query); - bool is_in = (count % 2) == 1; - - // try opposit direction result should be same, otherwise open model is used - //Vec3f c_ = a_ - (b_ - a_); // opposit direction - //P3 c(c_.x(), c_.y(), c_.z()); - //Ray ray_query2(a, b); - //size_t count2 = tree.number_of_intersected_primitives(ray_query2); - //bool is_in2 = (count2 % 2) == 1; - assert(((tree.number_of_intersected_primitives( - Ray(a, P3(2 * a.x() - b.x(), - 2 * a.y() - b.y(), - 2 * a.z() - b.z()))) % - 2) == 1) == is_in); - return is_in; - }; - - // used to find expolygon index - auto get_shape_point_index = [] - (const CutAOI &cut, const CutMesh &model) -> uint32_t{ - // map is created during intersection by corefine visitor - const VertexShapeMap &vert_shape_map = model.property_map(vert_shape_map_name).first; - // for each half edge of outline - for (HI hi : cut.second) { - VI vi = model.source(hi); - const IntersectingElement *ie = vert_shape_map[vi]; - if (ie == nullptr) continue; - assert(ie->shape_point_index != std::numeric_limits::max()); - return ie->shape_point_index; - } - // can't found any intersecting element in cut - assert(false); - return 0; - }; + Trees trees = create_trees(models); SurfacePatches patches; // queue of patches for one AOI (permanent with respect to for loop) @@ -2892,8 +2693,8 @@ priv::SurfacePatches priv::diff_models(VCutAOIs &cuts, create_reduce_map(vertex_reduction_map, cut_model); for (size_t cut_index = 0; cut_index < model_cuts.size(); ++cut_index, ++index) { - CutAOI &cut = model_cuts[cut_index]; - SurfacePatch patch = create_reduced_patch(cut, cut_model, vertex_reduction_map); + const CutAOI &cut = model_cuts[cut_index]; + SurfacePatch patch = create_surface_patch(cut.first, cut_model, &vertex_reduction_map); patch.bb = bbs[index]; patch.aoi_id = index; patch.model_id = model_index; @@ -2905,10 +2706,10 @@ priv::SurfacePatches priv::diff_models(VCutAOIs &cuts, // do not clip source model itself if (model_index == model_index2) continue; for (SurfacePatch &patch : aoi_patches) { - if (has_bb_intersection(patch.bb, model_index2) && + if (has_bb_intersection(patch.bb, model_index2, bbs, m2i) && clip_cut(patch, models[model_index2])){ patch.just_cliped = true; - } else if (is_patch_inside_of_model(patch, model_index2)) + } else if (is_patch_inside_of_model(patch, trees[model_index2], projection)) patch.full_inside = true; } // erase full inside @@ -2934,30 +2735,9 @@ priv::SurfacePatches priv::diff_models(VCutAOIs &cuts, cut_model_.remove_property_map(vertex_reduction_map); } - // fill outlines // Also use outline inside of patches(made by non manifold models) // IMPROVE: trace outline from AOIs - for (SurfacePatch &patch : patches) { - patch.outline.clear(); - const CutMesh &mesh = patch.mesh; - for (FI fi : mesh.faces()) { - HI hi1 = mesh.halfedge(fi); - assert(hi1.is_valid()); - HI hi2 = mesh.next(hi1); - assert(hi2.is_valid()); - HI hi3 = mesh.next(hi2); - assert(hi3.is_valid()); - // Is fi triangle? - assert(mesh.next(hi3) == hi1); - for (HI hi : {hi1, hi2, hi3}) { - HI hi_op = mesh.opposite(hi); - FI fi_op = mesh.face(hi_op); - if (!fi_op.is_valid()) - patch.outline.push_back(hi); - } - } - } - + collect_open_edges(patches); return patches; } @@ -3021,84 +2801,6 @@ std::vector priv::select_patches( return result; } -bool Slic3r::merge_intersection(SurfaceCut& sc1, const SurfaceCut& sc2) { - return priv::merge_intersection(sc1, sc2); -} - -SurfaceCut priv::merge_intersections( - SurfaceCuts &cuts, const CutAOIs& cutAOIs, const std::vector &use_cut) -{ - // create bounding boxes for cuts - std::vector bbs; - bbs.reserve(cuts.size()); - for (const SurfaceCut &cut : cuts) bbs.push_back(bounding_box(cut)); - - // extend used bb by intersecting bb - // NOTE: after merge 2 cuts could appears - // new intersection on surface of merged in - - std::vector finished(cuts.size(), {false}); - - // find intersection of cuts by Bounding boxes intersection - for (size_t cut_index = 0; cut_index < cuts.size(); ++cut_index) - { - if (finished[cut_index]) continue; - if (!use_cut[cut_index]) continue; - - BoundingBoxf3 &result_bb = bbs[cut_index]; - SurfaceCut &cut = cuts[cut_index]; - - // all merged cuts into cut_index - std::vector merged(cuts.size(), {false}); - - // merged in last iteration - std::vector new_merged; - - bool exist_new_extension= false; - bool is_first = true; - // while exist bb intersection - do { - exist_new_extension = false; - new_merged = std::vector(cuts.size(), {false}); - // check when exist intersection with result_bb - for (const BoundingBoxf3 &bb : bbs) { - size_t bb_index = &bb - &bbs.front(); - // do not merge itself - if (cut_index == bb_index) continue; - if (!is_first && merged[bb_index]) continue; - if (!bb.intersects(result_bb)) { - if (is_first) continue; - bool has_new_intersection = false; - for (size_t i = 0; i < cuts.size(); i++) { - if (!new_merged[i]) continue; - if (!bbs[i].intersects(bb)) continue; - // TODO: check that really intersect by merging - has_new_intersection = true; - } - if (!has_new_intersection) continue; - } - if (!priv::merge_intersection(cut, cuts[bb_index])) continue; - - result_bb = bounding_box(cut); - merged[bb_index] = true; - finished[bb_index] = true; - new_merged[bb_index] = true; - exist_new_extension = true; - } - is_first = false; - } while (exist_new_extension); - } - - // Cuts merged in are signed in finished vector as TRUE - // All rest cuts must be merged simple way - SurfaceCut result; - for (size_t cut_index = 0; cut_index < cuts.size(); ++cut_index) { - if (finished[cut_index]) continue; - append(result, std::move(cuts[cut_index])); - } - return result; -} - // help function to 'merge_patches' namespace priv { @@ -3188,7 +2890,8 @@ SurfaceCut priv::patch2cut(SurfacePatch &patch) CutMesh &mesh = patch.mesh; std::string convert_map_name = "v:convert"; - ConvertMap convert_map = mesh.add_property_map(convert_map_name).first; + CutMesh::Property_map convert_map = + mesh.add_property_map(convert_map_name).first; size_t indices_size = mesh.faces().size(); size_t vertices_size = mesh.vertices().size(); @@ -3242,7 +2945,38 @@ SurfaceCut priv::patch2cut(SurfacePatch &patch) return sc; } -SurfaceCut priv::merge_patches(SurfacePatches &patches, std::vector mask) +namespace priv { + +/// +/// Merge two surface cuts together +/// Added surface cut will be consumed +/// +/// Surface cut to extend +/// Surface cut to consume +void append(SurfaceCut &sc, SurfaceCut &&sc_add); + +}// namespace priv + +void priv::append(SurfaceCut &sc, SurfaceCut &&sc_add) +{ + if (sc.empty()) { + sc = std::move(sc_add); + return; + } + + if (!sc_add.contours.empty()) { + SurfaceCut::Index offset = static_cast( + sc.vertices.size()); + size_t require = sc.contours.size() + sc_add.contours.size(); + if (sc.contours.capacity() < require) sc.contours.reserve(require); + for (std::vector &cut : sc_add.contours) + for (SurfaceCut::Index &i : cut) i += offset; + Slic3r::append(sc.contours, std::move(sc_add.contours)); + } + its_merge(sc, std::move(sc_add)); +} + +SurfaceCut priv::merge_patches(SurfacePatches &patches, const std::vector& mask) { SurfaceCut result; for (SurfacePatch &patch : patches) { @@ -3253,38 +2987,6 @@ SurfaceCut priv::merge_patches(SurfacePatches &patches, std::vector mask) return result; } -SurfaceCuts priv::create_surface_cuts(const CutAOIs &cuts, - CutMesh &mesh, - const ReductionMap &reduction_map) -{ - // conversion map between vertex index in cgal_model and indices in result - // used instead of std::map - // NOTE: can't be used outside because it is rewrited during create_index_triangle_set - std::string convert_map_name = "v:convert"; - priv::ConvertMap convert_map = mesh.add_property_map(convert_map_name).first; - - // initialize convert_map to MAX values - for (VI vi : mesh.vertices()) - convert_map[vi] = std::numeric_limits::max(); - - SurfaceCuts result; - for (const CutAOI &cut : cuts) { - const std::vector& faces = cut.first; - const std::vector &outlines = cut.second; - - // convert_map could be used separately for each surface cut. - // But it is moore faster to use one memory allocation for them all. - SurfaceCut sc = create_index_triangle_set(faces, outlines.size(), mesh, reduction_map, convert_map); - - // connect outlines - sc.contours = create_cut(outlines, mesh, reduction_map, convert_map); - result.emplace_back(std::move(sc)); - } - - mesh.remove_property_map(convert_map); - return result; -} - #ifdef DEBUG_OUTPUT_DIR #include diff --git a/src/libslic3r/CutSurface.hpp b/src/libslic3r/CutSurface.hpp index b0bafd930..7e68c87f2 100644 --- a/src/libslic3r/CutSurface.hpp +++ b/src/libslic3r/CutSurface.hpp @@ -17,41 +17,15 @@ namespace Slic3r{ /// struct SurfaceCut : public indexed_triangle_set { - // connected cutted surface --> inheritance is used - //indexed_triangle_set mesh; - // vertex indices(index to mesh vertices) using Index = unsigned int; - using CutContour = std::vector>; + using Contour = std::vector; + using Contours = std::vector; // list of circulated open surface - CutContour contours; - - // Conversion map from vertex index to contour point - // Could be used for filtration of surface cuts - // Still I don't have an idea how to filtrate it. - // What is wanted result on wave? - // std::map vertex2contour; + Contours contours; }; using SurfaceCuts = std::vector; -/// -/// Merge two surface cuts together -/// Added surface cut will be consumed -/// -/// Surface cut to extend -/// Surface cut to consume -void append(SurfaceCut &sc, SurfaceCut &&sc_add); - -// call private function with same name to test it -bool merge_intersection(SurfaceCut &sc1, const SurfaceCut &sc2); - - -/// -/// Merge surface cuts int one -/// -/// input -SurfaceCut merge(SurfaceCuts&& cuts); - /// /// Cut surface shape from models. /// diff --git a/tests/libslic3r/test_cut_surface.cpp b/tests/libslic3r/test_cut_surface.cpp index d5bb746ed..2a634687f 100644 --- a/tests/libslic3r/test_cut_surface.cpp +++ b/tests/libslic3r/test_cut_surface.cpp @@ -162,20 +162,4 @@ TEST_CASE("CutSurface in 3mf", "[Emboss]") its_write_obj(cut, "C:/data/temp/cutSurface/result_cut.obj"); } -#include "libslic3r/Format/OBJ.hpp" -TEST_CASE("Merge Cuts", "[Emboss]") { - std::string dir = "C:/data/temp/"; - TriangleMesh tm1, tm2; - load_obj((dir + "aoi3.obj").c_str(), &tm1); - load_obj((dir + "aoi6.obj").c_str(), &tm2); - auto create_sc = [](TriangleMesh &tm) -> SurfaceCut { - SurfaceCut sc; - sc.vertices = std::move(tm.its.vertices); - sc.indices = std::move(tm.its.indices); - // sc.contours = ??? - return sc; - }; - SurfaceCut sc1 = create_sc(tm1), sc2 = create_sc(tm2); - assert(merge_intersection(sc1, sc2)); -} #endif // DEBUG_3MF