Skip to content

Commit 5601059

Browse files
authored
duplicateNonManifoldVertices: construct rings from close faces (#6358)
1 parent 715d3fe commit 5601059

2 files changed

Lines changed: 47 additions & 18 deletions

File tree

source/MRMesh/MRMeshBuilder.cpp

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -559,33 +559,34 @@ class PathOverIncidentVert
559559
Triangulation& faceToVertices;
560560
// all iterators in [vertexBegIt, vertexEndIt) must have the same central vertex
561561
std::vector<IncidentVert>::iterator vertexBegIt, vertexEndIt;
562-
size_t lastUnvisitedIndex = 0; // pivot index. [vertexBegIt, vertexBegIt + lastUnvisitedIndex) - unvisited vertices
562+
size_t firstUnvisitedIndex = 0; // pivot index. [vertexBegIt + firstUnvistedIndex, vertexBegIt) - unvisited vertices
563563

564564
public:
565565
PathOverIncidentVert( Triangulation& triangleToVertices,
566566
std::vector<IncidentVert>& incidentItemsVector, size_t beg, size_t end )
567567
: faceToVertices( triangleToVertices )
568568
, vertexBegIt( incidentItemsVector.begin() + beg )
569569
, vertexEndIt( incidentItemsVector.begin() + end )
570-
, lastUnvisitedIndex( end - beg )
571570
{}
572571

573572
// false if there are some unvisited vertices
574573
bool empty() const
575574
{
576-
return lastUnvisitedIndex <= 0;
575+
return vertexBegIt + firstUnvisitedIndex >= vertexEndIt;
577576
}
578577

579578
// first unvisited vertex
580579
VertId getFirstVertex() const
581580
{
581+
assert( !empty() );
582+
const auto first = vertexBegIt + firstUnvisitedIndex;
582583
// below selection ensures that getNextIncidentVertex( getFirstVertex(), true ) will find nextVertex in the very first triangle
583-
const auto & vs = faceToVertices[vertexBegIt->f];
584-
if ( vs[0] == vertexBegIt->srcVert )
584+
const auto & vs = faceToVertices[first->f];
585+
if ( vs[0] == first->srcVert )
585586
return vs[1];
586-
if ( vs[1] == vertexBegIt->srcVert )
587+
if ( vs[1] == first->srcVert )
587588
return vs[2];
588-
if ( vs[2] == vertexBegIt->srcVert )
589+
if ( vs[2] == first->srcVert )
589590
return vs[0];
590591
assert( false );
591592
return {};
@@ -594,11 +595,11 @@ class PathOverIncidentVert
594595
// find incident unvisited vertex, in case of several option prefer finding the vertex not equal to preVertex
595596
VertId getNextIncidentVertex( VertId v, bool triOrientation, VertId prevVertex = {} )
596597
{
597-
if ( lastUnvisitedIndex <= 0 )
598+
if ( empty() )
598599
return VertId( -1 );
599600

600-
auto prevIt = vertexBegIt + lastUnvisitedIndex;
601-
for ( auto it = vertexBegIt; it < vertexBegIt + lastUnvisitedIndex; ++it )
601+
auto prevIt = vertexEndIt;
602+
for ( auto it = vertexBegIt + firstUnvisitedIndex; it < vertexEndIt; ++it )
602603
{
603604
VertId nextVertex;
604605
const auto & vs = faceToVertices[it->f];
@@ -624,19 +625,21 @@ class PathOverIncidentVert
624625
{
625626
if ( nextVertex != prevVertex )
626627
{
627-
--lastUnvisitedIndex;
628-
std::iter_swap( it, vertexBegIt + lastUnvisitedIndex );
628+
if ( it != vertexBegIt + firstUnvisitedIndex )
629+
std::iter_swap( it, vertexBegIt + firstUnvisitedIndex );
630+
++firstUnvisitedIndex;
629631
return nextVertex;
630632
}
631633
// prevVertex is a possible continuation, store it, and search for other options
632634
prevIt = it;
633635
}
634636
}
635-
if ( prevIt < vertexBegIt + lastUnvisitedIndex )
637+
if ( prevIt < vertexEndIt )
636638
{
637639
// the only option is return in prevVertex
638-
--lastUnvisitedIndex;
639-
std::iter_swap( prevIt, vertexBegIt + lastUnvisitedIndex );
640+
if ( prevIt != vertexBegIt + firstUnvisitedIndex )
641+
std::iter_swap( prevIt, vertexBegIt + firstUnvisitedIndex );
642+
++firstUnvisitedIndex;
640643
return prevVertex;
641644
}
642645
return {};
@@ -654,7 +657,7 @@ class PathOverIncidentVert
654657

655658
for ( size_t i = 1; i < path.size(); ++i )
656659
{
657-
for ( auto it = vertexBegIt + lastUnvisitedIndex; it < vertexEndIt; ++it )
660+
for ( auto it = vertexBegIt; it < vertexBegIt + firstUnvisitedIndex; ++it )
658661
{
659662
VertId v1, v2;
660663
bool alreadyDuplicted = true;

source/MRTest/MRMeshBuilderTests.cpp

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ static void testBuildWithDups( const char * objMesh, int numVerts, int numComps
5050

5151
TEST( MRMesh, MeshBuildWithDups )
5252
{
53-
// this test case passed always
53+
// first 4 triangles subdivide a square with center point,
54+
// following 4 triangles subdivide the opposite side of same triangle,
55+
// all 5 points are shared,
56+
// expected that only center vetrex is duplicated and single connected component remains
5457
testBuildWithDups
5558
(
5659
"v 0 0.5 0\n"
@@ -68,7 +71,7 @@ TEST( MRMesh, MeshBuildWithDups )
6871
"f 1 4 5\n", 6, 1
6972
);
7073

71-
// this test start passing only after recent improvements (but will fail in case vertex reordering)
74+
// same situation as above with the order of triangles changed
7275
testBuildWithDups
7376
(
7477
"v 0 0.5 0\n"
@@ -85,6 +88,29 @@ TEST( MRMesh, MeshBuildWithDups )
8588
"f 1 5 4\n"
8689
"f 3 5 1\n", 6, 1
8790
);
91+
92+
// first 4 triangles subdivide a square with center point,
93+
// following 4 triangles subdivide same square with center point,
94+
// 3 points on one diagonal are shared,
95+
// it is divided properly on two components if the rings are computed from the smallest by id next triangle
96+
testBuildWithDups
97+
(
98+
"v -1 0 -1\n"
99+
"v 1 0 -1\n"
100+
"v -1 0 1\n"
101+
"v 1 0 1\n"
102+
"v -1 0 -1\n"
103+
"v 1 0 1\n"
104+
"v 0 0 0\n"
105+
"f 7 1 3\n"
106+
"f 1 7 2\n"
107+
"f 7 3 4\n"
108+
"f 2 7 4\n"
109+
"f 5 7 2\n"
110+
"f 7 3 6\n"
111+
"f 2 7 6\n"
112+
"f 7 5 3\n", 10, 2
113+
);
88114
}
89115

90116
} //namespace MeshBuilder

0 commit comments

Comments
 (0)