Visual Computing Library  devel
Loading...
Searching...
No Matches
topology.h
1/*****************************************************************************
2 * VCLib *
3 * Visual Computing Library *
4 * *
5 * Copyright(C) 2021-2025 *
6 * Visual Computing Lab *
7 * ISTI - Italian National Research Council *
8 * *
9 * All rights reserved. *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the Mozilla Public License Version 2.0 as published *
13 * by the Mozilla Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * Mozilla Public License Version 2.0 *
20 * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. *
21 ****************************************************************************/
22
23#ifndef VCL_ALGORITHMS_MESH_STAT_TOPOLOGY_H
24#define VCL_ALGORITHMS_MESH_STAT_TOPOLOGY_H
25
26#include "../sort.h"
27
28#include <vclib/mesh.h>
29#include <vclib/space/complex.h>
30
31#include <map>
32#include <set>
33#include <stack>
34
35namespace vcl {
36
37namespace detail {
38
39// function called for the Container Cont of the Mesh
40template<typename Cont>
41void setReferencedVertices(const auto& mesh, auto& refs, uint& nRefs)
42{
43 // check if the Cont container of the Mesh has vertex references
44 if constexpr (comp::HasVertexReferences<typename Cont::ElementType>) {
45 // if there are still some vertices non-referenced
46 if (nRefs < mesh.vertexNumber()) {
47 constexpr uint ELEM_ID = Cont::ElementType::ELEMENT_ID;
48 // for eache element of the Cont container
49 for (const auto& el : mesh.template elements<ELEM_ID>()) {
50 // for each vertex of the element
51 for (uint vi : el.vertexIndices()) {
52 // vertex references shoule never be null
53 assert(vi != UINT_NULL);
54 if (!refs[vi]) {
55 // set the vertex as referenced
56 refs[vi] = true;
57 nRefs++;
58 }
59 }
60 }
61 }
62 }
63}
64
65// function called for each container of the Mesh, using variadic templates
66template<typename... Cont>
67void setReferencedVertices(
68 const auto& mesh,
69 auto& refs,
70 uint& nRefs,
71 TypeWrapper<Cont...>)
72{
73 // call the setReferencedVerticesOnVector function for each container of the
74 // mesh
75 (setReferencedVertices<Cont>(mesh, refs, nRefs), ...);
76}
77
78// struct to store the information of the wedge texcoords
79template<typename WedgeTexCoordType>
80struct WedgeTexCoordsInfo
81{
82 WedgeTexCoordType texCoord;
83 uint texCoordIndex;
84
85 bool operator<(const WedgeTexCoordsInfo& other) const
86 {
87 if (texCoordIndex == other.texCoordIndex) {
88 return texCoord < other.texCoord;
89 }
90 return texCoordIndex < other.texCoordIndex;
91 }
92};
93
94template<FaceMeshConcept MeshType>
95std::vector<bool> nonManifoldVerticesVectorBool(const MeshType& m)
96 requires HasPerFaceAdjacentFaces<MeshType>
97{
99
100 using FaceType = MeshType::FaceType;
101
102 std::vector<bool> nonManifoldVertices(m.vertexContainerSize(), false);
103
104 std::vector<uint> TD(m.vertexContainerSize(), 0);
105 std::vector<bool> nonManifoldInc(m.vertexContainerSize(), false);
106 // First Loop, count how many faces are incident on a vertex and store it in
107 // TD, and flag how many vertices are incident on non manifold edges.
108 for (const FaceType& f : m.faces()) {
109 for (uint i = 0; i < f.vertexNumber(); ++i) {
110 TD[m.index(f.vertex(i))]++;
111 if (!isFaceManifoldOnEdge(f, i)) {
112 nonManifoldInc[m.index(f.vertex(i))] = true;
113 nonManifoldInc[m.index(f.vertexMod(i + 1))] = true;
114 }
115 }
116 }
117
118 std::vector<bool> visited(m.vertexContainerSize(), false);
119 for (const FaceType& f : m.faces()) {
120 for (uint i = 0; i < f.vertexNumber(); ++i) {
121 if (!visited[m.index(f.vertex(i))]) {
122 visited[m.index(f.vertex(i))] = true;
123 MeshPos pos(&f, i);
124 uint starSize = pos.numberOfAdjacentFacesToV();
125 if (starSize != TD[m.index(f.vertex(i))])
126 nonManifoldVertices[m.index(f.vertex(i))] = true;
127 }
128 }
129 }
130
131 return nonManifoldVertices;
132}
133
134template<FaceMeshConcept MeshType>
135uint numberEdges(
136 const MeshType& m,
137 uint& numBoundaryEdges,
138 uint& numNonManifoldEdges)
139{
140 std::vector<ConstMeshEdgeUtil<MeshType>> edgeVec =
141 fillAndSortMeshEdgeUtilVector(m);
142
143 uint numEdges = 0;
144 numBoundaryEdges = 0;
145 numNonManifoldEdges = 0;
146
147 size_t f_on_cur_edge = 1;
148 for (size_t i = 0; i < edgeVec.size(); ++i) {
149 if (((i + 1) == edgeVec.size()) || !(edgeVec[i] == edgeVec[i + 1])) {
150 ++numEdges;
151 if (f_on_cur_edge == 1)
152 ++numBoundaryEdges;
153 if (f_on_cur_edge > 2)
154 ++numNonManifoldEdges;
155 f_on_cur_edge = 1;
156 }
157 else {
158 ++f_on_cur_edge;
159 }
160 }
161 return numEdges;
162}
163
164inline std::list<uint> dummyUintList;
165inline std::list<std::list<std::pair<uint, uint>>> dummyListOfLists;
166inline std::vector<std::pair<uint, uint>> dummyVectorOfPairs;
167
168} // namespace detail
169
183{
184 using MeshType = decltype(mesh);
185
186 uint nRefs = 0;
187
188 if constexpr (TriangleMeshConcept<MeshType>) {
189 return mesh.faceNumber() * 3;
190 }
191 else {
192 for (const auto& f : mesh.faces()) {
193 nRefs += f.vertexNumber();
194 }
195 }
196
197 return nRefs;
198}
199
212uint largestFaceSize(const FaceMeshConcept auto& mesh)
213{
214 using FaceType = RemoveRef<decltype(mesh)>::FaceType;
215
216 uint maxFaceSize = 0;
217
218 if constexpr (FaceType::VERTEX_NUMBER > 0) {
219 return FaceType::VERTEX_NUMBER;
220 }
221 else {
222 for (const auto& f : mesh.faces()) {
223 maxFaceSize = std::max(maxFaceSize, f.vertexNumber());
224 }
225 }
226
227 return maxFaceSize;
228}
229
241{
242 using MeshType = decltype(mesh);
243
244 uint nTris = 0;
245
246 for (const auto& f : mesh.faces()) {
247 nTris += f.vertexNumber() - 2;
248 }
249
250 return nTris;
251}
252
269{
270 requirePerVertexAdjacentVertices(mesh);
271
272 uint maxAVN = 0;
273
274 for (const auto& v : mesh.vertices()) {
275 maxAVN = std::max(maxAVN, v.adjVerticesNumber());
276 }
277
278 return maxAVN;
279}
280
299template<uint ELEM_ID>
301{
303
304 uint maxAFN = 0;
305
306 for (const auto& e : mesh.template elements<ELEM_ID>()) {
307 maxAFN = std::max(maxAFN, e.adjFacesNumber());
308 }
309
310 return maxAFN;
311}
312
331
348 requires EdgeMeshConcept<decltype(mesh)>
349{
351}
352
371template<uint ELEM_ID>
373{
375
376 uint maxAEN = 0;
377
378 for (const auto& e : mesh.template elements<ELEM_ID>()) {
379 maxAEN = std::max(maxAEN, e.adjEdgesNumber());
380 }
381
382 return maxAEN;
383}
384
403
420 requires FaceMeshConcept<decltype(mesh)>
421{
423}
424
443
470template<FaceMeshConcept MeshType>
472 const MeshType& mesh,
473 std::vector<std::pair<uint, uint>>& vertWedgeMap =
474 detail::dummyVectorOfPairs,
475 std::list<uint>& vertsToDuplicate = detail::dummyUintList,
476 std::list<std::list<std::pair<uint, uint>>>& facesToReassign =
477 detail::dummyListOfLists)
478{
481
482 using WedgeTexCoordType = MeshType::FaceType::WedgeTexCoordType;
483 using WedgeTexCoordsInfo = detail::WedgeTexCoordsInfo<WedgeTexCoordType>;
484
485 // list of faces that reference a wedge texcoord, with the index of the
486 // vertex in the face
487 using FaceList = std::list<std::pair<uint, uint>>;
488
489 vertWedgeMap.resize(mesh.vertexContainerSize());
490 vertsToDuplicate.clear();
491 facesToReassign.clear();
492
493 uint count = 0;
494
495 // for each vertex, I'll store a map of WedgeTexCoordsInfo
496 // each element of the map represent a unique wedge texcoord(the texcoord
497 // itself and the index of the texcoord), and for each element it maps a
498 // list of faces that reference the texcoord
499 std::vector<std::map<WedgeTexCoordsInfo, FaceList>> wedges(
500 mesh.vertexContainerSize());
501
502 for (const auto& f : mesh.faces()) {
503 for (uint i = 0; i < f.vertexNumber(); ++i) {
504 uint vi = f.vertexIndex(i);
505
506 // check if the i-th wedge texcoord of the face already exists
507 WedgeTexCoordsInfo wi = {f.wedgeTexCoord(i), f.materialIndex()};
508 auto it = wedges[vi].find(wi);
509 if (it == wedges[vi].end()) { // if it doesn't exist, add it
510 // if there was already a texcoord for the vertex, it means that
511 // the vertex will be duplicated
512 if (!wedges[vi].empty()) {
513 count++;
514 }
515 wedges[vi][wi].emplace_back(f.index(), i);
516 }
517 else {
518 // if it exists, add the face to the list of faces that
519 // reference the texcoord
520 it->second.emplace_back(f.index(), i);
521 }
522 }
523 }
524
525 // for each vertex, check if there are multiple texcoords
526 // note: here we will modify the maps for convenience: at the end of this
527 // loop, the info will be contained in the two lists vertsToDuplicate and
528 // facesToReassign (the wedges vector of map will be inconsistent)
529 for (uint vi = 0; auto& map : wedges) {
530 if (map.size() > 1) {
531 // there are multiple texcoords for the vertex vi, so it will be
532 // duplicated
533
534 // remove from the map the element having the higher number of
535 // faces referencing the texcoord (we do this to reassign the less
536 // number of faces)
537 auto it = std::max_element(
538 map.begin(), map.end(), [](const auto& a, const auto& b) {
539 return a.second.size() < b.second.size();
540 });
541 // store the reference of the texcoord to keep (pair face/vertex
542 // index in the face)
543 vertWedgeMap[vi] = it->second.front();
544 map.erase(it);
545
546 // store the vertex to duplicate, and the faces to reassign
547 for (auto& [wi, fl] : map) {
548 vertsToDuplicate.push_back(vi);
549 facesToReassign.push_back(std::move(fl));
550 }
551 }
552 else {
553 if (map.size() == 1) {
554 // there is only one texcoord for the vertex vi, so store its
555 // reference (pair face/vertex index in the face)
556 vertWedgeMap[vi] = map.begin()->second.front();
557 }
558 else { // the vertex was unreferenced
560 }
561 }
562 ++vi;
563 }
564
565 assert(vertWedgeMap.size() == mesh.vertexContainerSize());
568
569 return count;
570}
571
600template<typename Container, MeshConcept MeshType>
602 const MeshType& mesh,
603 uint& nUnref,
604 bool onlyFaces = false)
605{
606 using VertexType = MeshType::VertexType;
607
608 uint nRefs = 0;
609
610 Container refVertices(mesh.vertexContainerSize(), false);
611
612 if (onlyFaces) {
613 if constexpr (FaceMeshConcept<MeshType>) {
614 using FaceContainer = MeshType::FaceContainer;
615
616 detail::setReferencedVertices(
618 }
619 }
620 else {
621 detail::setReferencedVertices(
622 mesh, refVertices, nRefs, typename MeshType::Containers());
623 }
624
625 nUnref = mesh.vertexNumber() - nRefs;
626
627 return refVertices;
628}
629
648template<MeshConcept MeshType>
649uint numberUnreferencedVertices(const MeshType& m, bool onlyFaces = false)
650{
651 uint nV = 0;
652 // store the number of unref vertices into nV
654
655 return nV;
656}
657
677template<FaceMeshConcept MeshType>
678uint numberNonManifoldVertices(const MeshType& m)
679{
680 std::vector<bool> nonManifoldVertices =
681 detail::nonManifoldVerticesVectorBool(m);
682 return std::count(
683 nonManifoldVertices.begin(), nonManifoldVertices.end(), true);
684}
685
706template<FaceMeshConcept MeshType>
707bool isWaterTight(const MeshType& m)
708{
710 detail::numberEdges(m, numEdgeBorder, numNonManifoldEdges);
711 return numEdgeBorder == 0 && numNonManifoldEdges == 0;
712}
713
733template<FaceMeshConcept MeshType>
734uint numberHoles(const MeshType& m) requires HasPerFaceAdjacentFaces<MeshType>
735{
737
738 using VertexType = MeshType::VertexType;
739 using FaceType = MeshType::FaceType;
740
741 uint loopNum = 0;
742
743 // create a vector of bools to keep track of visited faces.
744 std::vector<bool> visitedFaces(m.faceContainerSize(), false);
745
746 // Traverse the mesh using a depth-first search algorithm to find all the
747 // holes.
748 for (const FaceType& f : m.faces()) {
749 uint e = 0;
750 for (const VertexType* v : f.vertices()) {
751 if (!visitedFaces[m.index(f)] && f.adjFace(e) == nullptr) {
754 do {
755 curPos.nextEdgeOnBorderAdjacentToV();
756 curPos.flipVertex();
757 visitedFaces[m.index(curPos.face())] = true;
758 } while (curPos != startPos);
759 ++loopNum;
760 }
761 ++e;
762 }
763 }
764 return loopNum;
765}
766
790template<FaceMeshConcept MeshType>
791std::vector<std::set<uint>> connectedComponents(const MeshType& m)
793{
795
796 using FaceType = MeshType::FaceType;
797
798 std::vector<std::set<uint>> cc;
799
800 // create a vector of bools to keep track of visited faces.
801 std::vector<bool> visitedFaces(m.faceContainerSize(), false);
802
803 // create a stack to hold the faces that need to be visited during the
804 // depth-first search.
805 std::stack<const FaceType*> sf;
806
807 // traverse the mesh using a depth-first search algorithm to find the
808 // connected components.
809 for (const FaceType& f : m.faces()) {
810 if (!visitedFaces[m.index(f)]) { // first time I see this face
811 visitedFaces[m.index(f)] = true;
812
813 // new connected component
814 cc.emplace_back();
815 std::set<uint>& ccf = cc[cc.size() - 1];
816 ccf.insert(m.index(f));
817
818 // while the stack is empty, visit the adjacent faces of the top
819 // face of the stack
820 sf.push(&f);
821 while (!sf.empty()) {
822 const FaceType* fpt = sf.top();
823 // remove the top face and add it to the connected component
824 sf.pop();
825 ccf.insert(m.index(fpt));
826
827 // add the adjacent faces of the current visited in the stack
828 for (uint j = 0; j < fpt->vertexNumber(); ++j) {
829 const FaceType* adjf = fpt->adjFace(j);
830 // if there is an adj face and it has not been visited
831 if (adjf != nullptr && !visitedFaces[m.index(adjf)]) {
832 sf.push(adjf);
833 visitedFaces[m.index(adjf)] = true;
834 }
835 }
836 }
837 }
838 }
839 return cc;
840}
841
861template<FaceMeshConcept MeshType>
862uint numberConnectedComponents(const MeshType& m)
863{
864 return connectedComponents(m).size();
865}
866
867} // namespace vcl
868
869#endif // VCL_ALGORITHMS_MESH_STAT_TOPOLOGY_H
A class representing a box in N-dimensional space.
Definition box.h:46
PointT size() const
Computes the size of the box.
Definition box.h:267
The EdgeMeshConcept is evaluated true if the type T is a Mesh (it satisfies the vcl::MeshConcept) and...
Definition edge_requirements.h:58
The FaceMeshConcept is evaluated true if the type T is a Mesh (it satisfies the vcl::MeshConcept) and...
Definition face_requirements.h:70
Concept that checks if a Mesh has the per Face AdjacentFaces component.
Definition face_requirements.h:109
A concept that checks whether a class is (inherits from) a Mesh class.
Definition mesh.h:2169
Definition face_requirements.h:73
std::remove_reference_t< T > RemoveRef
Utility alias to get a type type without the reference. e.g. If T is int&, the resulting type is int.
Definition pointers.h:55
constexpr uint UINT_NULL
The UINT_NULL value represent a null value of uint that is the maximum value that can be represented ...
Definition base.h:48
std::vector< std::set< uint > > connectedComponents(const MeshType &m)
Computes the connected components of the input mesh based on its topology.
Definition topology.h:791
uint numberUnreferencedVertices(const MeshType &m, bool onlyFaces=false)
Returns the number of non-deleted unreferenced vertices of the mesh.
Definition topology.h:649
uint numberHoles(const MeshType &m)
Counts the number of holes in the input mesh.
Definition topology.h:734
uint numberNonManifoldVertices(const MeshType &m)
Counts the number of non-manifold vertices in the input mesh.
Definition topology.h:678
Container referencedVertices(const MeshType &mesh, uint &nUnref, bool onlyFaces=false)
Returns a Container of values interpreted as booleans telling, for each vertex of the mesh,...
Definition topology.h:601
bool isWaterTight(const MeshType &m)
Determines whether the input mesh is water tight.
Definition topology.h:707
uint numberConnectedComponents(const MeshType &m)
Computes the number of connected components of the input mesh based on its topology.
Definition topology.h:862
void requirePerFaceWedgeTexCoords(const MeshType &m)
This function asserts that a Mesh has a FaceContainer, the Face has a WedgeTexCoords Component,...
Definition face_requirements.h:1322
void requirePerFaceMaterialIndex(const MeshType &m)
This function asserts that a Mesh has a FaceContainer, the Face has a MaterialIndex Component,...
Definition face_requirements.h:1143
void requirePerFaceAdjacentFaces(const MeshType &m)
This function asserts that a Mesh has a FaceContainer, the Face has a AdjacentFaces Component,...
Definition face_requirements.h:1037
uint largestPerElementAdjacentFacesNumber(const FaceMeshConcept auto &mesh)
Returns the largest number of per-ELEM_ID element adjacent faces in the mesh.
Definition topology.h:300
uint countVerticesToDuplicateByWedgeTexCoords(const MeshType &mesh, std::vector< std::pair< uint, uint > > &vertWedgeMap=detail::dummyVectorOfPairs, std::list< uint > &vertsToDuplicate=detail::dummyUintList, std::list< std::list< std::pair< uint, uint > > > &facesToReassign=detail::dummyListOfLists)
This function counts the number of vertices that must be duplicated in a mesh to have a unique texcoo...
Definition topology.h:471
uint largestPerFaceAdjacentEdgesNumber(const EdgeMeshConcept auto &mesh)
Returns the largest number of per-face adjacent edges in the mesh.
Definition topology.h:419
uint largestPerEdgeAdjacentFacesNumber(const FaceMeshConcept auto &mesh)
Returns the largest number of per-edge adjacent faces in the mesh.
Definition topology.h:347
uint largestPerVertexAdjacentFacesNumber(const FaceMeshConcept auto &mesh)
Returns the largest number of per-vertex adjacent faces in the mesh.
Definition topology.h:327
uint largestPerVertexAdjacentVerticesNumber(const MeshConcept auto &mesh)
Returns the largest number of per-vertex adjacent vertices in the mesh.
Definition topology.h:268
uint countPerFaceVertexReferences(const FaceMeshConcept auto &mesh)
Count the number of references to vertices in the mesh faces.
Definition topology.h:182
uint largestPerVertexAdjacentEdgesNumber(const EdgeMeshConcept auto &mesh)
Returns the largest number of per-vertex adjacent edges in the mesh.
Definition topology.h:399
uint largestFaceSize(const FaceMeshConcept auto &mesh)
Returns the largest face size in the mesh.
Definition topology.h:212
uint countTriangulatedTriangles(const FaceMeshConcept auto &mesh)
Counts the number of resulting triangles if the input mesh would be triangulated by splitting each fa...
Definition topology.h:240
uint largestPerEdgeAdjacentEdgesNumber(const EdgeMeshConcept auto &mesh)
Returns the largest number of per-edge adjacent edges in the mesh.
Definition topology.h:439
uint largestPerElementAdjacentEdgesNumber(const EdgeMeshConcept auto &mesh)
Returns the largest number of per-ELEM_ID element adjacent edges in the mesh.
Definition topology.h:372
constexpr detail::FacesView faces
A view that allows to iterate overt the Face elements of an object.
Definition face.h:84