Visual Computing Library
Loading...
Searching...
No Matches
clean.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_CLEAN_H
24#define VCL_ALGORITHMS_MESH_CLEAN_H
25
26#include <vclib/algorithms/core/polygon/ear_cut.h>
27#include <vclib/algorithms/mesh/sort.h>
28#include <vclib/algorithms/mesh/stat/topology.h>
29#include <vclib/mesh/requirements.h>
30#include <vclib/space/complex/mesh_pos.h>
31
32#include <set>
33#include <stack>
34#include <vector>
35
47namespace vcl {
48
49namespace detail {
50
51/* classe di confronto per l'algoritmo di eliminazione vertici duplicati*/
52template<typename VertexPointer>
53class VertPositionComparator
54{
55public:
56 inline bool operator()(const VertexPointer& a, const VertexPointer& b)
57 {
58 return (a->coord() == b->coord()) ? (a < b) : (a->coord() < b->coord());
59 }
60};
61
74template<typename IndexType, typename SentinelType, int N>
75class SortedIndexContainer
76{
77public:
78 SortedIndexContainer() {}
79
80 template<Range RangeType>
81 SortedIndexContainer(SentinelType s, RangeType rng) : s(s), v(rng)
82 {
83 std::sort(v.begin(), v.end());
84 }
85
86 bool operator<(const SortedIndexContainer& s) const
87 {
88 if constexpr (N >= 0) {
89 for (uint i = 0; i < N; ++i) {
90 if (v[i] != s.v[i])
91 return v[i] < s.v[i];
92 }
93 return false;
94 }
95 else {
96 for (uint i = 0; i < v.size() && i < s.v.size(); ++i) {
97 if (v[i] != s.v[i])
98 return v[i] < s.v[i];
99 }
100 return v.size() < s.v.size();
101 }
102 }
103
104 bool operator==(const SortedIndexContainer& s) const
105 {
106 if constexpr (N >= 0) {
107 for (uint i = 0; i < N; ++i) {
108 if (v[i] != s.v[i])
109 return false;
110 }
111 return true;
112 }
113 else {
114 if (v.size() != s.v.size())
115 return false;
116 for (uint i = 0; i < v.size(); ++i) {
117 if (v[i] != s.v[i])
118 return false;
119 }
120 return true;
121 }
122 }
123
124 SentinelType sentinel() const { return s; }
125
126private:
127 Vector<IndexType, N> v;
128 SentinelType s;
129};
130
131template<FaceMeshConcept MeshType>
132std::vector<bool> nonManifoldVerticesVectorBool(const MeshType& m)
133 requires HasPerFaceAdjacentFaces<MeshType>
134{
136
137 using FaceType = MeshType::FaceType;
138
139 std::vector<bool> nonManifoldVertices(m.vertexContainerSize(), false);
140
141 std::vector<uint> TD(m.vertexContainerSize(), 0);
142 std::vector<bool> nonManifoldInc(m.vertexContainerSize(), false);
143 // First Loop, count how many faces are incident on a vertex and store it in
144 // TD, and flag how many vertices are incident on non manifold edges.
145 for (const FaceType& f : m.faces()) {
146 for (uint i = 0; i < f.vertexNumber(); ++i) {
147 TD[m.index(f.vertex(i))]++;
148 if (!isFaceManifoldOnEdge(f, i)) {
149 nonManifoldInc[m.index(f.vertex(i))] = true;
150 nonManifoldInc[m.index(f.vertexMod(i + 1))] = true;
151 }
152 }
153 }
154
155 std::vector<bool> visited(m.vertexContainerSize(), false);
156 for (const FaceType& f : m.faces()) {
157 for (uint i = 0; i < f.vertexNumber(); ++i) {
158 if (!visited[m.index(f.vertex(i))]) {
159 visited[m.index(f.vertex(i))] = true;
160 MeshPos pos(&f, i);
161 uint starSize = pos.numberOfAdjacentFacesToV();
162 if (starSize != TD[m.index(f.vertex(i))])
163 nonManifoldVertices[m.index(f.vertex(i))] = true;
164 }
165 }
166 }
167
168 return nonManifoldVertices;
169}
170
171template<FaceMeshConcept MeshType>
172uint numberEdges(
173 const MeshType& m,
174 uint& numBoundaryEdges,
175 uint& numNonManifoldEdges)
176{
177 std::vector<ConstMeshEdgeUtil<MeshType>> edgeVec =
178 fillAndSortMeshEdgeUtilVector(m);
179
180 uint numEdges = 0;
181 numBoundaryEdges = 0;
182 numNonManifoldEdges = 0;
183
184 size_t f_on_cur_edge = 1;
185 for (size_t i = 0; i < edgeVec.size(); ++i) {
186 if (((i + 1) == edgeVec.size()) || !(edgeVec[i] == edgeVec[i + 1])) {
187 ++numEdges;
188 if (f_on_cur_edge == 1)
189 ++numBoundaryEdges;
190 if (f_on_cur_edge > 2)
191 ++numNonManifoldEdges;
192 f_on_cur_edge = 1;
193 }
194 else {
195 ++f_on_cur_edge;
196 }
197 }
198 return numEdges;
199}
200
201} // namespace detail
202
218template<MeshConcept MeshType>
219uint numberUnreferencedVertices(const MeshType& m)
220{
221 uint nV = 0;
222 // store the number of unref vertices into nV
224
225 return nV;
226}
227
246template<MeshConcept MeshType>
248{
249 using VertexType = MeshType::VertexType;
250
251 // Generate a vector of boolean flags indicating whether each vertex is
252 // referenced by any of the mesh's elements.
253
254 uint n = 0;
256
257 // need to mark as deleted vertices only if the number of unreferenced is
258 // less than vn
259 if (n < m.vertexNumber()) {
260 // will store on this vector only the indices of the referenced vertices
261 std::vector<uint> refVertIndices(m.vertexContainerSize(), UINT_NULL);
262 // Iterate over all vertices in the mesh, and mark any unreferenced
263 // vertex as deleted.
264 for (const VertexType& v : m.vertices()) {
265 if (!refVertices[m.index(v)]) {
266 m.deleteVertex(m.index(v));
267 }
268 else {
269 refVertIndices[m.index(v)] = m.index(v);
270 }
271 }
272
273 // update the vertex indices of the mesh, setting to null the indices of
274 // the unreferenced vertices (it may happen on adjacent vertices of some
275 // container).
276 m.updateVertexIndices(refVertIndices);
277 }
278
279 return n;
280}
281
300template<MeshConcept MeshType>
302{
303 using VertexType = MeshType::VertexType;
304 using VertexPointer = MeshType::VertexType*;
305
306 if (m.vertexNumber() == 0)
307 return 0;
308
309 // a map that will be used to keep track of deleted vertices and their
310 // corresponding pointers.
311 std::vector<uint> newVertexIndices(m.vertexNumber());
312 // assigning each vertex index to itself.
313 std::iota(newVertexIndices.begin(), newVertexIndices.end(), 0);
314
315 uint deleted = 0;
316
317 std::vector<VertexPointer> perm(m.vertexNumber());
318
319 // put all the vertices into a vector for sorting.
320 uint k = 0;
321 for (VertexType& v : m.vertices())
322 perm[k++] = &v;
323
324 // sort the vector based on the vertices' spatial positions.
325 std::sort(
326 std::execution::par_unseq,
327 perm.begin(),
328 perm.end(),
329 detail::VertPositionComparator<VertexPointer>());
330
331 uint i = 0;
332
333 // compare the i-th position with the next ones while they are equal to the
334 // i-th.
335 while (i < perm.size() - 1) {
336 uint j = i + 1;
337 while (j < perm.size() && perm[i]->coord() == perm[j]->coord()) {
338 // j will be deleted, so we map its pointer to the i-th vertex's
339 // pointer.
340 newVertexIndices[m.index(perm[j])] = m.index(perm[i]); // map j -> i
341 m.deleteVertex(m.index(perm[j]));
342 j++;
343 deleted++;
344 }
345 // here perm[i] != perm[j], so we need to check perm[j] with the next
346 // vertex.
347 i = j;
348 }
349
350 // update the vertex pointers to point to the correct vertices, in every
351 // container of the mesh
352 m.updateVertexIndices(newVertexIndices);
353
354 // todo:
355 // - add a flag that removes degenerate elements after
356 return deleted;
357}
358
385template<FaceMeshConcept MeshType>
387{
388 using VertexType = MeshType::VertexType;
389 using FaceType = MeshType::FaceType;
390
391 // create a vector of sorted tuples of indices, where each tuple represents
392 // a face's vertices and a pointer to the face.
393 std::vector<detail::SortedIndexContainer<
394 VertexType*,
395 FaceType*,
396 FaceType::VERTEX_NUMBER>>
397 fvec;
398
399 for (FaceType& f : m.faces()) {
400 fvec.emplace_back(&f, f.vertices());
401 }
402
403 // sort the vector based on the face vertex indices.
404 std::sort(std::execution::par_unseq, fvec.begin(), fvec.end());
405 uint total = 0;
406
407 // iterate over the sorted vector, and mark any duplicate faces as deleted.
408 for (uint i = 0; i < fvec.size() - 1; ++i) {
409 if (fvec[i] == fvec[i + 1]) {
410 total++;
411 m.deleteFace(fvec[i].sentinel());
412 }
413 }
414 return total;
415}
416
440template<MeshConcept MeshType>
442{
443 using VertexType = MeshType::VertexType;
444
445 int count_vd = 0;
446
447 // iterate over all vertices in the mesh, and mark any with invalid floating
448 // point values as deleted.
449 for (VertexType& v : m.vertices()) {
450 if (v.coord().isDegenerate()) {
451 count_vd++;
452 m.deleteVertex(&v);
453 }
454 }
455
456 // If the mesh has faces and the `deleteAlsoFaces` flag is true, delete all
457 // faces incident on deleted vertices.
458 if constexpr (HasFaces<MeshType>) {
459 using FaceType = MeshType::FaceType;
460 if (deleteAlsoFaces) {
461 for (FaceType& f : m.faces()) {
462 bool deg = false;
463 for (VertexType* v : f.vertices()) {
464 if (v->deleted()) {
465 deg = true;
466 }
467 }
468 if (deg) {
469 m.deleteFace(&f);
470 }
471 }
472 }
473 }
474
475 return count_vd;
476}
477
500template<FaceMeshConcept MeshType>
502{
503 uint count = 0;
504 using FaceType = MeshType::FaceType;
505
506 // iterate over all faces in the mesh, and mark any that are degenerate as
507 // deleted.
508 for (FaceType& f : m.faces()) {
509 bool deg = false; // flag to check if a face is degenerate
510 for (uint i = 0; i < f.vertexNumber() && !deg; ++i) {
511 if (f.vertex(i) == f.vertexMod(i + 1)) {
512 deg = true;
513 m.deleteFace(m.index(f));
514 count++;
515 }
516 }
517 }
518 return count;
519}
520
539template<FaceMeshConcept MeshType>
540uint numberNonManifoldVertices(const MeshType& m)
541{
542 std::vector<bool> nonManifoldVertices =
543 detail::nonManifoldVerticesVectorBool(m);
544 return std::count(
545 nonManifoldVertices.begin(), nonManifoldVertices.end(), true);
546}
547
567template<FaceMeshConcept MeshType>
568bool isWaterTight(const MeshType& m)
569{
571 detail::numberEdges(m, numEdgeBorder, numNonManifoldEdges);
572 return numEdgeBorder == 0 && numNonManifoldEdges == 0;
573}
574
593template<FaceMeshConcept MeshType>
594uint numberHoles(const MeshType& m) requires HasPerFaceAdjacentFaces<MeshType>
595{
597
598 using VertexType = MeshType::VertexType;
599 using FaceType = MeshType::FaceType;
600
601 uint loopNum = 0;
602
603 // create a vector of bools to keep track of visited faces.
604 std::vector<bool> visitedFaces(m.faceContainerSize(), false);
605
606 // Traverse the mesh using a depth-first search algorithm to find all the
607 // holes.
608 for (const FaceType& f : m.faces()) {
609 uint e = 0;
610 for (const VertexType* v : f.vertices()) {
611 if (!visitedFaces[m.index(f)] && f.adjFace(e) == nullptr) {
614 do {
615 curPos.nextEdgeOnBorderAdjacentToV();
616 curPos.flipVertex();
617 visitedFaces[m.index(curPos.face())] = true;
618 } while (curPos != startPos);
619 ++loopNum;
620 }
621 ++e;
622 }
623 }
624 return loopNum;
625}
626
649template<FaceMeshConcept MeshType>
650std::vector<std::set<uint>> connectedComponents(const MeshType& m)
652{
654
655 using FaceType = MeshType::FaceType;
656
657 std::vector<std::set<uint>> cc;
658
659 // create a vector of bools to keep track of visited faces.
660 std::vector<bool> visitedFaces(m.faceContainerSize(), false);
661
662 // create a stack to hold the faces that need to be visited during the
663 // depth-first search.
664 std::stack<const FaceType*> sf;
665
666 // traverse the mesh using a depth-first search algorithm to find the
667 // connected components.
668 for (const FaceType& f : m.faces()) {
669 if (!visitedFaces[m.index(f)]) { // first time I see this face
670 visitedFaces[m.index(f)] = true;
671
672 // new connected component
673 cc.emplace_back();
674 std::set<uint>& ccf = cc[cc.size() - 1];
675 ccf.insert(m.index(f));
676
677 // while the stack is empty, visit the adjacent faces of the top
678 // face of the stack
679 sf.push(&f);
680 while (!sf.empty()) {
681 const FaceType* fpt = sf.top();
682 // remove the top face and add it to the connected component
683 sf.pop();
684 ccf.insert(m.index(fpt));
685
686 // add the adjacent faces of the current visited in the stack
687 for (uint j = 0; j < fpt->vertexNumber(); ++j) {
688 const FaceType* adjf = fpt->adjFace(j);
689 // if there is an adj face and it has not been visited
690 if (adjf != nullptr && !visitedFaces[m.index(adjf)]) {
691 sf.push(adjf);
692 visitedFaces[m.index(adjf)] = true;
693 }
694 }
695 }
696 }
697 }
698 return cc;
699}
700
719template<FaceMeshConcept MeshType>
720uint numberConnectedComponents(const MeshType& m)
721{
722 return connectedComponents(m).size();
723}
724
725} // namespace vcl
726
727#endif // VCL_ALGORITHMS_MESH_CLEAN_H
A class representing a line segment in n-dimensional space. The class is parameterized by a PointConc...
Definition segment.h:43
HasFaces concepts is satisfied when at least one of its template types is (or inherits from) a vcl::m...
Definition face_container.h:133
Concept that checks if a Mesh has the per Face AdjacentFaces component.
Definition per_face.h:79
std::vector< std::set< uint > > connectedComponents(const MeshType &m)
Computes the connected components of the input mesh based on its topology.
Definition clean.h:650
uint removeUnreferencedVertices(MeshType &m)
Marks as deleted all the non-deleted unreferenced vertices of the mesh.
Definition clean.h:247
uint removeDegeneratedVertices(MeshType &m, bool deleteAlsoFaces)
Removes all vertices that have coordinates with invalid floating point values (NaN or inf).
Definition clean.h:441
uint removeDegenerateFaces(MeshType &m)
Removes all degenerate faces from the input mesh.
Definition clean.h:501
uint removeDuplicatedFaces(MeshType &m)
Removes all duplicate faces of the mesh by looking only at their vertex references.
Definition clean.h:386
uint numberHoles(const MeshType &m)
Counts the number of holes in the input mesh.
Definition clean.h:594
uint removeDuplicatedVertices(MeshType &m)
Marks as deleted the duplicate vertices of the mesh, by looking only at their spatial positions.
Definition clean.h:301
uint numberNonManifoldVertices(const MeshType &m)
Counts the number of non-manifold vertices in the input mesh.
Definition clean.h:540
bool isWaterTight(const MeshType &m)
Determines whether the input mesh is water tight.
Definition clean.h:568
uint numberUnreferencedVertices(const MeshType &m)
Returns the number of non-deleted unreferenced vertices of the mesh.
Definition clean.h:219
uint numberConnectedComponents(const MeshType &m)
Computes the number of connected components of the input mesh based on its topology.
Definition clean.h:720
void requirePerFaceAdjacentFaces(const MeshType &m)
This function asserts that a Mesh has a FaceContainer, the Face has a AdjacentFaces Component,...
Definition face_requirements.h:698
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
constexpr detail::FacesView faces
A view that allows to iterate overt the Face elements of an object.
Definition face.h:52