From 6ffa432c4e59aa430a9cb43c71df6a621cae0c17 Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sat, 25 Oct 2025 14:55:04 +0300 Subject: [PATCH 01/23] left0 --- source/MRMesh/MRMeshTopology.cpp | 90 +++++++++++++++++--------------- source/MRMesh/MRMeshTopology.h | 15 +++--- 2 files changed, 57 insertions(+), 48 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index 47ed6a52dcce..661c018b0424 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -78,12 +78,12 @@ bool MeshTopology::isLoneEdge( EdgeId a ) const if ( a >= edges_.size() ) return true; auto & adata = edges_[a]; - if ( adata.left.valid() || adata.org.valid() || adata.next != a || adata.prev != a ) + if ( left_[a].valid() || adata.org.valid() || adata.next != a || adata.prev != a ) return false; auto b = a.sym(); auto & bdata = edges_[b]; - if ( bdata.left.valid() || bdata.org.valid() || bdata.next != b || bdata.prev != b ) + if ( left_[b].valid() || bdata.org.valid() || bdata.next != b || bdata.prev != b ) return false; return true; @@ -163,17 +163,19 @@ void MeshTopology::splice( EdgeId a, EdgeId b ) assert( a.valid() && b.valid() ); if ( a == b ) return; + const auto aNext = next( a ); + const auto bNext = next( b ); auto & aData = edges_[a]; - auto & aNextData = edges_[next( a )]; + auto & aNextData = edges_[aNext]; auto & bData = edges_[b]; - auto & bNextData = edges_[next( b )]; + auto & bNextData = edges_[bNext]; bool wasSameOriginId = aData.org == bData.org; assert( wasSameOriginId || !aData.org.valid() || !bData.org.valid() ); - bool wasSameLeftId = aData.left == bData.left; - assert( wasSameLeftId || !aData.left.valid() || !bData.left.valid() ); + bool wasSameLeftId = left_[a] == left_[b]; + assert( wasSameLeftId || !left_[a].valid() || !left_[b].valid() ); if ( !wasSameOriginId ) { @@ -185,10 +187,10 @@ void MeshTopology::splice( EdgeId a, EdgeId b ) if ( !wasSameLeftId ) { - if ( aData.left.valid() ) - setLeft_( b, aData.left ); - else if ( bData.left.valid() ) - setLeft_( a, bData.left ); + if ( left_[a].valid() ) + setLeft_( b, left_[a] ); + else if ( left_[b].valid() ) + setLeft_( a, left_[b] ); } std::swap( aData.next, bData.next ); @@ -201,11 +203,11 @@ void MeshTopology::splice( EdgeId a, EdgeId b ) edgePerVertex_[aData.org] = a; } - if ( wasSameLeftId && bData.left.valid() ) + if ( wasSameLeftId && left_[b].valid() ) { setLeft_( b, FaceId() ); - if ( !fromSameLeftRing( edgePerFace_[aData.left], a ) ) - edgePerFace_[aData.left] = a; + if ( !fromSameLeftRing( edgePerFace_[left_[a]], a ) ) + edgePerFace_[left_[a]] = a; } } @@ -496,9 +498,7 @@ void MeshTopology::setLeft_( EdgeId a, FaceId f ) { assert( a.valid() ); for ( EdgeId i : leftRing( *this, a ) ) - { - edges_[i].left = f; - } + left_[i] = f; } void MeshTopology::setLeft( EdgeId a, FaceId f ) @@ -959,7 +959,7 @@ void MeshTopology::deleteFaces( const FaceBitSet & fs, const UndirectedEdgeBitSe } template -void MeshTopology::translateNoFlip_( HalfEdgeRecord & r, const FM & fmap, const VM & vmap, const WEM & emap ) const +void MeshTopology::translateNoFlip_( HalfEdgeRecord & r, FaceId & left, const FM & fmap, const VM & vmap, const WEM & emap ) const { for ( auto n = r.next; ; n = next( n ) ) { @@ -976,22 +976,22 @@ void MeshTopology::translateNoFlip_( HalfEdgeRecord & r, const FM & fmap, const if ( r.org.valid() ) r.org = getAt( vmap, r.org ); - if ( r.left.valid() ) - r.left = getAt( fmap, r.left ); + if ( left.valid() ) + left = getAt( fmap, left ); } template -void MeshTopology::translate_( HalfEdgeRecord & r, HalfEdgeRecord & rsym, +void MeshTopology::translate_( HalfEdgeRecord & r, FaceId & left, HalfEdgeRecord & rsym, FaceId & symLeft, const FM & fmap, const VM & vmap, const WEM & emap, bool flipOrientation ) const { - translateNoFlip_( r, fmap, vmap, emap ); - translateNoFlip_( rsym, fmap, vmap, emap ); + translateNoFlip_( r, left, fmap, vmap, emap ); + translateNoFlip_( rsym, symLeft, fmap, vmap, emap ); if ( flipOrientation ) { std::swap( r.prev, r.next ); std::swap( rsym.prev, rsym.next ); - std::swap( r.left, rsym.left ); + std::swap( left, symLeft ); } } @@ -1183,7 +1183,7 @@ void MeshTopology::flipOrientation( const UndirectedEdgeBitSet * fullComponents auto & r1 = edges_[i + 1]; std::swap( r1.next, r1.prev ); - std::swap( r0.left, r1.left ); + std::swap( left_[i], left_[i + 1] ); } ); } @@ -1347,7 +1347,7 @@ void MeshTopology::addPart( const MeshTopology & from, const PartMapping & map, for ( UndirectedEdgeId ue = range.begin(); ue < range.end(); ++ue ) { EdgeId e{ ue }; - from.translate_( edges_[e], edges_[e.sym()], fmap, vmap, emap, false ); + from.translate_( edges_[e], left_[e], edges_[e.sym()], left_[e.sym()], fmap, vmap, emap, false ); } } ); @@ -1451,11 +1451,12 @@ void MeshTopology::addPackedPart( const MeshTopology & from, EdgeId toEdgeId, co assert ( !from.isLoneEdge( i ) ); const HalfEdgeRecord & fromEdge = from.edges_[i]; - HalfEdgeRecord & to = edges_[emap( i )]; + const auto j = emap( i ); + HalfEdgeRecord & to = edges_[j]; to.next = emap( fromEdge.next ); to.prev = emap( fromEdge.prev ); to.org = fromEdge.org.valid() ? vmap[fromEdge.org] : VertId{}; - to.left = fromEdge.left.valid() ? fmap[fromEdge.left] : FaceId{}; + left_[j] = from.left_[i].valid() ? fmap[from.left_[i]] : FaceId{}; } } @@ -1622,8 +1623,9 @@ bool MeshTopology::buildGridMesh( const GridSettings & settings, ProgressCallbac he.next = i + 1 < edgeRing.size() ? edgeRing[i + 1].e : edgeRing[0].e; he.prev = i > 0 ? edgeRing[i - 1].e : edgeRing.back().e; he.org = v; - he.left = edgeRing[i].f; - edges_[edgeRing[i].e] = he; + const auto e = edgeRing[i].e; + edges_[e] = he; + left_[e] = edgeRing[i].f; } } }, subprogress( cb, 0.0f, 0.5f ) ); @@ -1696,12 +1698,12 @@ void MeshTopology::computeAllFromEdges_() MR_TIMER; VertId maxValidVert; - FaceId maxValidFace; for( const auto & he : edges_ ) - { maxValidVert = std::max( maxValidVert, he.org ); - maxValidFace = std::max( maxValidFace, he.left ); - } + + FaceId maxValidFace; + for ( const auto& f : left_ ) + maxValidFace = std::max( maxValidFace, f ); edgePerVertex_.clear(); edgePerVertex_.resize( maxValidVert + 1 ); @@ -1726,11 +1728,11 @@ void MeshTopology::computeAllFromEdges_() ++numValidVerts_; } } - if ( he.left.valid() ) + if ( left_[e].valid() ) { - if ( !validFaces_.test_set( he.left ) ) + if ( !validFaces_.test_set( left_[e] ) ) { - edgePerFace_[he.left] = e; + edgePerFace_[left_[e]] = e; ++numValidFaces_; } } @@ -1961,8 +1963,8 @@ void MeshTopology::addPartByMask( const MeshTopology & from, const FaceBitSet * const HalfEdgeRecord & fromHe = from.edges_[e]; toHe.next = mapEdge( emap, flipOrientation ? fromHe.prev : fromHe.next ); assert( toHe.next ); - if ( auto left = flipOrientation ? from.edges_[e.sym()].left : fromHe.left ) - toHe.left = getAt( fmap, left ); + if ( auto left = flipOrientation ? from.left_[e.sym()] : from.left_[e] ) + left_[e1] = getAt( fmap, left ); } { HalfEdgeRecord & toHe = edges_[e1.sym()]; @@ -1977,14 +1979,20 @@ void MeshTopology::addPartByMask( const MeshTopology & from, const FaceBitSet * edges_.resizeNoInit( nextNewEdge ); BitSetParallelFor( fromCopiedEdges, [&]( UndirectedEdgeId fromUe ) { - auto e0 = from.edges_[EdgeId{ fromUe }]; - auto e1 = from.edges_[EdgeId{ fromUe }.sym()]; - from.translate_( e0, e1, fmap, vmap, emap, flipOrientation ); + const EdgeId fromE( fromUe ); + const EdgeId fromSymE( fromE.sym() ); + auto e0 = from.edges_[fromE]; + auto l0 = from.left_[fromE]; + auto e1 = from.edges_[fromSymE]; + auto l1 = from.left_[fromSymE]; + from.translate_( e0, l0, e1, l1, fmap, vmap, emap, flipOrientation ); const UndirectedEdgeId nue = getAt( emap, fromUe ); const EdgeId ne{ nue }; edges_[ne] = e0; + left_[ne] = l0; edges_[ne.sym()] = e1; + left_[ne.sym()] = l1; } ); // translate vertex records diff --git a/source/MRMesh/MRMeshTopology.h b/source/MRMesh/MRMeshTopology.h index e881b3e11030..12a0e9b54a50 100644 --- a/source/MRMesh/MRMeshTopology.h +++ b/source/MRMesh/MRMeshTopology.h @@ -93,10 +93,10 @@ class MeshTopology [[nodiscard]] VertId dest( EdgeId he ) const { assert(he.valid()); return edges_[he.sym()].org; } /// returns left face of half-edge - [[nodiscard]] FaceId left( EdgeId he ) const { assert(he.valid()); return edges_[he].left; } + [[nodiscard]] FaceId left( EdgeId he ) const { assert(he.valid()); return left_[he]; } /// returns right face of half-edge - [[nodiscard]] FaceId right( EdgeId he ) const { assert(he.valid()); return edges_[he.sym()].left; } + [[nodiscard]] FaceId right( EdgeId he ) const { assert(he.valid()); return left_[he.sym()]; } /// sets new origin to the full origin ring including this edge; @@ -551,25 +551,26 @@ class MeshTopology EdgeId next; ///< next counter clock wise half-edge in the origin ring EdgeId prev; ///< next clock wise half-edge in the origin ring VertId org; ///< vertex at the origin of the edge - FaceId left; ///< face at the left of the edge bool operator ==( const HalfEdgeRecord& b ) const { - return next == b.next && prev == b.prev && org == b.org && left == b.left; + return next == b.next && prev == b.prev && org == b.org; } HalfEdgeRecord() noexcept = default; - explicit HalfEdgeRecord( NoInit ) noexcept : next( noInit ), prev( noInit ), org( noInit ), left( noInit ) {} + explicit HalfEdgeRecord( NoInit ) noexcept : next( noInit ), prev( noInit ), org( noInit ) {} }; /// translates all fields in the record for this edge given maps template - void translateNoFlip_( HalfEdgeRecord & r, const FM & fmap, const VM & vmap, const WEM & emap ) const; + void translateNoFlip_( HalfEdgeRecord & r, FaceId & left, const FM & fmap, const VM & vmap, const WEM & emap ) const; template - void translate_( HalfEdgeRecord & r, HalfEdgeRecord & rsym, + void translate_( HalfEdgeRecord & r, FaceId & left, HalfEdgeRecord & rsym, FaceId & symLeft, const FM & fmap, const VM & vmap, const WEM & emap, bool flipOrientation ) const; /// edges_: EdgeId -> edge data Vector edges_; + Vector left_; ///< left_[e] - face at the left of the edge (e) + /// edgePerVertex_: VertId -> one edge id of one of edges with origin there Vector edgePerVertex_; VertBitSet validVerts_; ///< each true bit here corresponds to valid element in edgePerVertex_ From 44e3b80a1b9f54bdb6eb8156f1207a70ef2d8886 Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sat, 25 Oct 2025 15:14:29 +0300 Subject: [PATCH 02/23] left pack --- source/MRMesh/MRMeshTopology.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index 661c018b0424..1a957962950e 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -2118,6 +2118,24 @@ void MeshTopology::pack( const PackMapping & map ) { MR_TIMER; + // translate left faces + { + Timer t( "left" ); + Vector tmpLeft; + tmpLeft.resizeNoInit( map.e.tsize ); + ParallelFor( 0_ue, UndirectedEdgeId( undirectedEdgeSize() ), [&] ( UndirectedEdgeId oldUe ) + { + UndirectedEdgeId newUe = map.e.b[oldUe]; + if ( !newUe ) + return; + EdgeId oldE = oldUe; + EdgeId newE = newUe; + tmpLeft[newE] = getAt( map.f.b, left_[oldE] ); + tmpLeft[newE.sym()] = getAt( map.f.b, left_[oldE.sym()] ); + } ); + left_ = std::move( tmpLeft ); + } + Vector, UndirectedEdgeId> tmp( map.e.tsize ); auto translateHalfEdge = [&]( const HalfEdgeRecord & he ) { @@ -2125,7 +2143,6 @@ void MeshTopology::pack( const PackMapping & map ) res.next = getAt( map.e.b, he.next ); res.prev = getAt( map.e.b, he.prev ); res.org = getAt( map.v.b, he.org ); - res.left = getAt( map.f.b, he.left ); return res; }; From 77b0a9666e04a5a05a829aab04a9b1ff31d12498 Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sat, 25 Oct 2025 15:38:49 +0300 Subject: [PATCH 03/23] more --- source/MRMesh/MRMeshTopology.cpp | 86 ++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 10 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index 1a957962950e..34468bc4b405 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -1404,6 +1404,7 @@ void MeshTopology::resizeBeforeParallelAdd( size_t edgeSize, size_t vertSize, si updateValids_ = false; edges_.resizeNoInit( edgeSize ); + left_.resizeNoInit( edgeSize ); edgePerVertex_.resize( vertSize ); validVerts_.resize( vertSize ); @@ -1510,6 +1511,7 @@ bool MeshTopology::buildGridMesh( const GridSettings & settings, ProgressCallbac edgePerVertex_.resizeNoInit( settings.vertIds.tsize ); edgePerFace_.resizeNoInit( settings.faceIds.tsize ); edges_.resizeNoInit( 2 * settings.uedgeIds.tsize ); + left_.resizeNoInit( 2 * settings.uedgeIds.tsize ); auto getVertId = [&]( Vector2i v ) -> VertId { @@ -1977,6 +1979,7 @@ void MeshTopology::addPartByMask( const MeshTopology & from, const FaceBitSet * // translate edge records edges_.resizeNoInit( nextNewEdge ); + left_.resizeNoInit( nextNewEdge ); BitSetParallelFor( fromCopiedEdges, [&]( UndirectedEdgeId fromUe ) { const EdgeId fromE( fromUe ); @@ -2305,10 +2308,33 @@ void MeshTopology::packMinMem( const PackMapping & map ) validVerts_.resize( numValidVerts_, true ); } ); + struct EdgeRecord + { + HalfEdgeRecord he0; + FaceId l0; + HalfEdgeRecord he1; + FaceId l1; + }; + shuffle( map.e, - [&]( UndirectedEdgeId ue ) { return std::make_pair( edges_[ EdgeId{ue} ], edges_[ EdgeId{ue}.sym() ] ); }, - [&]( UndirectedEdgeId ue, const auto & val ) { edges_[ EdgeId{ue} ] = val.first; edges_[ EdgeId{ue}.sym() ] = val.second; } ); + [&]( UndirectedEdgeId ue ) + { + const EdgeId e0( ue ); + const EdgeId e1( e0.sym() ); + return EdgeRecord{ edges_[e0], left_[e0], edges_[e1], left_[e1] }; + }, + [&]( UndirectedEdgeId ue, const EdgeRecord& r ) + { + const EdgeId e0( ue ); + const EdgeId e1( e0.sym() ); + edges_[e0] = r.he0; + left_[e0] = r.l0; + edges_[e1] = r.he1; + left_[e1] = r.l1; + } + ); edges_.resize( 2 * map.e.tsize ); + left_.resize( 2 * map.e.tsize ); group.wait(); @@ -2323,10 +2349,13 @@ void MeshTopology::packMinMem( const PackMapping & map ) he.next = getAt( map.e.b, he.next ); he.prev = getAt( map.e.b, he.prev ); he.org = getAt( map.v.b, he.org ); - he.left = getAt( map.f.b, he.left ); }; - translateHalfEdge( edges_[ EdgeId{ue} ] ); - translateHalfEdge( edges_[ EdgeId{ue}.sym() ] ); + const EdgeId e0( ue ); + const EdgeId e1( e0.sym() ); + translateHalfEdge( edges_[e0] ); + translateHalfEdge( edges_[e1] ); + left_[e0] = getAt( map.f.b, left_[e0] ); + left_[e1] = getAt( map.f.b, left_[e1] ); } } ); @@ -2351,12 +2380,37 @@ void MeshTopology::packMinMem( const PackMapping & map ) updateValids_ = true; } +struct SerializedHalfEdgeRecord +{ + EdgeId next; + EdgeId prev; + VertId org; + FaceId left; + + SerializedHalfEdgeRecord() noexcept = default; + explicit SerializedHalfEdgeRecord( NoInit ) noexcept : next( noInit ), prev( noInit ), org( noInit ), left( noInit ) + { + } +}; +static_assert( sizeof( SerializedHalfEdgeRecord ) == 16 ); + void MeshTopology::write( std::ostream & s ) const { + MR_TIMER; // write edges auto numEdges = (std::uint32_t)edges_.size(); s.write( (const char*)&numEdges, 4 ); - s.write( (const char*)edges_.data(), edges_.size() * sizeof(HalfEdgeRecord) ); + + Vector recs; + recs.resizeNoInit( numEdges ); + ParallelFor( recs, [&] ( EdgeId e ) + { + recs[e].next = edges_[e].next; + recs[e].prev = edges_[e].prev; + recs[e].org = edges_[e].org; + recs[e].left = left_[e]; + } ); + s.write( (const char*)recs.data(), recs.size() * sizeof(HalfEdgeRecord) ); // write verts auto numVerts = (std::uint32_t)edgePerVertex_.size(); @@ -2371,6 +2425,7 @@ void MeshTopology::write( std::ostream & s ) const Expected MeshTopology::read( std::istream & s, ProgressCallback callback ) { + MR_TIMER; updateValids_ = false; // read edges @@ -2383,14 +2438,25 @@ Expected MeshTopology::read( std::istream & s, ProgressCallback callback ) if ( size_t( streamSize ) < numEdges * sizeof( HalfEdgeRecord ) ) return unexpected( std::string( "Stream reading error: stream is too short" ) ); // stream is too short - edges_.resize( numEdges ); - if ( !readByBlocks( s, ( char* )edges_.data(), edges_.size() * sizeof( HalfEdgeRecord ), + Vector recs; + recs.resizeNoInit( numEdges ); + + if ( !readByBlocks( s, ( char* )recs.data(), recs.size() * sizeof( SerializedHalfEdgeRecord ), callback ? [callback] ( float v ) { return callback( v / 3.f ); } : callback ) ) return unexpectedOperationCanceled(); + edges_.resizeNoInit( numEdges ); + ParallelFor( recs, [&] ( EdgeId e ) + { + edges_[e].next = recs[e].next; + edges_[e].prev = recs[e].prev; + edges_[e].org = recs[e].org; + left_[e] = recs[e].left; + } ); + // read verts std::uint32_t numVerts; s.read( (char*)&numVerts, 4 ); @@ -2460,7 +2526,7 @@ bool MeshTopology::checkValidity( ProgressCallback cb, bool allVerts ) const // check that vertex v is manifold - there is only one ring of edges around it parCheck( edgePerVertex_[v] && fromSameOriginRing( edgePerVertex_[v], e ) ); } - if ( auto f = edges_[e].left ) + if ( auto f = left_[e] ) { parCheck( validFaces_.test( f ) ); // check that face f is manifold - there is only one ring of edges around it @@ -2514,7 +2580,7 @@ bool MeshTopology::checkValidity( ProgressCallback cb, bool allVerts ) const { parCheck( validFaces_.test( f ) ); parCheck( edgePerFace_[f] < edges_.size() ); - parCheck( edges_[edgePerFace_[f]].left == f ); + parCheck( left_[edgePerFace_[f]] == f ); ++myValidFaces; for ( EdgeId e : leftRing( *this, f ) ) parCheck( left(e) == f ); From ac1bcac8cbff014e66cfa275decbd87a4f2d7720 Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sat, 25 Oct 2025 15:46:51 +0300 Subject: [PATCH 04/23] push_back --- source/MRMesh/MRMeshTopology.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index 34468bc4b405..593386e73862 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -64,10 +64,12 @@ EdgeId MeshTopology::makeEdge() HalfEdgeRecord d0; d0.next = d0.prev = he0; edges_.push_back( d0 ); + left_.emplace_back(); HalfEdgeRecord d1; d1.next = d1.prev = he1; edges_.push_back( d1 ); + left_.emplace_back(); return he0; } @@ -1253,8 +1255,12 @@ void MeshTopology::addPart( const MeshTopology & from, const PartMapping & map, setAt( emap, i, edges_.endId() ); if ( map.tgt2srcEdges ) map.tgt2srcEdges->pushBack( UndirectedEdgeId{ undirectedEdgeSize() }, EdgeId{ i } ); - edges_.push_back( from.edges_[ EdgeId( i ) ] ); - edges_.push_back( from.edges_[ EdgeId( i ).sym() ] ); + const EdgeId e0( i ); + const EdgeId e1 = e0.sym(); + edges_.push_back( from.edges_[e0] ); + left_.push_back( from.left_[e0] ); + edges_.push_back( from.edges_[e1] ); + left_.push_back( from.left_[e1] ); } auto vmap = map.src2tgtVerts ? std::move( *map.src2tgtVerts ) : VertMapOrHashMap::createMap(); From 497f57eca2669ebf67ae3c9a4c754515a3165f19 Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sat, 25 Oct 2025 16:04:59 +0300 Subject: [PATCH 05/23] fixes --- source/MRMesh/MRMeshTopology.cpp | 47 ++++++++++++++++------------ source/MRMesh/MRMeshTopology.h | 19 +++++++++-- source/MRMesh/MRMeshTopologyDiff.cpp | 6 ++-- source/MRMesh/MRMeshTopologyDiff.h | 2 +- 4 files changed, 48 insertions(+), 26 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index 593386e73862..eebe7c959511 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -2131,7 +2131,7 @@ void MeshTopology::pack( const PackMapping & map ) { Timer t( "left" ); Vector tmpLeft; - tmpLeft.resizeNoInit( map.e.tsize ); + tmpLeft.resizeNoInit( 2 * map.e.tsize ); ParallelFor( 0_ue, UndirectedEdgeId( undirectedEdgeSize() ), [&] ( UndirectedEdgeId oldUe ) { UndirectedEdgeId newUe = map.e.b[oldUe]; @@ -2386,19 +2386,31 @@ void MeshTopology::packMinMem( const PackMapping & map ) updateValids_ = true; } -struct SerializedHalfEdgeRecord +void MeshTopology::setHalfEdge_( EdgeId e, const SerializedHalfEdgeRecord & rec ) { - EdgeId next; - EdgeId prev; - VertId org; - FaceId left; + edges_[e].next = rec.next; + edges_[e].prev = rec.prev; + edges_[e].org = rec.org; + left_[e] = rec.left; +} - SerializedHalfEdgeRecord() noexcept = default; - explicit SerializedHalfEdgeRecord( NoInit ) noexcept : next( noInit ), prev( noInit ), org( noInit ), left( noInit ) - { - } -}; -static_assert( sizeof( SerializedHalfEdgeRecord ) == 16 ); +void MeshTopology::swapHalfEdge_( EdgeId e, SerializedHalfEdgeRecord & rec ) +{ + std::swap( edges_[e].next, rec.next ); + std::swap( edges_[e].prev, rec.prev ); + std::swap( edges_[e].org, rec.org ); + std::swap( left_[e], rec.left ); +} + +MeshTopology::SerializedHalfEdgeRecord MeshTopology::getHalfEdge_( EdgeId e ) const +{ + SerializedHalfEdgeRecord res( noInit ); + res.next = edges_[e].next; + res.prev = edges_[e].prev; + res.org = edges_[e].org; + res.left = left_[e]; + return res; +} void MeshTopology::write( std::ostream & s ) const { @@ -2411,10 +2423,7 @@ void MeshTopology::write( std::ostream & s ) const recs.resizeNoInit( numEdges ); ParallelFor( recs, [&] ( EdgeId e ) { - recs[e].next = edges_[e].next; - recs[e].prev = edges_[e].prev; - recs[e].org = edges_[e].org; - recs[e].left = left_[e]; + recs[e] = getHalfEdge_( e ); } ); s.write( (const char*)recs.data(), recs.size() * sizeof(HalfEdgeRecord) ); @@ -2455,12 +2464,10 @@ Expected MeshTopology::read( std::istream & s, ProgressCallback callback ) return unexpectedOperationCanceled(); edges_.resizeNoInit( numEdges ); + left_.resizeNoInit( numEdges ); ParallelFor( recs, [&] ( EdgeId e ) { - edges_[e].next = recs[e].next; - edges_[e].prev = recs[e].prev; - edges_[e].org = recs[e].org; - left_[e] = recs[e].left; + setHalfEdge_( e, recs[e] ); } ); // read verts diff --git a/source/MRMesh/MRMeshTopology.h b/source/MRMesh/MRMeshTopology.h index 12a0e9b54a50..6697a8f84c98 100644 --- a/source/MRMesh/MRMeshTopology.h +++ b/source/MRMesh/MRMeshTopology.h @@ -532,13 +532,11 @@ class MeshTopology MRMESH_API bool checkValidity( ProgressCallback cb = {}, bool allVerts = true ) const; private: - friend class MeshTopologyDiff; /// computes from edges_ all remaining fields: \n /// 1) numValidVerts_, 2) validVerts_, 3) edgePerVertex_, /// 4) numValidFaces_, 5) validFaces_, 6) edgePerFace_ MRMESH_API void computeAllFromEdges_(); -private: /// sets new origin to the full origin ring including this edge, without updating edgePerVertex_ table void setOrg_( EdgeId a, VertId v ); @@ -583,6 +581,23 @@ class MeshTopology int numValidFaces_ = 0; ///< the number of valid elements in edgePerFace_ or set bits in validFaces_ bool updateValids_ = true; ///< if false, validVerts_, validFaces_, numValidVerts_, numValidFaces_ are not updated + + friend class MeshTopologyDiff; + /// data of every half-edge + struct SerializedHalfEdgeRecord + { + EdgeId next; + EdgeId prev; + VertId org; + FaceId left; + + SerializedHalfEdgeRecord() noexcept = default; + explicit SerializedHalfEdgeRecord( NoInit ) noexcept : next( noInit ), prev( noInit ), org( noInit ), left( noInit ) {} + }; + static_assert( sizeof( SerializedHalfEdgeRecord ) == 16 ); + void setHalfEdge_( EdgeId e, const SerializedHalfEdgeRecord & rec ); + void swapHalfEdge_( EdgeId e, SerializedHalfEdgeRecord & rec ); + SerializedHalfEdgeRecord getHalfEdge_( EdgeId e ) const; }; template diff --git a/source/MRMesh/MRMeshTopologyDiff.cpp b/source/MRMesh/MRMeshTopologyDiff.cpp index caeb8843981b..5f957837f064 100644 --- a/source/MRMesh/MRMeshTopologyDiff.cpp +++ b/source/MRMesh/MRMeshTopologyDiff.cpp @@ -13,7 +13,7 @@ MeshTopologyDiff::MeshTopologyDiff( const MeshTopology & from, const MeshTopolog for ( EdgeId e{0}; e < toEdgesSize_; ++e ) { if ( e >= from.edges_.size() || from.edges_[e] != to.edges_[e] ) - changedEdges_[e] = to.edges_[e]; + changedEdges_[e] = to.getHalfEdge_( e ); } } @@ -25,7 +25,7 @@ void MeshTopologyDiff::applyAndSwap( MeshTopology & m ) // remember edges_ being deleted from m for ( EdgeId e{toEdgesSize_}; e < mEdgesSize; ++e ) { - changedEdges_[e] = m.edges_[e]; + changedEdges_[e] = m.getHalfEdge_( e ); } m.edges_.resize( toEdgesSize_ ); // swap common edges_ and delete edges_ for vertices missing in original m (that will be next target) @@ -35,7 +35,7 @@ void MeshTopologyDiff::applyAndSwap( MeshTopology & m ) auto & pos = it->second; if ( e < toEdgesSize_ ) { - std::swap( pos, m.edges_[e] ); + m.swapHalfEdge_( e, pos ); if ( e >= mEdgesSize ) { it = changedEdges_.erase( it ); diff --git a/source/MRMesh/MRMeshTopologyDiff.h b/source/MRMesh/MRMeshTopologyDiff.h index d483f6e29f59..b912d515f3e1 100644 --- a/source/MRMesh/MRMeshTopologyDiff.h +++ b/source/MRMesh/MRMeshTopologyDiff.h @@ -32,7 +32,7 @@ class MeshTopologyDiff private: size_t toEdgesSize_ = 0; - HashMap changedEdges_; + HashMap changedEdges_; }; } // namespace MR From 2731fedbf029689b1a3bca34055ac912201779da Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sat, 25 Oct 2025 22:14:22 +0300 Subject: [PATCH 06/23] fix write --- source/MRMesh/MRMeshTopology.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index eebe7c959511..3d55567fd694 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -2425,7 +2425,7 @@ void MeshTopology::write( std::ostream & s ) const { recs[e] = getHalfEdge_( e ); } ); - s.write( (const char*)recs.data(), recs.size() * sizeof(HalfEdgeRecord) ); + s.write( (const char*)recs.data(), recs.size() * sizeof( SerializedHalfEdgeRecord ) ); // write verts auto numVerts = (std::uint32_t)edgePerVertex_.size(); @@ -2450,7 +2450,7 @@ Expected MeshTopology::read( std::istream & s, ProgressCallback callback ) return unexpected( std::string( "Stream reading error" ) ); const auto streamSize = getStreamSize( s ); - if ( size_t( streamSize ) < numEdges * sizeof( HalfEdgeRecord ) ) + if ( size_t( streamSize ) < numEdges * sizeof( SerializedHalfEdgeRecord ) ) return unexpected( std::string( "Stream reading error: stream is too short" ) ); // stream is too short Vector recs; From 2165ff785f097c45c9446df29e189cb3b83b29cf Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sat, 25 Oct 2025 23:01:01 +0300 Subject: [PATCH 07/23] operator == --- source/MRMesh/MRMeshTopology.cpp | 2 +- source/MRMesh/MRMeshTopology.h | 14 ++++++-------- source/MRMesh/MRMeshTopologyDiff.cpp | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index 3d55567fd694..ed55ff347d10 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -1400,7 +1400,7 @@ bool MeshTopology::operator ==( const MeshTopology & b ) const */ } - return edges_ == b.edges_; + return edges_ == b.edges_ && left_ == b.left_; } void MeshTopology::resizeBeforeParallelAdd( size_t edgeSize, size_t vertSize, size_t faceSize ) diff --git a/source/MRMesh/MRMeshTopology.h b/source/MRMesh/MRMeshTopology.h index 6697a8f84c98..11f383a512eb 100644 --- a/source/MRMesh/MRMeshTopology.h +++ b/source/MRMesh/MRMeshTopology.h @@ -550,10 +550,7 @@ class MeshTopology EdgeId prev; ///< next clock wise half-edge in the origin ring VertId org; ///< vertex at the origin of the edge - bool operator ==( const HalfEdgeRecord& b ) const - { - return next == b.next && prev == b.prev && org == b.org; - } + bool operator ==( const HalfEdgeRecord& b ) const = default; HalfEdgeRecord() noexcept = default; explicit HalfEdgeRecord( NoInit ) noexcept : next( noInit ), prev( noInit ), org( noInit ) {} }; @@ -586,11 +583,12 @@ class MeshTopology /// data of every half-edge struct SerializedHalfEdgeRecord { - EdgeId next; - EdgeId prev; - VertId org; - FaceId left; + EdgeId next; ///< next counter clock wise half-edge in the origin ring + EdgeId prev; ///< next clock wise half-edge in the origin ring + VertId org; ///< vertex at the origin of the edge + FaceId left; ///< face at the left of the edge + bool operator ==( const SerializedHalfEdgeRecord& b ) const = default; SerializedHalfEdgeRecord() noexcept = default; explicit SerializedHalfEdgeRecord( NoInit ) noexcept : next( noInit ), prev( noInit ), org( noInit ), left( noInit ) {} }; diff --git a/source/MRMesh/MRMeshTopologyDiff.cpp b/source/MRMesh/MRMeshTopologyDiff.cpp index 5f957837f064..efbde4ca869b 100644 --- a/source/MRMesh/MRMeshTopologyDiff.cpp +++ b/source/MRMesh/MRMeshTopologyDiff.cpp @@ -12,7 +12,7 @@ MeshTopologyDiff::MeshTopologyDiff( const MeshTopology & from, const MeshTopolog toEdgesSize_ = to.edges_.size(); for ( EdgeId e{0}; e < toEdgesSize_; ++e ) { - if ( e >= from.edges_.size() || from.edges_[e] != to.edges_[e] ) + if ( e >= from.edges_.size() || from.getHalfEdge_( e ) != to.getHalfEdge_( e ) ) changedEdges_[e] = to.getHalfEdge_( e ); } } From bce6a578c4b33d806bb83b8ac27e28886021c3e2 Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sat, 25 Oct 2025 23:07:47 +0300 Subject: [PATCH 08/23] renamings --- source/MRMesh/MRMeshTopology.cpp | 52 +++++++++++++++--------------- source/MRMesh/MRMeshTopology.h | 30 ++++++++--------- source/MRMesh/MRMeshTopologyDiff.h | 2 +- 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index ed55ff347d10..c1fa913eaf3d 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -61,12 +61,12 @@ EdgeId MeshTopology::makeEdge() EdgeId he0( int( edges_.size() ) ); EdgeId he1( int( edges_.size() + 1 ) ); - HalfEdgeRecord d0; + OldHalfEdge d0; d0.next = d0.prev = he0; edges_.push_back( d0 ); left_.emplace_back(); - HalfEdgeRecord d1; + OldHalfEdge d1; d1.next = d1.prev = he1; edges_.push_back( d1 ); left_.emplace_back(); @@ -961,7 +961,7 @@ void MeshTopology::deleteFaces( const FaceBitSet & fs, const UndirectedEdgeBitSe } template -void MeshTopology::translateNoFlip_( HalfEdgeRecord & r, FaceId & left, const FM & fmap, const VM & vmap, const WEM & emap ) const +void MeshTopology::translateNoFlip_( OldHalfEdge & r, FaceId & left, const FM & fmap, const VM & vmap, const WEM & emap ) const { for ( auto n = r.next; ; n = next( n ) ) { @@ -983,7 +983,7 @@ void MeshTopology::translateNoFlip_( HalfEdgeRecord & r, FaceId & left, const FM } template -void MeshTopology::translate_( HalfEdgeRecord & r, FaceId & left, HalfEdgeRecord & rsym, FaceId & symLeft, +void MeshTopology::translate_( OldHalfEdge & r, FaceId & left, OldHalfEdge & rsym, FaceId & symLeft, const FM & fmap, const VM & vmap, const WEM & emap, bool flipOrientation ) const { translateNoFlip_( r, left, fmap, vmap, emap ); @@ -1457,9 +1457,9 @@ void MeshTopology::addPackedPart( const MeshTopology & from, EdgeId toEdgeId, co { assert ( !from.isLoneEdge( i ) ); - const HalfEdgeRecord & fromEdge = from.edges_[i]; + const OldHalfEdge & fromEdge = from.edges_[i]; const auto j = emap( i ); - HalfEdgeRecord & to = edges_[j]; + OldHalfEdge & to = edges_[j]; to.next = emap( fromEdge.next ); to.prev = emap( fromEdge.prev ); to.org = fromEdge.org.valid() ? vmap[fromEdge.org] : VertId{}; @@ -1627,7 +1627,7 @@ bool MeshTopology::buildGridMesh( const GridSettings & settings, ProgressCallbac edgePerVertex_[v] = edgeRing[0].e; for ( int i = 0; i < edgeRing.size(); ++i ) { - HalfEdgeRecord he( noInit ); + OldHalfEdge he( noInit ); he.next = i + 1 < edgeRing.size() ? edgeRing[i + 1].e : edgeRing[0].e; he.prev = i > 0 ? edgeRing[i - 1].e : edgeRing.back().e; he.org = v; @@ -1967,16 +1967,16 @@ void MeshTopology::addPartByMask( const MeshTopology & from, const FaceBitSet * { assert ( fromMappedEdges.test( e.undirected() ) ); assert( !left( e1 ) ); - HalfEdgeRecord & toHe = edges_[e1]; - const HalfEdgeRecord & fromHe = from.edges_[e]; + OldHalfEdge & toHe = edges_[e1]; + const OldHalfEdge & fromHe = from.edges_[e]; toHe.next = mapEdge( emap, flipOrientation ? fromHe.prev : fromHe.next ); assert( toHe.next ); if ( auto left = flipOrientation ? from.left_[e.sym()] : from.left_[e] ) left_[e1] = getAt( fmap, left ); } { - HalfEdgeRecord & toHe = edges_[e1.sym()]; - const HalfEdgeRecord & fromHe = from.edges_[e.sym()]; + OldHalfEdge & toHe = edges_[e1.sym()]; + const OldHalfEdge & fromHe = from.edges_[e.sym()]; toHe.prev = mapEdge( emap, flipOrientation ? fromHe.next : fromHe.prev ); assert( toHe.prev ); } @@ -2145,10 +2145,10 @@ void MeshTopology::pack( const PackMapping & map ) left_ = std::move( tmpLeft ); } - Vector, UndirectedEdgeId> tmp( map.e.tsize ); - auto translateHalfEdge = [&]( const HalfEdgeRecord & he ) + Vector, UndirectedEdgeId> tmp( map.e.tsize ); + auto translateHalfEdge = [&]( const OldHalfEdge & he ) { - HalfEdgeRecord res; + OldHalfEdge res; res.next = getAt( map.e.b, he.next ); res.prev = getAt( map.e.b, he.prev ); res.org = getAt( map.v.b, he.org ); @@ -2316,9 +2316,9 @@ void MeshTopology::packMinMem( const PackMapping & map ) struct EdgeRecord { - HalfEdgeRecord he0; + OldHalfEdge he0; FaceId l0; - HalfEdgeRecord he1; + OldHalfEdge he1; FaceId l1; }; @@ -2350,7 +2350,7 @@ void MeshTopology::packMinMem( const PackMapping & map ) { for ( auto ue = range.begin(); ue < range.end(); ++ue ) { - auto translateHalfEdge = [&]( HalfEdgeRecord & he ) + auto translateHalfEdge = [&]( OldHalfEdge & he ) { he.next = getAt( map.e.b, he.next ); he.prev = getAt( map.e.b, he.prev ); @@ -2386,7 +2386,7 @@ void MeshTopology::packMinMem( const PackMapping & map ) updateValids_ = true; } -void MeshTopology::setHalfEdge_( EdgeId e, const SerializedHalfEdgeRecord & rec ) +void MeshTopology::setHalfEdge_( EdgeId e, const HalfEdgeRecord & rec ) { edges_[e].next = rec.next; edges_[e].prev = rec.prev; @@ -2394,7 +2394,7 @@ void MeshTopology::setHalfEdge_( EdgeId e, const SerializedHalfEdgeRecord & rec left_[e] = rec.left; } -void MeshTopology::swapHalfEdge_( EdgeId e, SerializedHalfEdgeRecord & rec ) +void MeshTopology::swapHalfEdge_( EdgeId e, HalfEdgeRecord & rec ) { std::swap( edges_[e].next, rec.next ); std::swap( edges_[e].prev, rec.prev ); @@ -2402,9 +2402,9 @@ void MeshTopology::swapHalfEdge_( EdgeId e, SerializedHalfEdgeRecord & rec ) std::swap( left_[e], rec.left ); } -MeshTopology::SerializedHalfEdgeRecord MeshTopology::getHalfEdge_( EdgeId e ) const +MeshTopology::HalfEdgeRecord MeshTopology::getHalfEdge_( EdgeId e ) const { - SerializedHalfEdgeRecord res( noInit ); + HalfEdgeRecord res( noInit ); res.next = edges_[e].next; res.prev = edges_[e].prev; res.org = edges_[e].org; @@ -2419,13 +2419,13 @@ void MeshTopology::write( std::ostream & s ) const auto numEdges = (std::uint32_t)edges_.size(); s.write( (const char*)&numEdges, 4 ); - Vector recs; + Vector recs; recs.resizeNoInit( numEdges ); ParallelFor( recs, [&] ( EdgeId e ) { recs[e] = getHalfEdge_( e ); } ); - s.write( (const char*)recs.data(), recs.size() * sizeof( SerializedHalfEdgeRecord ) ); + s.write( (const char*)recs.data(), recs.size() * sizeof( HalfEdgeRecord ) ); // write verts auto numVerts = (std::uint32_t)edgePerVertex_.size(); @@ -2450,13 +2450,13 @@ Expected MeshTopology::read( std::istream & s, ProgressCallback callback ) return unexpected( std::string( "Stream reading error" ) ); const auto streamSize = getStreamSize( s ); - if ( size_t( streamSize ) < numEdges * sizeof( SerializedHalfEdgeRecord ) ) + if ( size_t( streamSize ) < numEdges * sizeof( HalfEdgeRecord ) ) return unexpected( std::string( "Stream reading error: stream is too short" ) ); // stream is too short - Vector recs; + Vector recs; recs.resizeNoInit( numEdges ); - if ( !readByBlocks( s, ( char* )recs.data(), recs.size() * sizeof( SerializedHalfEdgeRecord ), + if ( !readByBlocks( s, ( char* )recs.data(), recs.size() * sizeof( HalfEdgeRecord ), callback ? [callback] ( float v ) { return callback( v / 3.f ); diff --git a/source/MRMesh/MRMeshTopology.h b/source/MRMesh/MRMeshTopology.h index 11f383a512eb..ae9c977edfe1 100644 --- a/source/MRMesh/MRMeshTopology.h +++ b/source/MRMesh/MRMeshTopology.h @@ -544,25 +544,25 @@ class MeshTopology void setLeft_( EdgeId a, FaceId f ); /// data of every half-edge - struct HalfEdgeRecord + struct OldHalfEdge { EdgeId next; ///< next counter clock wise half-edge in the origin ring EdgeId prev; ///< next clock wise half-edge in the origin ring VertId org; ///< vertex at the origin of the edge - bool operator ==( const HalfEdgeRecord& b ) const = default; - HalfEdgeRecord() noexcept = default; - explicit HalfEdgeRecord( NoInit ) noexcept : next( noInit ), prev( noInit ), org( noInit ) {} + bool operator ==( const OldHalfEdge& b ) const = default; + OldHalfEdge() noexcept = default; + explicit OldHalfEdge( NoInit ) noexcept : next( noInit ), prev( noInit ), org( noInit ) {} }; /// translates all fields in the record for this edge given maps template - void translateNoFlip_( HalfEdgeRecord & r, FaceId & left, const FM & fmap, const VM & vmap, const WEM & emap ) const; + void translateNoFlip_( OldHalfEdge & r, FaceId & left, const FM & fmap, const VM & vmap, const WEM & emap ) const; template - void translate_( HalfEdgeRecord & r, FaceId & left, HalfEdgeRecord & rsym, FaceId & symLeft, + void translate_( OldHalfEdge & r, FaceId & left, OldHalfEdge & rsym, FaceId & symLeft, const FM & fmap, const VM & vmap, const WEM & emap, bool flipOrientation ) const; /// edges_: EdgeId -> edge data - Vector edges_; + Vector edges_; Vector left_; ///< left_[e] - face at the left of the edge (e) @@ -581,21 +581,21 @@ class MeshTopology friend class MeshTopologyDiff; /// data of every half-edge - struct SerializedHalfEdgeRecord + struct HalfEdgeRecord { EdgeId next; ///< next counter clock wise half-edge in the origin ring EdgeId prev; ///< next clock wise half-edge in the origin ring VertId org; ///< vertex at the origin of the edge FaceId left; ///< face at the left of the edge - bool operator ==( const SerializedHalfEdgeRecord& b ) const = default; - SerializedHalfEdgeRecord() noexcept = default; - explicit SerializedHalfEdgeRecord( NoInit ) noexcept : next( noInit ), prev( noInit ), org( noInit ), left( noInit ) {} + bool operator ==( const HalfEdgeRecord& b ) const = default; + HalfEdgeRecord() noexcept = default; + explicit HalfEdgeRecord( NoInit ) noexcept : next( noInit ), prev( noInit ), org( noInit ), left( noInit ) {} }; - static_assert( sizeof( SerializedHalfEdgeRecord ) == 16 ); - void setHalfEdge_( EdgeId e, const SerializedHalfEdgeRecord & rec ); - void swapHalfEdge_( EdgeId e, SerializedHalfEdgeRecord & rec ); - SerializedHalfEdgeRecord getHalfEdge_( EdgeId e ) const; + static_assert( sizeof( HalfEdgeRecord ) == 16 ); + void setHalfEdge_( EdgeId e, const HalfEdgeRecord & rec ); + void swapHalfEdge_( EdgeId e, HalfEdgeRecord & rec ); + HalfEdgeRecord getHalfEdge_( EdgeId e ) const; }; template diff --git a/source/MRMesh/MRMeshTopologyDiff.h b/source/MRMesh/MRMeshTopologyDiff.h index b912d515f3e1..d483f6e29f59 100644 --- a/source/MRMesh/MRMeshTopologyDiff.h +++ b/source/MRMesh/MRMeshTopologyDiff.h @@ -32,7 +32,7 @@ class MeshTopologyDiff private: size_t toEdgesSize_ = 0; - HashMap changedEdges_; + HashMap changedEdges_; }; } // namespace MR From 255c1bbab44e7b953add0a0b9ab0f8461f767c0a Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sat, 25 Oct 2025 23:39:45 +0300 Subject: [PATCH 09/23] HalfEdgeRecord --- source/MRMesh/MRMeshTopology.cpp | 18 +++++------------- source/MRMesh/MRMeshTopology.h | 8 ++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index c1fa913eaf3d..88872a0f9cd1 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -2314,29 +2314,21 @@ void MeshTopology::packMinMem( const PackMapping & map ) validVerts_.resize( numValidVerts_, true ); } ); - struct EdgeRecord - { - OldHalfEdge he0; - FaceId l0; - OldHalfEdge he1; - FaceId l1; - }; - shuffle( map.e, [&]( UndirectedEdgeId ue ) { const EdgeId e0( ue ); const EdgeId e1( e0.sym() ); - return EdgeRecord{ edges_[e0], left_[e0], edges_[e1], left_[e1] }; + return EdgeRecord{ HalfEdgeRecord{ edges_[e0], left_[e0] }, HalfEdgeRecord{ edges_[e1], left_[e1] } }; }, [&]( UndirectedEdgeId ue, const EdgeRecord& r ) { const EdgeId e0( ue ); const EdgeId e1( e0.sym() ); - edges_[e0] = r.he0; - left_[e0] = r.l0; - edges_[e1] = r.he1; - left_[e1] = r.l1; + edges_[e0] = r.he[0]; + left_[e0] = r.he[0].left; + edges_[e1] = r.he[1]; + left_[e1] = r.he[1].left; } ); edges_.resize( 2 * map.e.tsize ); diff --git a/source/MRMesh/MRMeshTopology.h b/source/MRMesh/MRMeshTopology.h index ae9c977edfe1..635ebe0dfc5d 100644 --- a/source/MRMesh/MRMeshTopology.h +++ b/source/MRMesh/MRMeshTopology.h @@ -588,14 +588,22 @@ class MeshTopology VertId org; ///< vertex at the origin of the edge FaceId left; ///< face at the left of the edge + operator OldHalfEdge() const { OldHalfEdge res( noInit ); res.next = next; res.prev = prev; res.org = org; return res; } + bool operator ==( const HalfEdgeRecord& b ) const = default; HalfEdgeRecord() noexcept = default; explicit HalfEdgeRecord( NoInit ) noexcept : next( noInit ), prev( noInit ), org( noInit ), left( noInit ) {} + HalfEdgeRecord( const OldHalfEdge & o, FaceId l ) : next( o.next ), prev( o.prev ), org( o.org ), left( l ) {} }; static_assert( sizeof( HalfEdgeRecord ) == 16 ); void setHalfEdge_( EdgeId e, const HalfEdgeRecord & rec ); void swapHalfEdge_( EdgeId e, HalfEdgeRecord & rec ); HalfEdgeRecord getHalfEdge_( EdgeId e ) const; + + struct EdgeRecord + { + HalfEdgeRecord he[2]; + }; }; template From 26c623026fcd491a9d6ffb8cd260bd59fdaa1f88 Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sun, 26 Oct 2025 10:22:02 +0300 Subject: [PATCH 10/23] org0 --- source/MRMesh/MRMeshTopology.cpp | 24 ++++++++++++------------ source/MRMesh/MRMeshTopology.h | 12 ++++++------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index 88872a0f9cd1..95fe71e12839 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -80,12 +80,12 @@ bool MeshTopology::isLoneEdge( EdgeId a ) const if ( a >= edges_.size() ) return true; auto & adata = edges_[a]; - if ( left_[a].valid() || adata.org.valid() || adata.next != a || adata.prev != a ) + if ( left_[a].valid() || org_[a].valid() || adata.next != a || adata.prev != a ) return false; auto b = a.sym(); auto & bdata = edges_[b]; - if ( left_[b].valid() || bdata.org.valid() || bdata.next != b || bdata.prev != b ) + if ( left_[b].valid() || org_[b].valid() || bdata.next != b || bdata.prev != b ) return false; return true; @@ -173,18 +173,18 @@ void MeshTopology::splice( EdgeId a, EdgeId b ) auto & bData = edges_[b]; auto & bNextData = edges_[bNext]; - bool wasSameOriginId = aData.org == bData.org; - assert( wasSameOriginId || !aData.org.valid() || !bData.org.valid() ); + bool wasSameOriginId = org_[a] == org_[b]; + assert( wasSameOriginId || !org_[a].valid() || !org_[b].valid() ); bool wasSameLeftId = left_[a] == left_[b]; assert( wasSameLeftId || !left_[a].valid() || !left_[b].valid() ); if ( !wasSameOriginId ) { - if ( aData.org.valid() ) - setOrg_( b, aData.org ); - else if ( bData.org.valid() ) - setOrg_( a, bData.org ); + if ( org_[a].valid() ) + setOrg_( b, org_[a] ); + else if ( org_[b].valid() ) + setOrg_( a, org_[b] ); } if ( !wasSameLeftId ) @@ -198,11 +198,11 @@ void MeshTopology::splice( EdgeId a, EdgeId b ) std::swap( aData.next, bData.next ); std::swap( aNextData.prev, bNextData.prev ); - if ( wasSameOriginId && bData.org.valid() ) + if ( wasSameOriginId && org_[b].valid() ) { setOrg_( b, VertId() ); - if ( !fromSameOriginRing( edgePerVertex_[aData.org], a ) ) - edgePerVertex_[aData.org] = a; + if ( !fromSameOriginRing( edgePerVertex_[org_[a]], a ) ) + edgePerVertex_[org_[a]] = a; } if ( wasSameLeftId && left_[b].valid() ) @@ -464,7 +464,7 @@ void MeshTopology::setOrg_( EdgeId a, VertId v ) assert( a.valid() ); for ( EdgeId i : orgRing( *this, a ) ) { - edges_[i].org = v; + org_[i] = v; } } diff --git a/source/MRMesh/MRMeshTopology.h b/source/MRMesh/MRMeshTopology.h index 635ebe0dfc5d..99fba60ebb40 100644 --- a/source/MRMesh/MRMeshTopology.h +++ b/source/MRMesh/MRMeshTopology.h @@ -87,10 +87,10 @@ class MeshTopology [[nodiscard]] EdgeId prev( EdgeId he ) const { assert(he.valid()); return edges_[he].prev; } /// returns origin vertex of half-edge - [[nodiscard]] VertId org( EdgeId he ) const { assert(he.valid()); return edges_[he].org; } + [[nodiscard]] VertId org( EdgeId he ) const { assert(he.valid()); return org_[he]; } /// returns destination vertex of half-edge - [[nodiscard]] VertId dest( EdgeId he ) const { assert(he.valid()); return edges_[he.sym()].org; } + [[nodiscard]] VertId dest( EdgeId he ) const { assert(he.valid()); return org_[he.sym()]; } /// returns left face of half-edge [[nodiscard]] FaceId left( EdgeId he ) const { assert(he.valid()); return left_[he]; } @@ -548,11 +548,10 @@ class MeshTopology { EdgeId next; ///< next counter clock wise half-edge in the origin ring EdgeId prev; ///< next clock wise half-edge in the origin ring - VertId org; ///< vertex at the origin of the edge bool operator ==( const OldHalfEdge& b ) const = default; OldHalfEdge() noexcept = default; - explicit OldHalfEdge( NoInit ) noexcept : next( noInit ), prev( noInit ), org( noInit ) {} + explicit OldHalfEdge( NoInit ) noexcept : next( noInit ), prev( noInit ) {} }; /// translates all fields in the record for this edge given maps template @@ -564,6 +563,7 @@ class MeshTopology /// edges_: EdgeId -> edge data Vector edges_; + Vector org_; ///< org_[e] - vertex at the origin of the edge (e) Vector left_; ///< left_[e] - face at the left of the edge (e) /// edgePerVertex_: VertId -> one edge id of one of edges with origin there @@ -588,12 +588,12 @@ class MeshTopology VertId org; ///< vertex at the origin of the edge FaceId left; ///< face at the left of the edge - operator OldHalfEdge() const { OldHalfEdge res( noInit ); res.next = next; res.prev = prev; res.org = org; return res; } + operator OldHalfEdge() const { OldHalfEdge res( noInit ); res.next = next; res.prev = prev; return res; } bool operator ==( const HalfEdgeRecord& b ) const = default; HalfEdgeRecord() noexcept = default; explicit HalfEdgeRecord( NoInit ) noexcept : next( noInit ), prev( noInit ), org( noInit ), left( noInit ) {} - HalfEdgeRecord( const OldHalfEdge & o, FaceId l ) : next( o.next ), prev( o.prev ), org( o.org ), left( l ) {} + HalfEdgeRecord( const OldHalfEdge & r, VertId o, FaceId l ) : next( r.next ), prev( r.prev ), org( o ), left( l ) {} }; static_assert( sizeof( HalfEdgeRecord ) == 16 ); void setHalfEdge_( EdgeId e, const HalfEdgeRecord & rec ); From ed45def2e8bee04d93e24a7094882749b0706247 Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sun, 26 Oct 2025 10:37:30 +0300 Subject: [PATCH 11/23] org1 --- source/MRMesh/MRMeshTopology.cpp | 52 ++++++++++++++++++------------ source/MRMesh/MRMeshTopology.h | 55 +++++++++++++++++--------------- 2 files changed, 60 insertions(+), 47 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index 95fe71e12839..c5064e47c5a8 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -961,7 +961,7 @@ void MeshTopology::deleteFaces( const FaceBitSet & fs, const UndirectedEdgeBitSe } template -void MeshTopology::translateNoFlip_( OldHalfEdge & r, FaceId & left, const FM & fmap, const VM & vmap, const WEM & emap ) const +void MeshTopology::translateNoFlip_( HalfEdgeRecord & r, const FM & fmap, const VM & vmap, const WEM & emap ) const { for ( auto n = r.next; ; n = next( n ) ) { @@ -978,22 +978,21 @@ void MeshTopology::translateNoFlip_( OldHalfEdge & r, FaceId & left, const FM & if ( r.org.valid() ) r.org = getAt( vmap, r.org ); - if ( left.valid() ) - left = getAt( fmap, left ); + if ( r.left.valid() ) + r.left = getAt( fmap, r.left ); } template -void MeshTopology::translate_( OldHalfEdge & r, FaceId & left, OldHalfEdge & rsym, FaceId & symLeft, - const FM & fmap, const VM & vmap, const WEM & emap, bool flipOrientation ) const +void MeshTopology::translate_( EdgeRecord & r, const FM & fmap, const VM & vmap, const WEM & emap, bool flipOrientation ) const { - translateNoFlip_( r, left, fmap, vmap, emap ); - translateNoFlip_( rsym, symLeft, fmap, vmap, emap ); + translateNoFlip_( r.he[0], fmap, vmap, emap ); + translateNoFlip_( r.he[1], fmap, vmap, emap ); if ( flipOrientation ) { - std::swap( r.prev, r.next ); - std::swap( rsym.prev, rsym.next ); - std::swap( left, symLeft ); + std::swap( r.he[0].prev, r.he[0].next ); + std::swap( r.he[1].prev, r.he[1].next ); + std::swap( r.he[0].left, r.he[1].left ); } } @@ -1352,8 +1351,9 @@ void MeshTopology::addPart( const MeshTopology & from, const PartMapping & map, { for ( UndirectedEdgeId ue = range.begin(); ue < range.end(); ++ue ) { - EdgeId e{ ue }; - from.translate_( edges_[e], left_[e], edges_[e.sym()], left_[e.sym()], fmap, vmap, emap, false ); + auto r = getEdge_( ue ); + from.translate_( r, fmap, vmap, emap, false ); + setEdge_( ue, r ); } } ); @@ -1462,8 +1462,8 @@ void MeshTopology::addPackedPart( const MeshTopology & from, EdgeId toEdgeId, co OldHalfEdge & to = edges_[j]; to.next = emap( fromEdge.next ); to.prev = emap( fromEdge.prev ); - to.org = fromEdge.org.valid() ? vmap[fromEdge.org] : VertId{}; - left_[j] = from.left_[i].valid() ? fmap[from.left_[i]] : FaceId{}; + org_[j] = getAt( vmap, from.org_[i] ); + left_[j] = getAt( fmap, from.left_[i] ); } } @@ -1630,9 +1630,9 @@ bool MeshTopology::buildGridMesh( const GridSettings & settings, ProgressCallbac OldHalfEdge he( noInit ); he.next = i + 1 < edgeRing.size() ? edgeRing[i + 1].e : edgeRing[0].e; he.prev = i > 0 ? edgeRing[i - 1].e : edgeRing.back().e; - he.org = v; const auto e = edgeRing[i].e; edges_[e] = he; + org_[e] = v; left_[e] = edgeRing[i].f; } } @@ -1706,8 +1706,8 @@ void MeshTopology::computeAllFromEdges_() MR_TIMER; VertId maxValidVert; - for( const auto & he : edges_ ) - maxValidVert = std::max( maxValidVert, he.org ); + for( const auto & v : org_ ) + maxValidVert = std::max( maxValidVert, v ); FaceId maxValidFace; for ( const auto& f : left_ ) @@ -1727,12 +1727,11 @@ void MeshTopology::computeAllFromEdges_() for ( EdgeId e{0}; e < edges_.size(); ++e ) { - const auto & he = edges_[e]; - if ( he.org.valid() ) + if ( org_[e].valid() ) { - if ( !validVerts_.test_set( he.org ) ) + if ( !validVerts_.test_set( org_[e] ) ) { - edgePerVertex_[he.org] = e; + edgePerVertex_[org_[e]] = e; ++numValidVerts_; } } @@ -2404,6 +2403,17 @@ MeshTopology::HalfEdgeRecord MeshTopology::getHalfEdge_( EdgeId e ) const return res; } +void MeshTopology::setEdge_( UndirectedEdgeId ue, const EdgeRecord & rec ) +{ + setHalfEdge_( EdgeId( ue ), rec.he[0] ); + setHalfEdge_( EdgeId( ue ).sym(), rec.he[1] ); +} + +MeshTopology::EdgeRecord MeshTopology::getEdge_( UndirectedEdgeId ue ) const +{ + return { getHalfEdge_( EdgeId( ue ) ), getHalfEdge_( EdgeId( ue ).sym() ) }; +} + void MeshTopology::write( std::ostream & s ) const { MR_TIMER; diff --git a/source/MRMesh/MRMeshTopology.h b/source/MRMesh/MRMeshTopology.h index 99fba60ebb40..027591456527 100644 --- a/source/MRMesh/MRMeshTopology.h +++ b/source/MRMesh/MRMeshTopology.h @@ -553,33 +553,7 @@ class MeshTopology OldHalfEdge() noexcept = default; explicit OldHalfEdge( NoInit ) noexcept : next( noInit ), prev( noInit ) {} }; - /// translates all fields in the record for this edge given maps - template - void translateNoFlip_( OldHalfEdge & r, FaceId & left, const FM & fmap, const VM & vmap, const WEM & emap ) const; - template - void translate_( OldHalfEdge & r, FaceId & left, OldHalfEdge & rsym, FaceId & symLeft, - const FM & fmap, const VM & vmap, const WEM & emap, bool flipOrientation ) const; - - /// edges_: EdgeId -> edge data - Vector edges_; - - Vector org_; ///< org_[e] - vertex at the origin of the edge (e) - Vector left_; ///< left_[e] - face at the left of the edge (e) - - /// edgePerVertex_: VertId -> one edge id of one of edges with origin there - Vector edgePerVertex_; - VertBitSet validVerts_; ///< each true bit here corresponds to valid element in edgePerVertex_ - - /// edgePerFace_: FaceId -> one edge id with this face at left - Vector edgePerFace_; - FaceBitSet validFaces_; ///< each true bit here corresponds to valid element in edgePerFace_ - - int numValidVerts_ = 0; ///< the number of valid elements in edgePerVertex_ or set bits in validVerts_ - int numValidFaces_ = 0; ///< the number of valid elements in edgePerFace_ or set bits in validFaces_ - - bool updateValids_ = true; ///< if false, validVerts_, validFaces_, numValidVerts_, numValidFaces_ are not updated - friend class MeshTopologyDiff; /// data of every half-edge struct HalfEdgeRecord { @@ -604,6 +578,35 @@ class MeshTopology { HalfEdgeRecord he[2]; }; + void setEdge_( UndirectedEdgeId ue, const EdgeRecord & rec ); + EdgeRecord getEdge_( UndirectedEdgeId ue ) const; + + /// translates all fields in the record for this edge given maps + template + void translateNoFlip_( HalfEdgeRecord & r, const FM & fmap, const VM & vmap, const WEM & emap ) const; + template + void translate_( EdgeRecord & r, const FM & fmap, const VM & vmap, const WEM & emap, bool flipOrientation ) const; + + /// edges_: EdgeId -> edge data + Vector edges_; + + Vector org_; ///< org_[e] - vertex at the origin of the edge (e) + Vector left_; ///< left_[e] - face at the left of the edge (e) + + /// edgePerVertex_: VertId -> one edge id of one of edges with origin there + Vector edgePerVertex_; + VertBitSet validVerts_; ///< each true bit here corresponds to valid element in edgePerVertex_ + + /// edgePerFace_: FaceId -> one edge id with this face at left + Vector edgePerFace_; + FaceBitSet validFaces_; ///< each true bit here corresponds to valid element in edgePerFace_ + + int numValidVerts_ = 0; ///< the number of valid elements in edgePerVertex_ or set bits in validVerts_ + int numValidFaces_ = 0; ///< the number of valid elements in edgePerFace_ or set bits in validFaces_ + + bool updateValids_ = true; ///< if false, validVerts_, validFaces_, numValidVerts_, numValidFaces_ are not updated + + friend class MeshTopologyDiff; }; template From a90707abb0df933e72f95261f9c5e02792134030 Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sun, 26 Oct 2025 10:48:26 +0300 Subject: [PATCH 12/23] org2 --- source/MRMesh/MRMeshTopology.cpp | 66 +++++++++++++++----------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index c5064e47c5a8..8ad6bcb4adc2 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -1987,20 +1987,9 @@ void MeshTopology::addPartByMask( const MeshTopology & from, const FaceBitSet * left_.resizeNoInit( nextNewEdge ); BitSetParallelFor( fromCopiedEdges, [&]( UndirectedEdgeId fromUe ) { - const EdgeId fromE( fromUe ); - const EdgeId fromSymE( fromE.sym() ); - auto e0 = from.edges_[fromE]; - auto l0 = from.left_[fromE]; - auto e1 = from.edges_[fromSymE]; - auto l1 = from.left_[fromSymE]; - from.translate_( e0, l0, e1, l1, fmap, vmap, emap, flipOrientation ); - - const UndirectedEdgeId nue = getAt( emap, fromUe ); - const EdgeId ne{ nue }; - edges_[ne] = e0; - left_[ne] = l0; - edges_[ne.sym()] = e1; - left_[ne.sym()] = l1; + auto r = from.getEdge_( fromUe ); + from.translate_( r, fmap, vmap, emap, flipOrientation ); + setEdge_( getAt( emap, fromUe ), r ); } ); // translate vertex records @@ -2126,6 +2115,24 @@ void MeshTopology::pack( const PackMapping & map ) { MR_TIMER; + // translate origin vertices + { + Timer t( "org" ); + Vector tmpOrg; + tmpOrg.resizeNoInit( 2 * map.e.tsize ); + ParallelFor( 0_ue, UndirectedEdgeId( undirectedEdgeSize() ), [&] ( UndirectedEdgeId oldUe ) + { + UndirectedEdgeId newUe = map.e.b[oldUe]; + if ( !newUe ) + return; + EdgeId oldE = oldUe; + EdgeId newE = newUe; + tmpOrg[newE] = getAt( map.v.b, org_[oldE] ); + tmpOrg[newE.sym()] = getAt( map.v.b, org_[oldE.sym()] ); + } ); + org_ = std::move( tmpOrg ); + } + // translate left faces { Timer t( "left" ); @@ -2150,7 +2157,6 @@ void MeshTopology::pack( const PackMapping & map ) OldHalfEdge res; res.next = getAt( map.e.b, he.next ); res.prev = getAt( map.e.b, he.prev ); - res.org = getAt( map.v.b, he.org ); return res; }; @@ -2314,21 +2320,8 @@ void MeshTopology::packMinMem( const PackMapping & map ) } ); shuffle( map.e, - [&]( UndirectedEdgeId ue ) - { - const EdgeId e0( ue ); - const EdgeId e1( e0.sym() ); - return EdgeRecord{ HalfEdgeRecord{ edges_[e0], left_[e0] }, HalfEdgeRecord{ edges_[e1], left_[e1] } }; - }, - [&]( UndirectedEdgeId ue, const EdgeRecord& r ) - { - const EdgeId e0( ue ); - const EdgeId e1( e0.sym() ); - edges_[e0] = r.he[0]; - left_[e0] = r.he[0].left; - edges_[e1] = r.he[1]; - left_[e1] = r.he[1].left; - } + [&]( UndirectedEdgeId ue ) { return getEdge_( ue ); }, + [&]( UndirectedEdgeId ue, const EdgeRecord& r ) { setEdge_( ue, r ); } ); edges_.resize( 2 * map.e.tsize ); left_.resize( 2 * map.e.tsize ); @@ -2345,12 +2338,13 @@ void MeshTopology::packMinMem( const PackMapping & map ) { he.next = getAt( map.e.b, he.next ); he.prev = getAt( map.e.b, he.prev ); - he.org = getAt( map.v.b, he.org ); }; const EdgeId e0( ue ); const EdgeId e1( e0.sym() ); translateHalfEdge( edges_[e0] ); translateHalfEdge( edges_[e1] ); + org_[e0] = getAt( map.v.b, org_[e0] ); + org_[e1] = getAt( map.v.b, org_[e1] ); left_[e0] = getAt( map.f.b, left_[e0] ); left_[e1] = getAt( map.f.b, left_[e1] ); } @@ -2381,7 +2375,7 @@ void MeshTopology::setHalfEdge_( EdgeId e, const HalfEdgeRecord & rec ) { edges_[e].next = rec.next; edges_[e].prev = rec.prev; - edges_[e].org = rec.org; + org_[e] = rec.org; left_[e] = rec.left; } @@ -2389,7 +2383,7 @@ void MeshTopology::swapHalfEdge_( EdgeId e, HalfEdgeRecord & rec ) { std::swap( edges_[e].next, rec.next ); std::swap( edges_[e].prev, rec.prev ); - std::swap( edges_[e].org, rec.org ); + std::swap( org_[e], rec.org ); std::swap( left_[e], rec.left ); } @@ -2398,7 +2392,7 @@ MeshTopology::HalfEdgeRecord MeshTopology::getHalfEdge_( EdgeId e ) const HalfEdgeRecord res( noInit ); res.next = edges_[e].next; res.prev = edges_[e].prev; - res.org = edges_[e].org; + res.org = org_[e]; res.left = left_[e]; return res; } @@ -2532,7 +2526,7 @@ bool MeshTopology::checkValidity( ProgressCallback cb, bool allVerts ) const return; parCheck( edges_[edges_[e].next].prev == e ); parCheck( edges_[edges_[e].prev].next == e ); - auto v = edges_[e].org; + auto v = org_[e]; if ( allVerts && !isLoneEdge( e ) ) parCheck( v.valid() ); if ( v ) @@ -2565,7 +2559,7 @@ bool MeshTopology::checkValidity( ProgressCallback cb, bool allVerts ) const { parCheck( validVerts_.test( v ) ); parCheck( edgePerVertex_[v] < edges_.size() ); - parCheck( edges_[edgePerVertex_[v]].org == v ); + parCheck( org_[edgePerVertex_[v]] == v ); ++myValidVerts; for ( EdgeId e : orgRing( *this, v ) ) parCheck( org(e) == v ); From 61880cdf9239563bf3ae2b8f0d59659308e3f97d Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sun, 26 Oct 2025 10:55:10 +0300 Subject: [PATCH 13/23] inline --- source/MRMesh/MRMeshTopology.cpp | 37 -------------------------------- source/MRMesh/MRMeshTopology.h | 11 +++++----- 2 files changed, 6 insertions(+), 42 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index 8ad6bcb4adc2..f8e8078e314a 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -2371,43 +2371,6 @@ void MeshTopology::packMinMem( const PackMapping & map ) updateValids_ = true; } -void MeshTopology::setHalfEdge_( EdgeId e, const HalfEdgeRecord & rec ) -{ - edges_[e].next = rec.next; - edges_[e].prev = rec.prev; - org_[e] = rec.org; - left_[e] = rec.left; -} - -void MeshTopology::swapHalfEdge_( EdgeId e, HalfEdgeRecord & rec ) -{ - std::swap( edges_[e].next, rec.next ); - std::swap( edges_[e].prev, rec.prev ); - std::swap( org_[e], rec.org ); - std::swap( left_[e], rec.left ); -} - -MeshTopology::HalfEdgeRecord MeshTopology::getHalfEdge_( EdgeId e ) const -{ - HalfEdgeRecord res( noInit ); - res.next = edges_[e].next; - res.prev = edges_[e].prev; - res.org = org_[e]; - res.left = left_[e]; - return res; -} - -void MeshTopology::setEdge_( UndirectedEdgeId ue, const EdgeRecord & rec ) -{ - setHalfEdge_( EdgeId( ue ), rec.he[0] ); - setHalfEdge_( EdgeId( ue ).sym(), rec.he[1] ); -} - -MeshTopology::EdgeRecord MeshTopology::getEdge_( UndirectedEdgeId ue ) const -{ - return { getHalfEdge_( EdgeId( ue ) ), getHalfEdge_( EdgeId( ue ).sym() ) }; -} - void MeshTopology::write( std::ostream & s ) const { MR_TIMER; diff --git a/source/MRMesh/MRMeshTopology.h b/source/MRMesh/MRMeshTopology.h index 027591456527..2d9503b7a861 100644 --- a/source/MRMesh/MRMeshTopology.h +++ b/source/MRMesh/MRMeshTopology.h @@ -568,18 +568,19 @@ class MeshTopology HalfEdgeRecord() noexcept = default; explicit HalfEdgeRecord( NoInit ) noexcept : next( noInit ), prev( noInit ), org( noInit ), left( noInit ) {} HalfEdgeRecord( const OldHalfEdge & r, VertId o, FaceId l ) : next( r.next ), prev( r.prev ), org( o ), left( l ) {} + HalfEdgeRecord( EdgeId n, EdgeId p, VertId o, FaceId l ) : next( n ), prev( p ), org( o ), left( l ) {} }; static_assert( sizeof( HalfEdgeRecord ) == 16 ); - void setHalfEdge_( EdgeId e, const HalfEdgeRecord & rec ); - void swapHalfEdge_( EdgeId e, HalfEdgeRecord & rec ); - HalfEdgeRecord getHalfEdge_( EdgeId e ) const; + void setHalfEdge_( EdgeId e, const HalfEdgeRecord & rec ) { edges_[e].next = rec.next; edges_[e].prev = rec.prev; org_[e] = rec.org; left_[e] = rec.left; } + void swapHalfEdge_( EdgeId e, HalfEdgeRecord & rec ) { std::swap( edges_[e].next, rec.next ); std::swap( edges_[e].prev, rec.prev ); std::swap( org_[e], rec.org ); std::swap( left_[e], rec.left ); } + HalfEdgeRecord getHalfEdge_( EdgeId e ) const { return { edges_[e].next, edges_[e].prev, org_[e], left_[e] }; } struct EdgeRecord { HalfEdgeRecord he[2]; }; - void setEdge_( UndirectedEdgeId ue, const EdgeRecord & rec ); - EdgeRecord getEdge_( UndirectedEdgeId ue ) const; + void setEdge_( UndirectedEdgeId ue, const EdgeRecord & rec ) { setHalfEdge_( EdgeId( ue ), rec.he[0] ); setHalfEdge_( EdgeId( ue ).sym(), rec.he[1] ); } + EdgeRecord getEdge_( UndirectedEdgeId ue ) const { return { getHalfEdge_( EdgeId( ue ) ), getHalfEdge_( EdgeId( ue ).sym() ) }; } /// translates all fields in the record for this edge given maps template From 03e1e19711cce0f8a1c2687f2ae1e3af13d6cd20 Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sun, 26 Oct 2025 10:58:41 +0300 Subject: [PATCH 14/23] org resize --- source/MRMesh/MRMeshTopology.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index f8e8078e314a..fad8b12a68f6 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -64,11 +64,13 @@ EdgeId MeshTopology::makeEdge() OldHalfEdge d0; d0.next = d0.prev = he0; edges_.push_back( d0 ); + org_.emplace_back(); left_.emplace_back(); OldHalfEdge d1; d1.next = d1.prev = he1; edges_.push_back( d1 ); + org_.emplace_back(); left_.emplace_back(); return he0; @@ -1257,8 +1259,10 @@ void MeshTopology::addPart( const MeshTopology & from, const PartMapping & map, const EdgeId e0( i ); const EdgeId e1 = e0.sym(); edges_.push_back( from.edges_[e0] ); + org_.push_back( from.org_[e0] ); left_.push_back( from.left_[e0] ); edges_.push_back( from.edges_[e1] ); + org_.push_back( from.org_[e1] ); left_.push_back( from.left_[e1] ); } @@ -1410,6 +1414,7 @@ void MeshTopology::resizeBeforeParallelAdd( size_t edgeSize, size_t vertSize, si updateValids_ = false; edges_.resizeNoInit( edgeSize ); + org_.resizeNoInit( edgeSize ); left_.resizeNoInit( edgeSize ); edgePerVertex_.resize( vertSize ); @@ -1517,6 +1522,7 @@ bool MeshTopology::buildGridMesh( const GridSettings & settings, ProgressCallbac edgePerVertex_.resizeNoInit( settings.vertIds.tsize ); edgePerFace_.resizeNoInit( settings.faceIds.tsize ); edges_.resizeNoInit( 2 * settings.uedgeIds.tsize ); + org_.resizeNoInit( 2 * settings.uedgeIds.tsize ); left_.resizeNoInit( 2 * settings.uedgeIds.tsize ); auto getVertId = [&]( Vector2i v ) -> VertId @@ -1984,6 +1990,7 @@ void MeshTopology::addPartByMask( const MeshTopology & from, const FaceBitSet * // translate edge records edges_.resizeNoInit( nextNewEdge ); + org_.resizeNoInit( nextNewEdge ); left_.resizeNoInit( nextNewEdge ); BitSetParallelFor( fromCopiedEdges, [&]( UndirectedEdgeId fromUe ) { @@ -2324,6 +2331,7 @@ void MeshTopology::packMinMem( const PackMapping & map ) [&]( UndirectedEdgeId ue, const EdgeRecord& r ) { setEdge_( ue, r ); } ); edges_.resize( 2 * map.e.tsize ); + org_.resize( 2 * map.e.tsize ); left_.resize( 2 * map.e.tsize ); group.wait(); @@ -2423,6 +2431,7 @@ Expected MeshTopology::read( std::istream & s, ProgressCallback callback ) return unexpectedOperationCanceled(); edges_.resizeNoInit( numEdges ); + org_.resizeNoInit( numEdges ); left_.resizeNoInit( numEdges ); ParallelFor( recs, [&] ( EdgeId e ) { From b03cd868e02d09bfcb8c1cd4300ae242c180b835 Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sun, 26 Oct 2025 11:04:50 +0300 Subject: [PATCH 15/23] edges0 --- source/MRMesh/MRMeshTopology.cpp | 12 ++++++++++++ source/MRMesh/MRMeshTopology.h | 29 +++++++---------------------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index fad8b12a68f6..7d812de38006 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -142,6 +142,18 @@ UndirectedEdgeBitSet MeshTopology::findNotLoneUndirectedEdges() const return res; } +void MeshTopology::edgeReserve( size_t newCapacity ) +{ + assert( next_.capacity() == prev_.capacity() ); + assert( next_.capacity() == org_.capacity() ); + assert( next_.capacity() == left_.capacity() ); + + next_.reserve( newCapacity ); + prev_.reserve( newCapacity ); + org_.reserve( newCapacity ); + left_.reserve( newCapacity ); +} + size_t MeshTopology::heapBytes() const { return diff --git a/source/MRMesh/MRMeshTopology.h b/source/MRMesh/MRMeshTopology.h index 2d9503b7a861..eccd57b3c574 100644 --- a/source/MRMesh/MRMeshTopology.h +++ b/source/MRMesh/MRMeshTopology.h @@ -34,16 +34,16 @@ class MeshTopology MRMESH_API void excludeLoneEdges( UndirectedEdgeBitSet & edges ) const; /// returns the number of half-edge records including lone ones - [[nodiscard]] size_t edgeSize() const { return edges_.size(); } + [[nodiscard]] size_t edgeSize() const { return next_.size(); } /// returns the number of allocated edge records - [[nodiscard]] size_t edgeCapacity() const { return edges_.capacity(); } + [[nodiscard]] size_t edgeCapacity() const { return next_.capacity(); } /// returns the number of undirected edges (pairs of half-edges) including lone ones - [[nodiscard]] size_t undirectedEdgeSize() const { return edges_.size() >> 1; } + [[nodiscard]] size_t undirectedEdgeSize() const { return next_.size() >> 1; } /// returns the number of allocated undirected edges (pairs of half-edges) - [[nodiscard]] size_t undirectedEdgeCapacity() const { return edges_.capacity() >> 1; } + [[nodiscard]] size_t undirectedEdgeCapacity() const { return next_.capacity() >> 1; } /// computes the number of not-lone (valid) undirected edges [[nodiscard]] MRMESH_API size_t computeNotLoneUndirectedEdges() const; @@ -52,7 +52,7 @@ class MeshTopology [[nodiscard]] MRMESH_API UndirectedEdgeBitSet findNotLoneUndirectedEdges() const; /// sets the capacity of half-edges vector - void edgeReserve( size_t newCapacity ) { edges_.reserve( newCapacity ); } + MRMESH_API void edgeReserve( size_t newCapacity ); /// returns true if given edge is within valid range and not-lone [[nodiscard]] bool hasEdge( EdgeId e ) const { assert( e.valid() ); return e < (int)edgeSize() && !isLoneEdge( e ); } @@ -543,17 +543,6 @@ class MeshTopology /// sets new left face to the full left ring including this edge, without updating edgePerFace_ table void setLeft_( EdgeId a, FaceId f ); - /// data of every half-edge - struct OldHalfEdge - { - EdgeId next; ///< next counter clock wise half-edge in the origin ring - EdgeId prev; ///< next clock wise half-edge in the origin ring - - bool operator ==( const OldHalfEdge& b ) const = default; - OldHalfEdge() noexcept = default; - explicit OldHalfEdge( NoInit ) noexcept : next( noInit ), prev( noInit ) {} - }; - /// data of every half-edge struct HalfEdgeRecord { @@ -562,12 +551,9 @@ class MeshTopology VertId org; ///< vertex at the origin of the edge FaceId left; ///< face at the left of the edge - operator OldHalfEdge() const { OldHalfEdge res( noInit ); res.next = next; res.prev = prev; return res; } - bool operator ==( const HalfEdgeRecord& b ) const = default; HalfEdgeRecord() noexcept = default; explicit HalfEdgeRecord( NoInit ) noexcept : next( noInit ), prev( noInit ), org( noInit ), left( noInit ) {} - HalfEdgeRecord( const OldHalfEdge & r, VertId o, FaceId l ) : next( r.next ), prev( r.prev ), org( o ), left( l ) {} HalfEdgeRecord( EdgeId n, EdgeId p, VertId o, FaceId l ) : next( n ), prev( p ), org( o ), left( l ) {} }; static_assert( sizeof( HalfEdgeRecord ) == 16 ); @@ -588,9 +574,8 @@ class MeshTopology template void translate_( EdgeRecord & r, const FM & fmap, const VM & vmap, const WEM & emap, bool flipOrientation ) const; - /// edges_: EdgeId -> edge data - Vector edges_; - + Vector next_; ///< next_[e] - next counter clock wise half-edge in the origin ring of (e) + Vector prev_; ///< prev_[e] - next clock wise half-edge in the origin ring of (e) Vector org_; ///< org_[e] - vertex at the origin of the edge (e) Vector left_; ///< left_[e] - face at the left of the edge (e) From 09987cd24480838be3fc666bf386d9f14ae05903 Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sun, 26 Oct 2025 11:11:13 +0300 Subject: [PATCH 16/23] edges1 --- source/MRMesh/MRMeshTopology.cpp | 45 +++++++++++++++++++------------- source/MRMesh/MRMeshTopology.h | 10 +++---- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index 7d812de38006..34272ef24182 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -57,19 +57,20 @@ void MeshTopology::faceResizeWithReserve( size_t newSize ) EdgeId MeshTopology::makeEdge() { - assert( edges_.size() % 2 == 0 ); - EdgeId he0( int( edges_.size() ) ); - EdgeId he1( int( edges_.size() + 1 ) ); - - OldHalfEdge d0; - d0.next = d0.prev = he0; - edges_.push_back( d0 ); + assert( next_.size() == prev_.size() ); + assert( next_.size() == org_.size() ); + assert( next_.size() == left_.size() ); + assert( next_.size() % 2 == 0 ); + EdgeId he0( int( next_.size() ) ); + EdgeId he1( int( next_.size() + 1 ) ); + + next_.push_back( he0 ); + prev_.push_back( he0 ); org_.emplace_back(); left_.emplace_back(); - OldHalfEdge d1; - d1.next = d1.prev = he1; - edges_.push_back( d1 ); + next_.push_back( he1 ); + prev_.push_back( he1 ); org_.emplace_back(); left_.emplace_back(); @@ -79,15 +80,13 @@ EdgeId MeshTopology::makeEdge() bool MeshTopology::isLoneEdge( EdgeId a ) const { assert( a.valid() ); - if ( a >= edges_.size() ) + if ( a >= next_.size() ) return true; - auto & adata = edges_[a]; - if ( left_[a].valid() || org_[a].valid() || adata.next != a || adata.prev != a ) + if ( left_[a].valid() || org_[a].valid() || next_[a] != a || prev_[a] != a ) return false; auto b = a.sym(); - auto & bdata = edges_[b]; - if ( left_[b].valid() || org_[b].valid() || bdata.next != b || bdata.prev != b ) + if ( left_[b].valid() || org_[b].valid() || next_[b] != b || prev_[b] != b ) return false; return true; @@ -95,7 +94,7 @@ bool MeshTopology::isLoneEdge( EdgeId a ) const UndirectedEdgeId MeshTopology::lastNotLoneUndirectedEdge() const { - assert( edges_.size() % 2 == 0 ); + assert( next_.size() % 2 == 0 ); for ( UndirectedEdgeId i{ (int)undirectedEdgeSize() - 1 }; i.valid(); --i ) { if ( !isLoneEdge( i ) ) @@ -157,7 +156,10 @@ void MeshTopology::edgeReserve( size_t newCapacity ) size_t MeshTopology::heapBytes() const { return - edges_.heapBytes() + + next_.heapBytes() + + prev_.heapBytes() + + org_.heapBytes() + + left_.heapBytes() + edgePerVertex_.heapBytes() + validVerts_.heapBytes() + edgePerFace_.heapBytes() + @@ -167,7 +169,14 @@ size_t MeshTopology::heapBytes() const void MeshTopology::shrinkToFit() { MR_TIMER; - edges_.vec_.shrink_to_fit(); + next_.vec_.shrink_to_fit(); + prev_.vec_.shrink_to_fit(); + org_.vec_.shrink_to_fit(); + left_.vec_.shrink_to_fit(); + assert( next_.size() == prev_.size() ); + assert( next_.size() == org_.size() ); + assert( next_.size() == left_.size() ); + edgePerVertex_.vec_.shrink_to_fit(); validVerts_.shrink_to_fit(); edgePerFace_.vec_.shrink_to_fit(); diff --git a/source/MRMesh/MRMeshTopology.h b/source/MRMesh/MRMeshTopology.h index eccd57b3c574..af9cd8bb3c54 100644 --- a/source/MRMesh/MRMeshTopology.h +++ b/source/MRMesh/MRMeshTopology.h @@ -81,10 +81,10 @@ class MeshTopology /// next (counter clock wise) half-edge in the origin ring - [[nodiscard]] EdgeId next( EdgeId he ) const { assert(he.valid()); return edges_[he].next; } + [[nodiscard]] EdgeId next( EdgeId he ) const { assert(he.valid()); return next_[he]; } /// previous (clock wise) half-edge in the origin ring - [[nodiscard]] EdgeId prev( EdgeId he ) const { assert(he.valid()); return edges_[he].prev; } + [[nodiscard]] EdgeId prev( EdgeId he ) const { assert(he.valid()); return prev_[he]; } /// returns origin vertex of half-edge [[nodiscard]] VertId org( EdgeId he ) const { assert(he.valid()); return org_[he]; } @@ -557,9 +557,9 @@ class MeshTopology HalfEdgeRecord( EdgeId n, EdgeId p, VertId o, FaceId l ) : next( n ), prev( p ), org( o ), left( l ) {} }; static_assert( sizeof( HalfEdgeRecord ) == 16 ); - void setHalfEdge_( EdgeId e, const HalfEdgeRecord & rec ) { edges_[e].next = rec.next; edges_[e].prev = rec.prev; org_[e] = rec.org; left_[e] = rec.left; } - void swapHalfEdge_( EdgeId e, HalfEdgeRecord & rec ) { std::swap( edges_[e].next, rec.next ); std::swap( edges_[e].prev, rec.prev ); std::swap( org_[e], rec.org ); std::swap( left_[e], rec.left ); } - HalfEdgeRecord getHalfEdge_( EdgeId e ) const { return { edges_[e].next, edges_[e].prev, org_[e], left_[e] }; } + void setHalfEdge_( EdgeId e, const HalfEdgeRecord & rec ) { next_[e] = rec.next; prev_[e] = rec.prev; org_[e] = rec.org; left_[e] = rec.left; } + void swapHalfEdge_( EdgeId e, HalfEdgeRecord & rec ) { std::swap( next_[e], rec.next ); std::swap( prev_[e], rec.prev ); std::swap( org_[e], rec.org ); std::swap( left_[e], rec.left ); } + HalfEdgeRecord getHalfEdge_( EdgeId e ) const { return { next_[e], prev_[e], org_[e], left_[e] }; } struct EdgeRecord { From 738d3a11352bb978c46f67f5bba01ec0c002dab4 Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sun, 26 Oct 2025 11:17:45 +0300 Subject: [PATCH 17/23] edges2 --- source/MRMesh/MRMeshTopology.cpp | 43 ++++++++++++++------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index 34272ef24182..fdcb144f03e8 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -191,11 +191,6 @@ void MeshTopology::splice( EdgeId a, EdgeId b ) const auto aNext = next( a ); const auto bNext = next( b ); - auto & aData = edges_[a]; - auto & aNextData = edges_[aNext]; - auto & bData = edges_[b]; - auto & bNextData = edges_[bNext]; - bool wasSameOriginId = org_[a] == org_[b]; assert( wasSameOriginId || !org_[a].valid() || !org_[b].valid() ); @@ -218,8 +213,8 @@ void MeshTopology::splice( EdgeId a, EdgeId b ) setLeft_( a, left_[b] ); } - std::swap( aData.next, bData.next ); - std::swap( aNextData.prev, bNextData.prev ); + std::swap( next_[a], next_[b] ); + std::swap( prev_[aNext], prev_[bNext] ); if ( wasSameOriginId && org_[b].valid() ) { @@ -734,7 +729,7 @@ std::vector MeshTopology::getLeftRings( const std::vector & es EdgeBitSet MeshTopology::findBoundaryEdges() const { MR_TIMER; - EdgeBitSet res( edges_.size() ); + EdgeBitSet res( edgeSize() ); BitSetParallelForAll( res, [&]( EdgeId e ) { if ( !left( e ) && !isLoneEdge( e ) ) @@ -756,7 +751,7 @@ bool MeshTopology::isBdEdge( EdgeId e, const FaceBitSet * region ) const EdgeBitSet MeshTopology::findLeftBdEdges( const FaceBitSet * region, const EdgeBitSet * test ) const { MR_TIMER; - EdgeBitSet res( edges_.size() ); + EdgeBitSet res( edgeSize() ); BitSetParallelForAll( res, [&]( EdgeId e ) { if ( test && !test->test( e ) ) @@ -1201,12 +1196,8 @@ void MeshTopology::flipOrientation( const UndirectedEdgeBitSet * fullComponents if ( fullComponents && !fullComponents->test( ue ) ) return; EdgeId i = ue; - auto & r0 = edges_[i]; - std::swap( r0.next, r0.prev ); - - auto & r1 = edges_[i + 1]; - std::swap( r1.next, r1.prev ); - + std::swap( next_[i], prev_[i] ); + std::swap( next_[i + 1], prev_[i + 1] ); std::swap( left_[i], left_[i + 1] ); } ); } @@ -1269,20 +1260,23 @@ void MeshTopology::addPart( const MeshTopology & from, const PartMapping & map, auto emap = map.src2tgtEdges ? std::move( *map.src2tgtEdges ) : WholeEdgeMapOrHashMap::createMap(); const auto ueSize = from.undirectedEdgeSize(); emap.resizeReserve( ueSize, ueSize ); - EdgeId firstNewEdge = edges_.endId(); + EdgeId firstNewEdge = next_.endId(); for ( UndirectedEdgeId i{ 0 }; i < ueSize; ++i ) { if ( from.isLoneEdge( i ) ) continue; - setAt( emap, i, edges_.endId() ); + setAt( emap, i, next_.endId() ); if ( map.tgt2srcEdges ) map.tgt2srcEdges->pushBack( UndirectedEdgeId{ undirectedEdgeSize() }, EdgeId{ i } ); const EdgeId e0( i ); - const EdgeId e1 = e0.sym(); - edges_.push_back( from.edges_[e0] ); + next_.push_back( from.next_[e0] ); + prev_.push_back( from.prev_[e0] ); org_.push_back( from.org_[e0] ); left_.push_back( from.left_[e0] ); - edges_.push_back( from.edges_[e1] ); + + const EdgeId e1 = e0.sym(); + next_.push_back( from.next_[e1] ); + prev_.push_back( from.prev_[e1] ); org_.push_back( from.org_[e1] ); left_.push_back( from.left_[e1] ); } @@ -1371,7 +1365,7 @@ void MeshTopology::addPart( const MeshTopology & from, const PartMapping & map, } // translate edge records - tbb::parallel_for( tbb::blocked_range( firstNewEdge.undirected(), edges_.endId().undirected() ), + tbb::parallel_for( tbb::blocked_range( firstNewEdge.undirected(), next_.endId().undirected() ), [&]( const tbb::blocked_range & range ) { for ( UndirectedEdgeId ue = range.begin(); ue < range.end(); ++ue ) @@ -1425,7 +1419,7 @@ bool MeshTopology::operator ==( const MeshTopology & b ) const */ } - return edges_ == b.edges_ && left_ == b.left_; + return next_ == b.next_ && prev_ == b.prev_ && org_ == b.org_ && left_ == b.left_; } void MeshTopology::resizeBeforeParallelAdd( size_t edgeSize, size_t vertSize, size_t faceSize ) @@ -1434,7 +1428,8 @@ void MeshTopology::resizeBeforeParallelAdd( size_t edgeSize, size_t vertSize, si updateValids_ = false; - edges_.resizeNoInit( edgeSize ); + next_.resizeNoInit( edgeSize ); + prev_.resizeNoInit( edgeSize ); org_.resizeNoInit( edgeSize ); left_.resizeNoInit( edgeSize ); @@ -1450,7 +1445,7 @@ void MeshTopology::addPackedPart( const MeshTopology & from, EdgeId toEdgeId, co MR_TIMER; assert( toEdgeId.valid() ); - assert( (int)toEdgeId + from.edges_.size() <= edges_.size() ); + assert( (int)toEdgeId + from.next_.size() <= next_.size() ); // in all maps: from index -> to index auto emap = [toEdgeId]( EdgeId e ) { return toEdgeId + (int)e; }; From 17f7dcd347d8c139e00af385496ee05264d50003 Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sun, 26 Oct 2025 11:25:57 +0300 Subject: [PATCH 18/23] edges3 --- source/MRMesh/MRMeshTopology.cpp | 52 +++++++++++++------------------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index fdcb144f03e8..09b59af434b7 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -1477,12 +1477,9 @@ void MeshTopology::addPackedPart( const MeshTopology & from, EdgeId toEdgeId, co for ( EdgeId i{ 0 }; i < from.edgeSize(); ++i ) { assert ( !from.isLoneEdge( i ) ); - - const OldHalfEdge & fromEdge = from.edges_[i]; const auto j = emap( i ); - OldHalfEdge & to = edges_[j]; - to.next = emap( fromEdge.next ); - to.prev = emap( fromEdge.prev ); + next_[j] = emap( from.next_[i] ); + prev_[j] = emap( from.prev_[i] ); org_[j] = getAt( vmap, from.org_[i] ); left_[j] = getAt( fmap, from.left_[i] ); } @@ -1537,7 +1534,8 @@ bool MeshTopology::buildGridMesh( const GridSettings & settings, ProgressCallbac // note: some vertices might be valid but have no edge edgePerVertex_.resizeNoInit( settings.vertIds.tsize ); edgePerFace_.resizeNoInit( settings.faceIds.tsize ); - edges_.resizeNoInit( 2 * settings.uedgeIds.tsize ); + next_.resizeNoInit( 2 * settings.uedgeIds.tsize ); + prev_.resizeNoInit( 2 * settings.uedgeIds.tsize ); org_.resizeNoInit( 2 * settings.uedgeIds.tsize ); left_.resizeNoInit( 2 * settings.uedgeIds.tsize ); @@ -1649,11 +1647,9 @@ bool MeshTopology::buildGridMesh( const GridSettings & settings, ProgressCallbac edgePerVertex_[v] = edgeRing[0].e; for ( int i = 0; i < edgeRing.size(); ++i ) { - OldHalfEdge he( noInit ); - he.next = i + 1 < edgeRing.size() ? edgeRing[i + 1].e : edgeRing[0].e; - he.prev = i > 0 ? edgeRing[i - 1].e : edgeRing.back().e; const auto e = edgeRing[i].e; - edges_[e] = he; + next_[e] = i + 1 < edgeRing.size() ? edgeRing[i + 1].e : edgeRing[0].e; + prev_[e] = i > 0 ? edgeRing[i - 1].e : edgeRing.back().e; org_[e] = v; left_[e] = edgeRing[i].f; } @@ -1747,7 +1743,7 @@ void MeshTopology::computeAllFromEdges_() validFaces_.resize( maxValidFace + 1 ); numValidFaces_ = 0; - for ( EdgeId e{0}; e < edges_.size(); ++e ) + for ( EdgeId e{0}; e < next_.size(); ++e ) { if ( org_[e].valid() ) { @@ -1849,7 +1845,7 @@ void MeshTopology::addPartByMask( const MeshTopology & from, const FaceBitSet * } } - const EdgeId firstNewEdge = edges_.endId(); + const EdgeId firstNewEdge = next_.endId(); EdgeId nextNewEdge = firstNewEdge; auto copyEdge = [&]( UndirectedEdgeId fromUe ) { @@ -1985,27 +1981,21 @@ void MeshTopology::addPartByMask( const MeshTopology & from, const FaceBitSet * auto e = fromContour[j]; auto e1 = thisContour[j]; - { - assert ( fromMappedEdges.test( e.undirected() ) ); - assert( !left( e1 ) ); - OldHalfEdge & toHe = edges_[e1]; - const OldHalfEdge & fromHe = from.edges_[e]; - toHe.next = mapEdge( emap, flipOrientation ? fromHe.prev : fromHe.next ); - assert( toHe.next ); - if ( auto left = flipOrientation ? from.left_[e.sym()] : from.left_[e] ) - left_[e1] = getAt( fmap, left ); - } - { - OldHalfEdge & toHe = edges_[e1.sym()]; - const OldHalfEdge & fromHe = from.edges_[e.sym()]; - toHe.prev = mapEdge( emap, flipOrientation ? fromHe.next : fromHe.prev ); - assert( toHe.prev ); - } + assert ( fromMappedEdges.test( e.undirected() ) ); + assert( !left( e1 ) ); + next_[e1] = mapEdge( emap, flipOrientation ? from.prev_[e] : from.next_[e] ); + assert( next_[e1] ); + if ( auto left = flipOrientation ? from.left_[e.sym()] : from.left_[e] ) + left_[e1] = getAt( fmap, left ); + + prev_[e1.sym()] = mapEdge( emap, flipOrientation ? from.next_[e.sym()] : from.prev_[e.sym()] ); + assert( prev_[e1.sym()] ); } } // translate edge records - edges_.resizeNoInit( nextNewEdge ); + next_.resizeNoInit( nextNewEdge ); + prev_.resizeNoInit( nextNewEdge ); org_.resizeNoInit( nextNewEdge ); left_.resizeNoInit( nextNewEdge ); BitSetParallelFor( fromCopiedEdges, [&]( UndirectedEdgeId fromUe ) @@ -2058,8 +2048,8 @@ void MeshTopology::addPartByMask( const MeshTopology & from, const FaceBitSet * assert( !left( ePr ) ); assert( !right( eNx ) ); - edges_[ePr].next = eNx; - edges_[eNx].prev = ePr; + next_[ePr] = eNx; + prev_[eNx] = ePr; } #ifndef NDEBUG From 2fde897897224521605cc7ca3996a322e6b3b57b Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sun, 26 Oct 2025 11:30:54 +0300 Subject: [PATCH 19/23] edges4 --- source/MRMesh/MRMeshTopology.cpp | 95 ++++++++++++-------------------- 1 file changed, 36 insertions(+), 59 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index 09b59af434b7..48a61d44a7b8 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -2128,7 +2128,40 @@ void MeshTopology::pack( const PackMapping & map ) { MR_TIMER; - // translate origin vertices + { + Timer t( "next" ); + Vector tmpNext; + tmpNext.resizeNoInit( 2 * map.e.tsize ); + ParallelFor( 0_ue, UndirectedEdgeId( undirectedEdgeSize() ), [&] ( UndirectedEdgeId oldUe ) + { + UndirectedEdgeId newUe = map.e.b[oldUe]; + if ( !newUe ) + return; + EdgeId oldE = oldUe; + EdgeId newE = newUe; + tmpNext[newE] = getAt( map.e.b, next_[oldE] ); + tmpNext[newE.sym()] = getAt( map.e.b, next_[oldE.sym()] ); + } ); + next_ = std::move( tmpNext ); + } + + { + Timer t( "prev" ); + Vector tmpPrev; + tmpPrev.resizeNoInit( 2 * map.e.tsize ); + ParallelFor( 0_ue, UndirectedEdgeId( undirectedEdgeSize() ), [&] ( UndirectedEdgeId oldUe ) + { + UndirectedEdgeId newUe = map.e.b[oldUe]; + if ( !newUe ) + return; + EdgeId oldE = oldUe; + EdgeId newE = newUe; + tmpPrev[newE] = getAt( map.e.b, prev_[oldE] ); + tmpPrev[newE.sym()] = getAt( map.e.b, prev_[oldE.sym()] ); + } ); + prev_ = std::move( tmpPrev ); + } + { Timer t( "org" ); Vector tmpOrg; @@ -2146,7 +2179,6 @@ void MeshTopology::pack( const PackMapping & map ) org_ = std::move( tmpOrg ); } - // translate left faces { Timer t( "left" ); Vector tmpLeft; @@ -2164,62 +2196,6 @@ void MeshTopology::pack( const PackMapping & map ) left_ = std::move( tmpLeft ); } - Vector, UndirectedEdgeId> tmp( map.e.tsize ); - auto translateHalfEdge = [&]( const OldHalfEdge & he ) - { - OldHalfEdge res; - res.next = getAt( map.e.b, he.next ); - res.prev = getAt( map.e.b, he.prev ); - return res; - }; - - // translate even half-edges - tbb::parallel_for( tbb::blocked_range( 0_ue, UndirectedEdgeId( undirectedEdgeSize() ) ), - [&]( const tbb::blocked_range & range ) - { - for ( auto oldUe = range.begin(); oldUe < range.end(); ++oldUe ) - { - auto newUe = map.e.b[oldUe]; - if ( !newUe ) - continue; - tmp[ newUe ] = translateHalfEdge( edges_[ EdgeId{oldUe} ] ); - } - } ); - // copy back even half-edges - tbb::parallel_for( tbb::blocked_range( 0_ue, UndirectedEdgeId( map.e.tsize ) ), - [&]( const tbb::blocked_range & range ) - { - for ( auto newUe = range.begin(); newUe < range.end(); ++newUe ) - { - edges_[ EdgeId{newUe} ] = tmp[ newUe ]; - } - } ); - - // translate odd half-edges - tbb::parallel_for( tbb::blocked_range( 0_ue, UndirectedEdgeId( undirectedEdgeSize() ) ), - [&]( const tbb::blocked_range & range ) - { - for ( auto oldUe = range.begin(); oldUe < range.end(); ++oldUe ) - { - auto newUe = map.e.b[oldUe]; - if ( !newUe ) - continue; - tmp[ newUe ] = translateHalfEdge( edges_[ EdgeId{oldUe}.sym() ] ); - } - } ); - // copy back odd half-edges - tbb::parallel_for( tbb::blocked_range( 0_ue, UndirectedEdgeId( map.e.tsize ) ), - [&]( const tbb::blocked_range & range ) - { - for ( auto newUe = range.begin(); newUe < range.end(); ++newUe ) - { - edges_[ EdgeId{newUe}.sym() ] = tmp[ newUe ]; - } - } ); - - tmp = {}; - edges_.resize( 2 * map.e.tsize ); - Vector newEdgePerFace; newEdgePerFace.resizeNoInit( map.f.tsize ); tbb::parallel_for( tbb::blocked_range( 0_f, FaceId( faceSize() ) ), @@ -2336,7 +2312,8 @@ void MeshTopology::packMinMem( const PackMapping & map ) [&]( UndirectedEdgeId ue ) { return getEdge_( ue ); }, [&]( UndirectedEdgeId ue, const EdgeRecord& r ) { setEdge_( ue, r ); } ); - edges_.resize( 2 * map.e.tsize ); + next_.resize( 2 * map.e.tsize ); + prev_.resize( 2 * map.e.tsize ); org_.resize( 2 * map.e.tsize ); left_.resize( 2 * map.e.tsize ); From eef07395cc591091753bff45c1184f6980784b92 Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sun, 26 Oct 2025 11:35:54 +0300 Subject: [PATCH 20/23] edges5 --- source/MRMesh/MRMeshTopology.cpp | 38 ++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index 48a61d44a7b8..ba3b16f054e2 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -2325,17 +2325,18 @@ void MeshTopology::packMinMem( const PackMapping & map ) { for ( auto ue = range.begin(); ue < range.end(); ++ue ) { - auto translateHalfEdge = [&]( OldHalfEdge & he ) - { - he.next = getAt( map.e.b, he.next ); - he.prev = getAt( map.e.b, he.prev ); - }; const EdgeId e0( ue ); const EdgeId e1( e0.sym() ); - translateHalfEdge( edges_[e0] ); - translateHalfEdge( edges_[e1] ); + + next_[e0] = getAt( map.e.b, next_[e0] ); + next_[e1] = getAt( map.e.b, next_[e1] ); + + prev_[e0] = getAt( map.e.b, prev_[e0] ); + prev_[e1] = getAt( map.e.b, prev_[e1] ); + org_[e0] = getAt( map.v.b, org_[e0] ); org_[e1] = getAt( map.v.b, org_[e1] ); + left_[e0] = getAt( map.f.b, left_[e0] ); left_[e1] = getAt( map.f.b, left_[e1] ); } @@ -2366,7 +2367,10 @@ void MeshTopology::write( std::ostream & s ) const { MR_TIMER; // write edges - auto numEdges = (std::uint32_t)edges_.size(); + assert( next_.size() == prev_.size() ); + assert( next_.size() == org_.size() ); + assert( next_.size() == left_.size() ); + auto numEdges = (std::uint32_t)next_.size(); s.write( (const char*)&numEdges, 4 ); Vector recs; @@ -2413,7 +2417,8 @@ Expected MeshTopology::read( std::istream & s, ProgressCallback callback ) } : callback ) ) return unexpectedOperationCanceled(); - edges_.resizeNoInit( numEdges ); + next_.resizeNoInit( numEdges ); + prev_.resizeNoInit( numEdges ); org_.resizeNoInit( numEdges ); left_.resizeNoInit( numEdges ); ParallelFor( recs, [&] ( EdgeId e ) @@ -2462,6 +2467,11 @@ bool MeshTopology::checkValidity( ProgressCallback cb, bool allVerts ) const MR_TIMER; #define CHECK(x) { assert(x); if (!(x)) return false; } + + CHECK( next_.size() == prev_.size() ); + CHECK( next_.size() == org_.size() ); + CHECK( next_.size() == left_.size() ); + CHECK( updateValids_ ); const auto vSize = edgePerVertex_.size(); CHECK( vSize == validVerts_.size() ) @@ -2475,12 +2485,12 @@ bool MeshTopology::checkValidity( ProgressCallback cb, bool allVerts ) const failed.store( true, std::memory_order_relaxed ); }; - auto result = ParallelFor( edges_, [&] (const EdgeId& e) + auto result = ParallelFor( next_, [&] (const EdgeId& e) { if ( failed.load( std::memory_order_relaxed ) ) return; - parCheck( edges_[edges_[e].next].prev == e ); - parCheck( edges_[edges_[e].prev].next == e ); + parCheck( prev_[next_[e]] == e ); + parCheck( next_[prev_[e]] == e ); auto v = org_[e]; if ( allVerts && !isLoneEdge( e ) ) parCheck( v.valid() ); @@ -2513,7 +2523,7 @@ bool MeshTopology::checkValidity( ProgressCallback cb, bool allVerts ) const if ( edgePerVertex_[v].valid() ) { parCheck( validVerts_.test( v ) ); - parCheck( edgePerVertex_[v] < edges_.size() ); + parCheck( edgePerVertex_[v] < next_.size() ); parCheck( org_[edgePerVertex_[v]] == v ); ++myValidVerts; for ( EdgeId e : orgRing( *this, v ) ) @@ -2543,7 +2553,7 @@ bool MeshTopology::checkValidity( ProgressCallback cb, bool allVerts ) const if ( edgePerFace_[f].valid() ) { parCheck( validFaces_.test( f ) ); - parCheck( edgePerFace_[f] < edges_.size() ); + parCheck( edgePerFace_[f] < next_.size() ); parCheck( left_[edgePerFace_[f]] == f ); ++myValidFaces; for ( EdgeId e : leftRing( *this, f ) ) From 3e577d07dda3f8c5a935fd4440ce3a27be20708c Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sun, 26 Oct 2025 11:37:51 +0300 Subject: [PATCH 21/23] old style --- source/MRMesh/MRMeshTopology.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.h b/source/MRMesh/MRMeshTopology.h index af9cd8bb3c54..7f19f2deea0f 100644 --- a/source/MRMesh/MRMeshTopology.h +++ b/source/MRMesh/MRMeshTopology.h @@ -532,11 +532,13 @@ class MeshTopology MRMESH_API bool checkValidity( ProgressCallback cb = {}, bool allVerts = true ) const; private: + friend class MeshTopologyDiff; /// computes from edges_ all remaining fields: \n /// 1) numValidVerts_, 2) validVerts_, 3) edgePerVertex_, /// 4) numValidFaces_, 5) validFaces_, 6) edgePerFace_ MRMESH_API void computeAllFromEdges_(); +private: /// sets new origin to the full origin ring including this edge, without updating edgePerVertex_ table void setOrg_( EdgeId a, VertId v ); @@ -551,7 +553,10 @@ class MeshTopology VertId org; ///< vertex at the origin of the edge FaceId left; ///< face at the left of the edge - bool operator ==( const HalfEdgeRecord& b ) const = default; + bool operator ==( const HalfEdgeRecord& b ) const + { + return next == b.next && prev == b.prev && org == b.org && left == b.left; + } HalfEdgeRecord() noexcept = default; explicit HalfEdgeRecord( NoInit ) noexcept : next( noInit ), prev( noInit ), org( noInit ), left( noInit ) {} HalfEdgeRecord( EdgeId n, EdgeId p, VertId o, FaceId l ) : next( n ), prev( p ), org( o ), left( l ) {} @@ -591,8 +596,6 @@ class MeshTopology int numValidFaces_ = 0; ///< the number of valid elements in edgePerFace_ or set bits in validFaces_ bool updateValids_ = true; ///< if false, validVerts_, validFaces_, numValidVerts_, numValidFaces_ are not updated - - friend class MeshTopologyDiff; }; template From d3dcff7ce5c830624951a5b218e362dc24b43319 Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sun, 26 Oct 2025 11:44:28 +0300 Subject: [PATCH 22/23] diff --- source/MRMesh/MRMeshTopologyDiff.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/source/MRMesh/MRMeshTopologyDiff.cpp b/source/MRMesh/MRMeshTopologyDiff.cpp index efbde4ca869b..11df16292797 100644 --- a/source/MRMesh/MRMeshTopologyDiff.cpp +++ b/source/MRMesh/MRMeshTopologyDiff.cpp @@ -9,10 +9,10 @@ MeshTopologyDiff::MeshTopologyDiff( const MeshTopology & from, const MeshTopolog { MR_TIMER; - toEdgesSize_ = to.edges_.size(); + toEdgesSize_ = to.edgeSize(); for ( EdgeId e{0}; e < toEdgesSize_; ++e ) { - if ( e >= from.edges_.size() || from.getHalfEdge_( e ) != to.getHalfEdge_( e ) ) + if ( e >= from.edgeSize() || from.getHalfEdge_( e ) != to.getHalfEdge_( e ) ) changedEdges_[e] = to.getHalfEdge_( e ); } } @@ -21,13 +21,16 @@ void MeshTopologyDiff::applyAndSwap( MeshTopology & m ) { MR_TIMER; - auto mEdgesSize = m.edges_.size(); + auto mEdgesSize = m.edgeSize(); // remember edges_ being deleted from m for ( EdgeId e{toEdgesSize_}; e < mEdgesSize; ++e ) { changedEdges_[e] = m.getHalfEdge_( e ); } - m.edges_.resize( toEdgesSize_ ); + m.next_.resize( toEdgesSize_ ); + m.prev_.resize( toEdgesSize_ ); + m.org_.resize( toEdgesSize_ ); + m.left_.resize( toEdgesSize_ ); // swap common edges_ and delete edges_ for vertices missing in original m (that will be next target) for ( auto it = changedEdges_.begin(); it != changedEdges_.end(); ) { From c052fbce442de7cd72233d47305732e319183c6d Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sun, 26 Oct 2025 12:23:54 +0300 Subject: [PATCH 23/23] fix pack + test --- source/MRMesh/MRMeshTopology.cpp | 9 +++++---- source/MRTest/MRMeshDecimateTests.cpp | 4 ++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/source/MRMesh/MRMeshTopology.cpp b/source/MRMesh/MRMeshTopology.cpp index ba3b16f054e2..19ac4c14468b 100644 --- a/source/MRMesh/MRMeshTopology.cpp +++ b/source/MRMesh/MRMeshTopology.cpp @@ -2128,11 +2128,12 @@ void MeshTopology::pack( const PackMapping & map ) { MR_TIMER; + const UndirectedEdgeId oldLastUe( undirectedEdgeSize() ); // undirectedEdgeSize() is computed from next_ { Timer t( "next" ); Vector tmpNext; tmpNext.resizeNoInit( 2 * map.e.tsize ); - ParallelFor( 0_ue, UndirectedEdgeId( undirectedEdgeSize() ), [&] ( UndirectedEdgeId oldUe ) + ParallelFor( 0_ue, oldLastUe, [&] ( UndirectedEdgeId oldUe ) { UndirectedEdgeId newUe = map.e.b[oldUe]; if ( !newUe ) @@ -2149,7 +2150,7 @@ void MeshTopology::pack( const PackMapping & map ) Timer t( "prev" ); Vector tmpPrev; tmpPrev.resizeNoInit( 2 * map.e.tsize ); - ParallelFor( 0_ue, UndirectedEdgeId( undirectedEdgeSize() ), [&] ( UndirectedEdgeId oldUe ) + ParallelFor( 0_ue, oldLastUe, [&] ( UndirectedEdgeId oldUe ) { UndirectedEdgeId newUe = map.e.b[oldUe]; if ( !newUe ) @@ -2166,7 +2167,7 @@ void MeshTopology::pack( const PackMapping & map ) Timer t( "org" ); Vector tmpOrg; tmpOrg.resizeNoInit( 2 * map.e.tsize ); - ParallelFor( 0_ue, UndirectedEdgeId( undirectedEdgeSize() ), [&] ( UndirectedEdgeId oldUe ) + ParallelFor( 0_ue, oldLastUe, [&] ( UndirectedEdgeId oldUe ) { UndirectedEdgeId newUe = map.e.b[oldUe]; if ( !newUe ) @@ -2183,7 +2184,7 @@ void MeshTopology::pack( const PackMapping & map ) Timer t( "left" ); Vector tmpLeft; tmpLeft.resizeNoInit( 2 * map.e.tsize ); - ParallelFor( 0_ue, UndirectedEdgeId( undirectedEdgeSize() ), [&] ( UndirectedEdgeId oldUe ) + ParallelFor( 0_ue, oldLastUe, [&] ( UndirectedEdgeId oldUe ) { UndirectedEdgeId newUe = map.e.b[oldUe]; if ( !newUe ) diff --git a/source/MRTest/MRMeshDecimateTests.cpp b/source/MRTest/MRMeshDecimateTests.cpp index 8c094ed6eba2..8dab2656275f 100644 --- a/source/MRTest/MRMeshDecimateTests.cpp +++ b/source/MRTest/MRMeshDecimateTests.cpp @@ -29,6 +29,10 @@ TEST( MRMesh, MeshDecimate ) ASSERT_NE(regionSaved, regionForDecimation); ASSERT_GT(decimateResults.vertsDeleted, 0); ASSERT_GT(decimateResults.facesDeleted, 0); + + ASSERT_TRUE(meshCylinder.topology.checkValidity()); + meshCylinder.packOptimally(); + ASSERT_TRUE(meshCylinder.topology.checkValidity()); } TEST( MRMesh, MeshDecimateParallel )