Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions source/MRMesh/MRMeshFillHole.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,27 @@ void executeHoleFillPlan( Mesh & mesh, EdgeId a0, HoleFillPlan & plan, FaceBitSe
assert( plan.numTris == int( fsz - fsz0 + ( f0 ? 1 : 0 ) ) );
}

bool isFillingMultipleEdgeFree( const MeshTopology & topology, const HoleFillPlan & plan )
{
if ( plan.items.empty() )
return true;

auto getVert = [&]( int code )
{
while ( code < 0 )
code = plan.items[ -(code+1) ].edgeCode1;
return topology.org( EdgeId( code ) );
};
for ( int i = 0; i < plan.items.size(); ++i )
{
auto v1 = getVert( plan.items[i].edgeCode1 );
auto v2 = getVert( plan.items[i].edgeCode2 );
if ( topology.findEdge( v1, v2 ) )
return false;
}
return true;
}

/// this class allows you to prepare fill plans for several holes with no new memory allocations on
/// second and subsequent calls
class HoleFillPlanner
Expand Down
13 changes: 9 additions & 4 deletions source/MRMesh/MRMeshFillHole.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,23 +173,28 @@ struct HoleFillPlan

/// prepares the plan how to triangulate the face or hole to the left of (e) (not filling it immediately),
/// several getHoleFillPlan can work in parallel
MRMESH_API HoleFillPlan getHoleFillPlan( const Mesh& mesh, EdgeId e, const FillHoleParams& params = {} );
[[nodiscard]] MRMESH_API HoleFillPlan getHoleFillPlan( const Mesh& mesh, EdgeId e, const FillHoleParams& params = {} );

/// prepares the plans how to triangulate the faces or holes, each given by a boundary edge (with filling target to the left),
/// the plans are prepared in parallel with minimal memory allocation compared to manual calling of several getHoleFillPlan(), but it can inefficient when some holes are very complex
MRMESH_API std::vector<HoleFillPlan> getHoleFillPlans( const Mesh& mesh, const std::vector<EdgeId>& holeRepresentativeEdges, const FillHoleParams& params = {} );
[[nodiscard]] MRMESH_API std::vector<HoleFillPlan> getHoleFillPlans( const Mesh& mesh, const std::vector<EdgeId>& holeRepresentativeEdges, const FillHoleParams& params = {} );

/// prepares the plan how to triangulate the planar face or planar hole to the left of (e) (not filling it immediately),
/// several getPlanarHoleFillPlan can work in parallel
MRMESH_API HoleFillPlan getPlanarHoleFillPlan( const Mesh& mesh, EdgeId e );
[[nodiscard]] MRMESH_API HoleFillPlan getPlanarHoleFillPlan( const Mesh& mesh, EdgeId e );

/// prepares the plans how to triangulate the planar faces or holes, each given by a boundary edge (with filling target to the left),
/// the plans are prepared in parallel with minimal memory allocation compared to manual calling of several getPlanarHoleFillPlan(), but it can inefficient when some holes are very complex
MRMESH_API std::vector<HoleFillPlan> getPlanarHoleFillPlans( const Mesh& mesh, const std::vector<EdgeId>& holeRepresentativeEdges );
[[nodiscard]] MRMESH_API std::vector<HoleFillPlan> getPlanarHoleFillPlans( const Mesh& mesh, const std::vector<EdgeId>& holeRepresentativeEdges );

/// quickly triangulates the face or hole to the left of (e) given the plan (quickly compared to fillHole function)
MRMESH_API void executeHoleFillPlan( Mesh & mesh, EdgeId a0, HoleFillPlan & plan, FaceBitSet * outNewFaces = nullptr );

/// returns true if executeHoleFillPlan() with the same topology and plan
/// does not introduce any edge with the same end-vertices as any existed edge in the mesh;
/// note: this function can be used for checking a fill plan that was generated before filling other holes
[[nodiscard]] MRMESH_API bool isFillingMultipleEdgeFree( const MeshTopology & topology, const HoleFillPlan & plan );

