Skip to content

Commit

Permalink
WatershedGraph improvements, e.g. special "outside" basin (#1559)
Browse files Browse the repository at this point in the history
* WatershedGraph: special "outside" basin

* parentBasin_
  • Loading branch information
Fedr authored Aug 24, 2023
1 parent 7c4ff94 commit 2667c64
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 40 deletions.
6 changes: 6 additions & 0 deletions source/MRMesh/MRGraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,15 @@ class Graph
/// returns all valid vertices in the graph
[[nodiscard]] const VertBitSet & validVerts() const { return validVerts_; }

/// returns true if given vertex is valid
[[nodiscard]] bool valid( VertId v ) const { return validVerts_.test( v ); }

/// returns all valid edges in the graph
[[nodiscard]] const EdgeBitSet & validEdges() const { return validEdges_; }

/// returns true if given edge is valid
[[nodiscard]] bool valid( EdgeId e ) const { return validEdges_.test( e ); }

/// returns all edges adjacent to given vertex
[[nodiscard]] const Neighbours & neighbours( VertId v ) const { return neighboursPerVertex_[v]; }

Expand Down
104 changes: 70 additions & 34 deletions source/MRMesh/MRWatershedGraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "MRphmap.h"
#include "MRRingIterator.h"
#include "MRBitSetParallelFor.h"
#include "MRParallelFor.h"
#include "MRTimer.h"

namespace std
Expand Down Expand Up @@ -37,9 +38,14 @@ WatershedGraph::WatershedGraph( const MeshTopology & topology, const VertScalars
basins_.clear();
bds_.clear();

outsideId_ = Graph::VertId( numBasins );
++numBasins;
basins_.resize( numBasins );
Graph::NeighboursPerVertex neighboursPerVertex( numBasins );
ufBasins_.reset( numBasins );
parentBasin_.clear();
parentBasin_.reserve( numBasins );
for ( Graph::VertId v( 0 ); v < numBasins; ++v )
parentBasin_.push_back( v );
Graph::EndsPerEdge endsPerEdge;

HashMap<Graph::EndVertices, Graph::EdgeId> neiBasins2edge;
Expand All @@ -52,9 +58,7 @@ WatershedGraph::WatershedGraph( const MeshTopology & topology, const VertScalars
for ( auto e : orgRing( topology, v ) )
{
auto l = topology.left( e );
if ( !l )
continue;
Graph::VertId basin( face2basin[l] );
Graph::VertId basin( l ? face2basin[l] : outsideId_ );
if ( !basin0 )
{
basin0 = basin;
Expand All @@ -79,16 +83,12 @@ WatershedGraph::WatershedGraph( const MeshTopology & topology, const VertScalars
for ( auto e : orgRing( topology, v ) )
{
auto l = topology.left( e );
if ( !l )
continue;
const Graph::VertId basinL( face2basin[l] );
const Graph::VertId basinL( l ? face2basin[l] : outsideId_ );
auto & infoL = basins_[basinL];
if ( h < getHeightAt( infoL.lowestVert ) )
infoL.lowestVert = v;
auto r = topology.right( e );
if ( !r )
continue;
const Graph::VertId basinR( face2basin[r] );
const Graph::VertId basinR( r ? face2basin[r] : outsideId_ );
if ( basinL == basinR )
continue;

Expand All @@ -114,6 +114,27 @@ WatershedGraph::WatershedGraph( const MeshTopology & topology, const VertScalars
graph_.construct( std::move( neighboursPerVertex ), std::move( endsPerEdge ) );
}

Graph::VertId WatershedGraph::getRootBasin( Graph::VertId v ) const
{
assert( v );
for (;;)
{
auto p = parentBasin_[v];
if ( p == v )
return v;
v = p;
}
}

void WatershedGraph::setParentsToRoots()
{
MR_TIMER
ParallelFor( parentBasin_, [&]( Graph::VertId v )
{
parentBasin_[v] = getRootBasin( v );
} );
}

MRMESH_API std::pair<Graph::EdgeId, float> WatershedGraph::findLowestBd() const
{
MR_TIMER
Expand All @@ -122,6 +143,8 @@ MRMESH_API std::pair<Graph::EdgeId, float> WatershedGraph::findLowestBd() const
for ( auto ei : graph_.validEdges() )
{
const auto ends = graph_.ends( ei );
if ( ends.v0 == outsideId_ || ends.v1 == outsideId_ )
continue;
const auto l0 = getHeightAt( basins_[ends.v0].lowestVert );
const auto l1 = getHeightAt( basins_[ends.v1].lowestVert );
const auto le = getHeightAt( bds_[ei].lowestVert );
Expand All @@ -136,36 +159,47 @@ MRMESH_API std::pair<Graph::EdgeId, float> WatershedGraph::findLowestBd() const
return { lowestEdge, lowestLevel };
}

void WatershedGraph::mergeViaBd( Graph::EdgeId bd )
Graph::VertId WatershedGraph::merge( Graph::VertId v0, Graph::VertId v1 )
{
MR_TIMER
if ( !bd )
return;

const auto ends = graph_.ends( bd );
const auto [vremnant, united] = ufBasins_.unite( ends.v0, ends.v1 );
assert( united );
const auto vdead = ends.otherEnd( vremnant );
{
if ( getHeightAt( basins_[vdead].lowestVert ) < getHeightAt( basins_[vremnant].lowestVert ) )
basins_[vremnant].lowestVert = basins_[vdead].lowestVert;
}
graph_.merge( vremnant, vdead, [&]( Graph::EdgeId eremnant, Graph::EdgeId edead )
assert( v0 && v1 );
assert( v0 != outsideId_ && v1 != outsideId_ );
assert( graph_.valid( v0 ) && graph_.valid( v1 ) );
if ( v0 == v1 )
return v0;

assert( parentBasin_[v1] == v1 );
parentBasin_[v1] = v0;
if ( getHeightAt( basins_[v1].lowestVert ) < getHeightAt( basins_[v0].lowestVert ) )
basins_[v0].lowestVert = basins_[v1].lowestVert;

graph_.merge( v0, v1, [&]( Graph::EdgeId eremnant, Graph::EdgeId edead )
{
if ( getHeightAt( bds_[edead].lowestVert ) < getHeightAt( bds_[eremnant].lowestVert ) )
bds_[eremnant].lowestVert = bds_[edead].lowestVert;
} );
return v0;
}

Graph::VertId WatershedGraph::mergeViaBd( Graph::EdgeId bd )
{
assert( bd );
const auto ends = graph_.ends( bd );
return merge( ends.v0, ends.v1 );
}

FaceBitSet WatershedGraph::getBasinFaces( Graph::VertId basin ) const
{
MR_TIMER
FaceBitSet res( topology_.faceSize() );
const auto & roots = ufBasins_.roots();
assert( basin == roots[basin] );
FaceBitSet res;
if ( basin == outsideId_ )
return res;
res.resize( topology_.faceSize() );
assert( graph_.valid( basin ) );
assert( basin == parentBasin_[basin] );
BitSetParallelForAll( res, [&]( FaceId f )
{
if ( basin == roots[Graph::VertId( face2iniBasin_[f] )] )
if ( basin == getRootBasin( Graph::VertId( face2iniBasin_[f] ) ) )
res.set( f );
} );
return res;
Expand All @@ -174,12 +208,15 @@ FaceBitSet WatershedGraph::getBasinFaces( Graph::VertId basin ) const
FaceBitSet WatershedGraph::getBasinFacesBelowLevel( Graph::VertId basin, float waterLevel ) const
{
MR_TIMER
FaceBitSet res( topology_.faceSize() );
const auto & roots = ufBasins_.roots();
assert( basin == roots[basin] );
FaceBitSet res;
if ( basin == outsideId_ )
return res;
res.resize( topology_.faceSize() );
assert( graph_.valid( basin ) );
assert( basin == parentBasin_[basin] );
BitSetParallelForAll( res, [&]( FaceId f )
{
if ( basin != roots[Graph::VertId( face2iniBasin_[f] )] )
if ( basin != getRootBasin( Graph::VertId( face2iniBasin_[f] ) ) )
return;
VertId vs[3];
topology_.getTriVerts( f, vs );
Expand All @@ -197,7 +234,6 @@ UndirectedEdgeBitSet WatershedGraph::getInterBasinEdges() const
{
MR_TIMER
UndirectedEdgeBitSet res( topology_.undirectedEdgeSize() );
const auto & roots = ufBasins_.roots();
BitSetParallelForAll( res, [&]( UndirectedEdgeId ue )
{
auto l = topology_.left( ue );
Expand All @@ -206,8 +242,8 @@ UndirectedEdgeBitSet WatershedGraph::getInterBasinEdges() const
auto r = topology_.right( ue );
if ( !r )
return;
const auto lBasin = roots[Graph::VertId( face2iniBasin_[l] )];
const auto rBasin = roots[Graph::VertId( face2iniBasin_[r] )];
const auto lBasin = getRootBasin( Graph::VertId( face2iniBasin_[l] ) );
const auto rBasin = getRootBasin( Graph::VertId( face2iniBasin_[r] ) );
if ( lBasin != rBasin )
res.set( ue );
} );
Expand Down
32 changes: 26 additions & 6 deletions source/MRMesh/MRWatershedGraph.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#pragma once

#include "MRGraph.h"
#include "MRUnionFind.h"
#include <cfloat>

namespace MR
Expand All @@ -11,13 +10,13 @@ namespace MR
class WatershedGraph
{
public:
// associated with each vertex in graph
/// associated with each vertex in graph
struct BasinInfo
{
VertId lowestVert; ///< in the whole basin
};

// associated with each edge in graph
/// associated with each edge in graph
struct BdInfo
{
VertId lowestVert; ///< on this boundary
Expand All @@ -33,18 +32,34 @@ class WatershedGraph
/// returns underlying graph where each basin is a vertex
[[nodiscard]] const Graph & graph() const { return graph_; }

/// returns the current number of basins (excluding special "outside" basin)
[[nodiscard]] int numBasins() const { return (int)graph_.validVerts().count() - 1; }

/// returns data associated with given basin
[[nodiscard]] const BasinInfo & basinInfo( Graph::VertId v ) const { return basins_[v]; }

/// returns data associated with given boundary between basins
[[nodiscard]] const BdInfo & bdInfo( Graph::EdgeId e ) const { return bds_[e]; }

/// returns special "basin" representing outside areas of the mesh
[[nodiscard]] Graph::VertId outsideId() const { return outsideId_; }

/// for valid basin return its id; for invalid basin returns the id of basin it was merged in
[[nodiscard]] MRMESH_API Graph::VertId getRootBasin( Graph::VertId v ) const;

/// replaces parent of each basin with its computed root;
/// this speeds up following calls to getRootBasin()
MRMESH_API void setParentsToRoots();

/// finds the lowest boundary between basins and its height, which is defined
/// as the minimal different between lowest boundary point and lowest point in a basin
[[nodiscard]] MRMESH_API std::pair<Graph::EdgeId, float> findLowestBd() const;

/// merge two basins sharing given boundary
MRMESH_API void mergeViaBd( Graph::EdgeId bd );
/// merges basin v1 into basin v0, v1 is deleted after that, returns v0
MRMESH_API Graph::VertId merge( Graph::VertId v0, Graph::VertId v1 );

/// merges two basins sharing given boundary, returns remaining basin
MRMESH_API Graph::VertId mergeViaBd( Graph::EdgeId bd );

/// returns the mesh faces of given basin
[[nodiscard]] MRMESH_API FaceBitSet getBasinFaces( Graph::VertId basin ) const;
Expand All @@ -63,7 +78,12 @@ class WatershedGraph
Graph graph_;
Vector<BasinInfo, Graph::VertId> basins_;
Vector<BdInfo, Graph::EdgeId> bds_;
mutable UnionFind<Graph::VertId> ufBasins_;

/// special "basin" representing outside areas of the mesh
Graph::VertId outsideId_;

/// for valid basin, parent is the same; for invalid basin, sequence of parents point on valid root basin
Vector<Graph::VertId, Graph::VertId> parentBasin_;
};

} //namespace MR

0 comments on commit 2667c64

Please sign in to comment.