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 ushort 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
211uint largestFaceSize(const FaceMeshConcept auto& mesh)
212{
213 using MeshType = decltype(mesh);
214
215 uint maxFaceSize = 0;
216
217 if constexpr (TriangleMeshConcept<MeshType>) {
218 return 3;
219 }
220 else {
221 for (const auto& f : mesh.faces()) {
222 maxFaceSize = std::max(maxFaceSize, f.vertexNumber());
223 }
224 }
225
226 return maxFaceSize;
227}
228
240{
241 using MeshType = decltype(mesh);
242
243 uint nTris = 0;
244
245 for (const auto& f : mesh.faces()) {
246 nTris += f.vertexNumber() - 2;
247 }
248
249 return nTris;
250}
251
278template<FaceMeshConcept MeshType>
280 const MeshType& mesh,
281 std::vector<std::pair<uint, uint>>& vertWedgeMap =
282 detail::dummyVectorOfPairs,
283 std::list<uint>& vertsToDuplicate = detail::dummyUintList,
284 std::list<std::list<std::pair<uint, uint>>>& facesToReassign =
285 detail::dummyListOfLists)
286{
288
289 using WedgeTexCoordType = MeshType::FaceType::WedgeTexCoordType;
290 using WedgeTexCoordsInfo = detail::WedgeTexCoordsInfo<WedgeTexCoordType>;
291
292 // list of faces that reference a wedge texcoord, with the index of the
293 // vertex in the face
294 using FaceList = std::list<std::pair<uint, uint>>;
295
296 vertWedgeMap.resize(mesh.vertexContainerSize());
297 vertsToDuplicate.clear();
298 facesToReassign.clear();
299
300 uint count = 0;
301
302 // for each vertex, I'll store a map of WedgeTexCoordsInfo
303 // each element of the map represent a unique wedge texcoord(the texcoord
304 // itself and the index of the texcoord), and for each element it maps a
305 // list of faces that reference the texcoord
306 std::vector<std::map<WedgeTexCoordsInfo, FaceList>> wedges(
307 mesh.vertexContainerSize());
308
309 for (const auto& f : mesh.faces()) {
310 for (uint i = 0; i < f.vertexNumber(); ++i) {
311 uint vi = f.vertexIndex(i);
312
313 // check if the i-th wedge texcoord of the face already exists
314 WedgeTexCoordsInfo wi = {f.wedgeTexCoord(i), f.textureIndex()};
315 auto it = wedges[vi].find(wi);
316 if (it == wedges[vi].end()) { // if it doesn't exist, add it
317 // if there was already a texcoord for the vertex, it means that
318 // the vertex will be duplicated
319 if (!wedges[vi].empty()) {
320 count++;
321 }
322 wedges[vi][wi].emplace_back(f.index(), i);
323 }
324 else {
325 // if it exists, add the face to the list of faces that
326 // reference the texcoord
327 it->second.emplace_back(f.index(), i);
328 }
329 }
330 }
331
332 // for each vertex, check if there are multiple texcoords
333 // note: here we will modify the maps for convenience: at the end of this
334 // loop, the info will be contained in the two lists vertsToDuplicate and
335 // facesToReassign (the wedges vector of map will be inconsistent)
336 for (uint vi = 0; auto& map : wedges) {
337 if (map.size() > 1) {
338 // there are multiple texcoords for the vertex vi, so it will be
339 // duplicated
340
341 // remove from the map the element having the higher number of
342 // faces referencing the texcoord (we do this to reassign the less
343 // number of faces)
344 auto it = std::max_element(
345 map.begin(), map.end(), [](const auto& a, const auto& b) {
346 return a.second.size() < b.second.size();
347 });
348 // store the reference of the texcoord to keep (pair face/vertex
349 // index in the face)
350 vertWedgeMap[vi] = it->second.front();
351 map.erase(it);
352
353 // store the vertex to duplicate, and the faces to reassign
354 for (auto& [wi, fl] : map) {
355 vertsToDuplicate.push_back(vi);
356 facesToReassign.push_back(std::move(fl));
357 }
358 }
359 else {
360 if (map.size() == 1) {
361 // there is only one texcoord for the vertex vi, so store its
362 // reference (pair face/vertex index in the face)
363 vertWedgeMap[vi] = map.begin()->second.front();
364 }
365 else { // the vertex was unreferenced
367 }
368 }
369 ++vi;
370 }
371
372 assert(vertWedgeMap.size() == mesh.vertexContainerSize());
375
376 return count;
377}
378
407template<typename Container, MeshConcept MeshType>
409 const MeshType& mesh,
410 uint& nUnref,
411 bool onlyFaces = false)
412{
413 using VertexType = MeshType::VertexType;
414
415 uint nRefs = 0;
416
417 Container refVertices(mesh.vertexContainerSize(), false);
418
419 if (onlyFaces) {
420 if constexpr (FaceMeshConcept<MeshType>) {
421 using FaceContainer = MeshType::FaceContainer;
422
423 detail::setReferencedVertices(
425 }
426 }
427 else {
428 detail::setReferencedVertices(
429 mesh, refVertices, nRefs, typename MeshType::Containers());
430 }
431
432 nUnref = mesh.vertexNumber() - nRefs;
433
434 return refVertices;
435}
436
455template<MeshConcept MeshType>
456uint numberUnreferencedVertices(const MeshType& m, bool onlyFaces = false)
457{
458 uint nV = 0;
459 // store the number of unref vertices into nV
461
462 return nV;
463}
464
484template<FaceMeshConcept MeshType>
485uint numberNonManifoldVertices(const MeshType& m)
486{
487 std::vector<bool> nonManifoldVertices =
488 detail::nonManifoldVerticesVectorBool(m);
489 return std::count(
490 nonManifoldVertices.begin(), nonManifoldVertices.end(), true);
491}
492
513template<FaceMeshConcept MeshType>
514bool isWaterTight(const MeshType& m)
515{
517 detail::numberEdges(m, numEdgeBorder, numNonManifoldEdges);
518 return numEdgeBorder == 0 && numNonManifoldEdges == 0;
519}
520
540template<FaceMeshConcept MeshType>
541uint numberHoles(const MeshType& m) requires HasPerFaceAdjacentFaces<MeshType>
542{
544
545 using VertexType = MeshType::VertexType;
546 using FaceType = MeshType::FaceType;
547
548 uint loopNum = 0;
549
550 // create a vector of bools to keep track of visited faces.
551 std::vector<bool> visitedFaces(m.faceContainerSize(), false);
552
553 // Traverse the mesh using a depth-first search algorithm to find all the
554 // holes.
555 for (const FaceType& f : m.faces()) {
556 uint e = 0;
557 for (const VertexType* v : f.vertices()) {
558 if (!visitedFaces[m.index(f)] && f.adjFace(e) == nullptr) {
561 do {
562 curPos.nextEdgeOnBorderAdjacentToV();
563 curPos.flipVertex();
564 visitedFaces[m.index(curPos.face())] = true;
565 } while (curPos != startPos);
566 ++loopNum;
567 }
568 ++e;
569 }
570 }
571 return loopNum;
572}
573
597template<FaceMeshConcept MeshType>
598std::vector<std::set<uint>> connectedComponents(const MeshType& m)
600{
602
603 using FaceType = MeshType::FaceType;
604
605 std::vector<std::set<uint>> cc;
606
607 // create a vector of bools to keep track of visited faces.
608 std::vector<bool> visitedFaces(m.faceContainerSize(), false);
609
610 // create a stack to hold the faces that need to be visited during the
611 // depth-first search.
612 std::stack<const FaceType*> sf;
613
614 // traverse the mesh using a depth-first search algorithm to find the
615 // connected components.
616 for (const FaceType& f : m.faces()) {
617 if (!visitedFaces[m.index(f)]) { // first time I see this face
618 visitedFaces[m.index(f)] = true;
619
620 // new connected component
621 cc.emplace_back();
622 std::set<uint>& ccf = cc[cc.size() - 1];
623 ccf.insert(m.index(f));
624
625 // while the stack is empty, visit the adjacent faces of the top
626 // face of the stack
627 sf.push(&f);
628 while (!sf.empty()) {
629 const FaceType* fpt = sf.top();
630 // remove the top face and add it to the connected component
631 sf.pop();
632 ccf.insert(m.index(fpt));
633
634 // add the adjacent faces of the current visited in the stack
635 for (uint j = 0; j < fpt->vertexNumber(); ++j) {
636 const FaceType* adjf = fpt->adjFace(j);
637 // if there is an adj face and it has not been visited
638 if (adjf != nullptr && !visitedFaces[m.index(adjf)]) {
639 sf.push(adjf);
640 visitedFaces[m.index(adjf)] = true;
641 }
642 }
643 }
644 }
645 }
646 return cc;
647}
648
668template<FaceMeshConcept MeshType>
669uint numberConnectedComponents(const MeshType& m)
670{
671 return connectedComponents(m).size();
672}
673
674} // namespace vcl
675
676#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 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
Definition face_requirements.h:73
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:598
uint numberUnreferencedVertices(const MeshType &m, bool onlyFaces=false)
Returns the number of non-deleted unreferenced vertices of the mesh.
Definition topology.h:456
uint numberHoles(const MeshType &m)
Counts the number of holes in the input mesh.
Definition topology.h:541
uint numberNonManifoldVertices(const MeshType &m)
Counts the number of non-manifold vertices in the input mesh.
Definition topology.h:485
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:408
bool isWaterTight(const MeshType &m)
Determines whether the input mesh is water tight.
Definition topology.h:514
uint numberConnectedComponents(const MeshType &m)
Computes the number of connected components of the input mesh based on its topology.
Definition topology.h:669
void requirePerFaceWedgeTexCoords(const MeshType &m)
This function asserts that a Mesh has a FaceContainer, the Face has a WedgeTexCoords Component,...
Definition face_requirements.h:1209
void requirePerFaceAdjacentFaces(const MeshType &m)
This function asserts that a Mesh has a FaceContainer, the Face has a AdjacentFaces Component,...
Definition face_requirements.h:960
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:279
uint countPerFaceVertexReferences(const FaceMeshConcept auto &mesh)
Count the number of references to vertices in the mesh faces.
Definition topology.h:182
uint largestFaceSize(const FaceMeshConcept auto &mesh)
Returns the largest face size in the mesh.
Definition topology.h:211
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:239
constexpr detail::FacesView faces
A view that allows to iterate overt the Face elements of an object.
Definition face.h:84