/** \brief Triangulates face of hole in mesh trivially\n
* \ingroup FillHoleGroup
*
Expand Down
75 changes: 75 additions & 0 deletions source/MRTest/MRFillHoleTests.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <MRMesh/MRMeshFillHole.h>
#include <MRMesh/MRMesh.h>
#include <MRMesh/MRMeshBuilder.h>
#include <MRMesh/MRMeshFixer.h>
#include <MRMesh/MRGTest.h>

namespace MR
Expand Down Expand Up @@ -117,4 +118,78 @@ TEST( MRMesh, makeBridgeEdge )
EXPECT_FALSE( x.valid() );
}

TEST( MRMesh, HoleFillPlan3 )
{
Mesh mesh;
const auto e = mesh.addSeparateEdgeLoop
( {
{ 0, -1, 0 },
{ 2, 0, 0 },
{ 0, 1, 0 }
} );

auto p0 = getPlanarHoleFillPlan( mesh, e );
EXPECT_EQ( p0.items.size(), 0 );
EXPECT_EQ( p0.numTris, 1 );

auto p1 = getPlanarHoleFillPlan( mesh, e.sym() );
EXPECT_EQ( p1.items.size(), 0 );
EXPECT_EQ( p1.numTris, 1 );

EXPECT_TRUE( isFillingMultipleEdgeFree( mesh.topology, p0 ) );
executeHoleFillPlan( mesh, e, p0 );
EXPECT_EQ( mesh.topology.numValidFaces(), 1 );
EXPECT_FALSE( mesh.topology.isClosed() );

EXPECT_TRUE( isFillingMultipleEdgeFree( mesh.topology, p1 ) );
executeHoleFillPlan( mesh, e.sym(), p1 );
EXPECT_EQ( mesh.topology.numValidFaces(), 2 );
EXPECT_TRUE( mesh.topology.isClosed() );
}

TEST( MRMesh, HoleFillPlan4 )
{
Mesh mesh;
const auto e = mesh.addSeparateEdgeLoop
( {
{ 0, -1, 0 },
{ 2, 0, 0 },
{ 0, 1, 0 },
{ -2, 0, 0 }
} );

auto p0 = getPlanarHoleFillPlan( mesh, e );
EXPECT_EQ( p0.items.size(), 1 );
EXPECT_EQ( p0.numTris, 2 );

auto p1 = getPlanarHoleFillPlan( mesh, e.sym() );
EXPECT_EQ( p1.items.size(), 1 );
EXPECT_EQ( p1.numTris, 2 );

EXPECT_TRUE( isFillingMultipleEdgeFree( mesh.topology, p0 ) );
executeHoleFillPlan( mesh, e, p0 );
EXPECT_EQ( mesh.topology.numValidFaces(), 2 );
EXPECT_FALSE( mesh.topology.isClosed() );
EXPECT_FALSE( hasMultipleEdges( mesh.topology ) );

auto mesh1 = mesh;

// independently produced plans can result in multiple edges after execution:
EXPECT_FALSE( isFillingMultipleEdgeFree( mesh.topology, p1 ) );
executeHoleFillPlan( mesh, e.sym(), p1 );
EXPECT_EQ( mesh.topology.numValidFaces(), 4 );
EXPECT_TRUE( mesh.topology.isClosed() );
EXPECT_TRUE( hasMultipleEdges( mesh.topology ) );

// if the plan to fill the second hole is prepared after the first hole is filled, no multiple edges appear
auto p11 = getPlanarHoleFillPlan( mesh1, e.sym() );
EXPECT_EQ( p11.items.size(), 1 );
EXPECT_EQ( p11.numTris, 2 );
EXPECT_TRUE( isFillingMultipleEdgeFree( mesh1.topology, p11 ) );
executeHoleFillPlan( mesh1, e.sym(), p11 );
EXPECT_EQ( mesh1.topology.numValidFaces(), 4 );
EXPECT_TRUE( mesh1.topology.isClosed() );
EXPECT_FALSE( hasMultipleEdges( mesh1.topology ) );
}

} //namespace MR
Loading