Skip to content

Commit c26183e

Browse files
authored
FillHoles: smoothBd parameter (#4916)
1 parent 25b7394 commit c26183e

File tree

6 files changed

+52
-20
lines changed

6 files changed

+52
-20
lines changed

source/MRDotNet/MRMeshFillHole.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ public struct FillHoleParams
3939
*/
4040
public FillHoleMetric Metric = new FillHoleMetric();
4141

42+
/** If true, hole filling will minimize the sum of metrics including boundary edges,
43+
* where one triangle was present before hole filling, and another is added during hole filling.
44+
* This makes boundary edges same smooth as inner edges of the patch.
45+
* If false, edge metric will not be applied to boundary edges, and the patch tends to make a sharper turn there.
46+
*/
47+
public bool SmoothBd = true;
48+
4249
/// If not null accumulate new faces
4350
public FaceBitSet? OutNewFaces = null;
4451

@@ -94,6 +101,7 @@ public FillHoleNicelyParams() { }
94101
internal struct MRFillHoleParams
95102
{
96103
public IntPtr metric = IntPtr.Zero;
104+
public byte smoothBd = 1;
97105
public IntPtr outNewFaces = IntPtr.Zero;
98106
public MultipleEdgesResolveMode multipleEdgesResolveMode = MultipleEdgesResolveMode.Simple;
99107
public byte makeDegenerateBand = 0;
@@ -140,6 +148,7 @@ unsafe public static void FillHole(ref Mesh mesh, EdgeId a, FillHoleParams param
140148
{
141149
MRFillHoleParams mrParam;
142150
mrParam.metric = parameters.Metric.mrMetric_;
151+
mrParam.smoothBd = parameters.SmoothBd ? (byte)1 : (byte)0;
143152
mrParam.outNewFaces = parameters.OutNewFaces?.bs_ ?? IntPtr.Zero;
144153
mrParam.multipleEdgesResolveMode = parameters.MultipleEdgesResolveMode;
145154
mrParam.makeDegenerateBand = parameters.MakeDegenerateBand ? (byte)1 : (byte)0;
@@ -166,6 +175,7 @@ unsafe public static FaceBitSet FillHoleNicely(ref Mesh mesh, EdgeId holeEdge, F
166175
{
167176
MRFillHoleNicelyParams mrParam;
168177
mrParam.triangulationParams.metric = parameters.triangulationParams.Metric.mrMetric_;
178+
mrParam.triangulationParams.smoothBd = parameters.triangulationParams.SmoothBd ? (byte)1 : (byte)0;
169179
mrParam.triangulationParams.outNewFaces = parameters.triangulationParams.OutNewFaces?.bs_ ?? IntPtr.Zero;
170180
mrParam.triangulationParams.multipleEdgesResolveMode = parameters.triangulationParams.MultipleEdgesResolveMode;
171181
mrParam.triangulationParams.makeDegenerateBand = parameters.triangulationParams.MakeDegenerateBand ? (byte)1 : (byte)0;
@@ -200,6 +210,7 @@ unsafe public static void FillHoles(ref Mesh mesh, List<EdgeId> edges, FillHoleP
200210
{
201211
MRFillHoleParams mrParam;
202212
mrParam.metric = parameters.Metric.mrMetric_;
213+
mrParam.smoothBd = parameters.SmoothBd ? (byte)1 : (byte)0;
203214
mrParam.outNewFaces = parameters.OutNewFaces?.bs_ ?? IntPtr.Zero;
204215
mrParam.multipleEdgesResolveMode = parameters.MultipleEdgesResolveMode;
205216
mrParam.makeDegenerateBand = parameters.MakeDegenerateBand ? (byte)1 : (byte)0;

source/MRMesh/MRFillHoleNicely.cpp

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -111,19 +111,22 @@ FaceBitSet fillHoleNicely( Mesh & mesh,
111111
// exclude boundary vertices from positionVertsSmoothly(), since it tends to move them inside the mesh
112112
auto vertsForSmoothing = newVerts - mesh.topology.findBdVerts( nullptr, &newVerts );
113113
positionVertsSmoothlySharpBd( mesh, vertsForSmoothing );
114-
positionVertsSmoothly( mesh, vertsForSmoothing, settings.edgeWeights, settings.vmass );
115-
if ( settings.naturalSmooth )
114+
if ( settings.triangulateParams.smoothBd )
116115
{
117-
auto undirectedEdgeBitSet = findRegionBoundaryUndirectedEdgesInsideMesh( mesh.topology, newFaces );
118-
auto incidentVerts = getIncidentVerts( mesh.topology, undirectedEdgeBitSet );
119-
expand( mesh.topology, incidentVerts, 5 );
120-
shrink( mesh.topology, incidentVerts, 2 );
121-
MeshComponents::excludeFullySelectedComponents( mesh, incidentVerts );
122-
if ( incidentVerts.any() )
116+
positionVertsSmoothly( mesh, vertsForSmoothing, settings.edgeWeights, settings.vmass );
117+
if ( settings.naturalSmooth )
123118
{
124-
vertsForSmoothing = incidentVerts - mesh.topology.findBdVerts( nullptr, &incidentVerts );
125-
positionVertsSmoothlySharpBd( mesh, vertsForSmoothing );
126-
positionVertsSmoothly( mesh, vertsForSmoothing, settings.edgeWeights, settings.vmass );
119+
auto undirectedEdgeBitSet = findRegionBoundaryUndirectedEdgesInsideMesh( mesh.topology, newFaces );
120+
auto incidentVerts = getIncidentVerts( mesh.topology, undirectedEdgeBitSet );
121+
expand( mesh.topology, incidentVerts, 5 );
122+
shrink( mesh.topology, incidentVerts, 2 );
123+
MeshComponents::excludeFullySelectedComponents( mesh, incidentVerts );
124+
if ( incidentVerts.any() )
125+
{
126+
vertsForSmoothing = incidentVerts - mesh.topology.findBdVerts( nullptr, &incidentVerts );
127+
positionVertsSmoothlySharpBd( mesh, vertsForSmoothing );
128+
positionVertsSmoothly( mesh, vertsForSmoothing, settings.edgeWeights, settings.vmass );
129+
}
127130
}
128131
}
129132
}

source/MRMesh/MRMeshFillHole.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ void getOptimalSteps( std::vector<unsigned>& optimalSteps, unsigned start, unsig
111111

112112
// finds best candidate among all given steps
113113
void getTriangulationWeights( const MeshTopology& topology, const NewEdgesMap& map, const EdgePath& loop,
114-
const FillHoleMetric& metrics,
114+
const FillHoleMetric& metrics, bool smoothBd,
115115
const std::vector<unsigned>& optimalStepsCache, WeightedConn& processedConn )
116116
{
117117
for ( unsigned s = 0; s < optimalStepsCache.size(); ++s )
@@ -139,7 +139,7 @@ void getTriangulationWeights( const MeshTopology& topology, const NewEdgesMap& m
139139
VertId leftVert;
140140
if ( abConn.hasPrev() )
141141
leftVert = topology.org( loop[abConn.prevA] );
142-
else if ( topology.right( loop[processedConn.a] ) )
142+
else if ( smoothBd && topology.right( loop[processedConn.a] ) )
143143
leftVert = topology.dest( topology.prev( loop[processedConn.a] ) );
144144

145145
if ( leftVert )
@@ -151,7 +151,7 @@ void getTriangulationWeights( const MeshTopology& topology, const NewEdgesMap& m
151151
VertId rightVert;
152152
if ( bcConn.hasPrev() )
153153
rightVert = topology.org( loop[bcConn.prevA] );
154-
else if ( topology.right( loop[v] ) )
154+
else if ( smoothBd && topology.right( loop[v] ) )
155155
rightVert = topology.dest( topology.prev( loop[v] ) );
156156

157157
if ( rightVert )
@@ -179,7 +179,7 @@ using MapPatch = std::vector<MapPatchElement>;
179179

180180
// this function go backward by given triangulation and tries to fix multiple edges
181181
// return false if triangulation has multiple edges that cannot be fixed
182-
bool removeMultipleEdgesFromTriangulation( const MeshTopology& topology, const NewEdgesMap& map, const EdgePath& loop, const FillHoleMetric& metricRef,
182+
bool removeMultipleEdgesFromTriangulation( const MeshTopology& topology, const NewEdgesMap& map, const EdgePath& loop, const FillHoleMetric& metricRef, bool smoothBd,
183183
WeightedConn start, int maxPolygonSubdivisions, MapPatch& mapPatch )
184184
{
185185
MR_TIMER;
@@ -232,7 +232,7 @@ bool removeMultipleEdgesFromTriangulation( const MeshTopology& topology, const N
232232
if ( optimalStepsCache.empty() )
233233
return false;
234234
WeightedConn newPrev{ start.a,start.b,DBL_MAX,0 };
235-
getTriangulationWeights( topology, map, loop, metricRef, optimalStepsCache, newPrev ); // find better among steps
235+
getTriangulationWeights( topology, map, loop, metricRef, smoothBd, optimalStepsCache, newPrev ); // find better among steps
236236
if ( !newPrev.hasPrev() || !map[start.a][newPrev.prevA].hasPrev() || !map[start.prevA][newPrev.b].hasPrev() )
237237
return false;
238238
start.prevA = newPrev.prevA;
@@ -659,7 +659,7 @@ HoleFillPlan HoleFillPlanner::run( const Mesh& mesh, EdgeId a0, const FillHolePa
659659
sameEdgeExists( mesh.topology, aCur, cCur ) )
660660
return;
661661
getOptimalSteps( optimalSteps, ( i + 1 ) % loopEdgesCounter, steps, loopEdgesCounter, params.maxPolygonSubdivisions );
662-
getTriangulationWeights( mesh.topology, newEdgesMap_, edgeMap_, metrics, optimalSteps, current ); // find better among steps
662+
getTriangulationWeights( mesh.topology, newEdgesMap_, edgeMap_, metrics, params.smoothBd, optimalSteps, current ); // find better among steps
663663
};
664664
if ( parallelProcessing )
665665
{
@@ -689,13 +689,13 @@ HoleFillPlan HoleFillPlanner::run( const Mesh& mesh, EdgeId a0, const FillHolePa
689689
VertId leftVert;
690690
if ( newEdgesMap_[i][cIndex].hasPrev() )
691691
leftVert = mesh.topology.org( edgeMap_[newEdgesMap_[i][cIndex].prevA] );
692-
else if ( mesh.topology.right( edgeMap_[i] ) )
692+
else if ( params.smoothBd && mesh.topology.right( edgeMap_[i] ) )
693693
leftVert = mesh.topology.dest( mesh.topology.prev( edgeMap_[i] ) );
694694

695695
VertId rightVert;
696696
if ( newEdgesMap_[cIndex][i].hasPrev() )
697697
rightVert = mesh.topology.org( edgeMap_[newEdgesMap_[cIndex][i].prevA] );
698-
else if ( mesh.topology.right( edgeMap_[cIndex] ) )
698+
else if ( params.smoothBd && mesh.topology.right( edgeMap_[cIndex] ) )
699699
rightVert = mesh.topology.dest( mesh.topology.prev( edgeMap_[cIndex] ) );
700700

701701
if ( leftVert && rightVert )
@@ -706,7 +706,7 @@ HoleFillPlan HoleFillPlanner::run( const Mesh& mesh, EdgeId a0, const FillHolePa
706706
}
707707
if ( weight < finConn.weight &&
708708
( params.multipleEdgesResolveMode != FillHoleParams::MultipleEdgesResolveMode::Strong || // try to fix multiple if needed
709-
removeMultipleEdgesFromTriangulation( mesh.topology, newEdgesMap_, edgeMap_, metrics, newEdgesMap_[cIndex][i], params.maxPolygonSubdivisions, cachedMapPatch_ ) ) )
709+
removeMultipleEdgesFromTriangulation( mesh.topology, newEdgesMap_, edgeMap_, metrics, params.smoothBd, newEdgesMap_[cIndex][i], params.maxPolygonSubdivisions, cachedMapPatch_ ) ) )
710710
{
711711
savedMapPatch_ = cachedMapPatch_;
712712
finConn = newEdgesMap_[cIndex][i];

source/MRMesh/MRMeshFillHole.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,17 @@ struct FillHoleParams
2929
* \sa \ref FillHoleMetric
3030
*/
3131
FillHoleMetric metric;
32+
33+
/** If true, hole filling will minimize the sum of metrics including boundary edges,
34+
* where one triangle was present before hole filling, and another is added during hole filling.
35+
* This makes boundary edges same smooth as inner edges of the patch.
36+
* If false, edge metric will not be applied to boundary edges, and the patch tends to make a sharper turn there.
37+
*/
38+
bool smoothBd{ true };
39+
3240
/// If not nullptr accumulate new faces
3341
FaceBitSet* outNewFaces{ nullptr };
42+
3443
/** If Strong makes additional efforts to avoid creating multiple edges,
3544
* in some rare cases it is not possible (cases with extremely bad topology),
3645
* if you faced one try to use \ref MR::duplicateMultiHoleVertices before \ref MR::fillHole

source/MRMeshC/MRMeshFillHole.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ MRFillHoleParams mrFillHoleParamsNew( void )
2020
static const FillHoleParams def;
2121
return {
2222
.metric = auto_cast( &def.metric ),
23+
.smoothBd = def.smoothBd,
2324
.outNewFaces = auto_cast( def.outNewFaces ),
2425
.multipleEdgesResolveMode = auto_cast( def.multipleEdgesResolveMode ),
2526
.makeDegenerateBand = def.makeDegenerateBand,
@@ -37,6 +38,7 @@ void mrFillHole( MRMesh* mesh_, MREdgeId a_, const MRFillHoleParams* params_ )
3738
{
3839
params = {
3940
.metric = params_->metric ? *auto_cast( params_->metric ) : FillHoleMetric {},
41+
.smoothBd = params_->smoothBd,
4042
.outNewFaces = auto_cast( params_->outNewFaces ),
4143
.multipleEdgesResolveMode = auto_cast( params_->multipleEdgesResolveMode ),
4244
.makeDegenerateBand = params_->makeDegenerateBand,

source/MRMeshC/MRMeshFillHole.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ typedef struct MRFillHoleParams
2727
*/
2828
const MRFillHoleMetric* metric;
2929

30+
/** If true, hole filling will minimize the sum of metrics including boundary edges,
31+
* where one triangle was present before hole filling, and another is added during hole filling.
32+
* This makes boundary edges same smooth as inner edges of the patch.
33+
* If false, edge metric will not be applied to boundary edges, and the patch tends to make a sharper turn there.
34+
*/
35+
bool smoothBd;
36+
3037
/// If not nullptr accumulate new faces
3138
MRFaceBitSet* outNewFaces;
3239

0 commit comments

Comments
 (0)