Skip to content

Commit 5b3d287

Browse files
authored
DM-based Radius Compensation fixes (#4084)
* DM-based Radius Compensation fixes * update * remove debug output * use fixed tolerance
1 parent 7893875 commit 5b3d287

2 files changed

Lines changed: 39 additions & 22 deletions

File tree

source/MRMesh/MRRadiusCompensation.cpp

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class RadiusCompensator
3232
// prepares raw distance map
3333
Expected<void> init();
3434

35-
// finds tool locations for each pixel and summary compensation volume for each
35+
// finds tool locations for each pixel and summary compensation cost for each
3636
Expected<void> calcCompensations();
3737

3838
// creates compensation distance map
@@ -61,8 +61,8 @@ class RadiusCompensator
6161
// if callback returns false iterations stops
6262
void iteratePixelsInRadius_( const Vector2i& pixelCoord, const std::function<bool( const Vector2i& )>& callback );
6363

64-
// calculates summary compensation volume for given tool location
65-
float sumCompensationVolume_( const Vector3f& toolCenter );
64+
// calculates summary compensation cost for given tool location
65+
float sumCompensationCost_( const Vector3f& toolCenter );
6666

6767
Mesh& mesh_;
6868
CompensateRadiusParams params_;
@@ -77,9 +77,13 @@ class RadiusCompensator
7777
Vector2i pixelsInRadius_;
7878

7979
std::vector<Vector3f> toolCenters_; // per pixel
80-
std::vector<std::pair<float,int>> volumes_; // <voulme, id> per pixel (this array will be sorted, thats why we need id as value and not only as key)
80+
// cost is (approx compensation Volume)/(approx compensation Projected Area) - less is better
81+
std::vector<std::pair<float,int>> costs_; // <cost, id> per pixel (this array will be sorted, thats why we need id as value and not only as key)
8182

8283
DistanceMap compensatedDm_;
84+
85+
// tolerance of distance map height for "touching" compensations
86+
static constexpr float cDMTolearance = 1e-5f;
8387
};
8488

8589
Expected<void> RadiusCompensator::init()
@@ -113,20 +117,20 @@ Expected<void> RadiusCompensator::calcCompensations()
113117
{
114118
MR_TIMER;
115119
toolCenters_.resize( dm_.size(), Vector3f::diagonal( FLT_MAX ) );
116-
volumes_.resize( dm_.size(), std::make_pair( -1.0f, -1 ) );
120+
costs_.resize( dm_.size(), std::make_pair( -1.0f, -1 ) );
117121
bool keepGoing = ParallelFor( size_t( 0 ), dm_.size(), [&] ( size_t i )
118122
{
119123
auto pixelCoord = dm_.toPos( i );
120124
auto& toolCenter = toolCenters_[i];
121125
toolCenter = findToolCenterAtPixel_(pixelCoord);
122126
if ( toolCenter.x != FLT_MAX )
123-
volumes_[i] = std::make_pair( sumCompensationVolume_( toolCenter ), int( i ) );
127+
costs_[i] = std::make_pair( sumCompensationCost_( toolCenter ), int( i ) );
124128
}, subprogress( params_.callback, 0.05f, 0.15f ) );
125129

126130
if ( !keepGoing )
127131
return unexpectedOperationCanceled();
128132

129-
tbb::parallel_sort( volumes_.begin(), volumes_.end(), [] ( const auto& l, const auto& r )
133+
tbb::parallel_sort( costs_.begin(), costs_.end(), [] ( const auto& l, const auto& r )
130134
{
131135
return l.first < r.first;
132136
} );
@@ -143,12 +147,13 @@ Expected<void> RadiusCompensator::compensateDistanceMap()
143147
auto sb = subprogress( params_.callback, 0.2f, 0.5f );
144148
compensatedDm_ = DistanceMap( dm_.resX(), dm_.resY() );
145149
size_t i = 0;
146-
for ( auto [volume, cId] : volumes_ )
150+
const float cTolerance = params_.toolRadius * cDMTolearance;
151+
for ( auto [cost, cId] : costs_ )
147152
{
148-
if ( ( ++i % 1024 == 0 ) && !reportProgress( sb, float( i ) / volumes_.size() ) )
153+
if ( ( ++i % 1024 == 0 ) && !reportProgress( sb, float( i ) / costs_.size() ) )
149154
return unexpectedOperationCanceled();
150155

151-
if ( cId < 0 || volume < 0.0f )
156+
if ( cId < 0 || cost < 0.0f )
152157
continue;
153158

154159
const auto& toolCenter = toolCenters_[cId];
@@ -169,10 +174,10 @@ Expected<void> RadiusCompensator::compensateDistanceMap()
169174
iteratePixelsInRadius_( pixelCoord, [&] ( const Vector2i& pixeli )->bool
170175
{
171176
auto realValue = dm_.getValue( pixeli.x, pixeli.y );
172-
auto& value = compensatedDm_.getValue( pixeli.x, pixeli.y );
173-
auto compValue = calcCompensatedHeightAtPixel_( pixeli, toolCenter );
174-
if ( compValue > value && compValue >= realValue )
175-
value = compValue;
177+
auto& compValue = compensatedDm_.getValue( pixeli.x, pixeli.y );
178+
auto newCompValue = calcCompensatedHeightAtPixel_( pixeli, toolCenter );
179+
if ( newCompValue > compValue && newCompValue + cTolerance >= realValue )
180+
compValue = std::max( newCompValue, realValue );
176181
return true;
177182
} );
178183
}
@@ -208,6 +213,7 @@ Expected<void> RadiusCompensator::applyCompensation()
208213
}
209214

210215
// fix inverted faces (undercuts on original mesh)
216+
for ( int iFlipped = 0; iFlipped < 10; ++iFlipped ) // repeat until no flipped faces left, 10 - max iters
211217
{
212218
// only fix flipped areas
213219
auto sumDirArea = Vector3f( mesh_.dirArea( faceRegion_ ).normalized() ); // we only care about direction
@@ -221,6 +227,9 @@ Expected<void> RadiusCompensator::applyCompensation()
221227
expand( mesh_.topology, flippedFaces, 4 );
222228
flippedFaces &= *faceRegion_;
223229

230+
if ( flippedFaces.none() )
231+
break;
232+
224233
auto equalizingVertRegion = getInnerVerts( mesh_.topology, flippedFaces );
225234
assert( ( equalizingVertRegion & bounds ).none() );
226235
MeshEqualizeTriAreasParams etParams;
@@ -254,7 +263,6 @@ Expected<void> RadiusCompensator::applyCompensation()
254263
vertRegion_.reset( v );
255264
}, subprogress( params_.callback, 0.65f, 0.8f ) );
256265

257-
258266
if ( vertRegion_.any() )
259267
positionVertsSmoothlySharpBd( mesh_, vertRegion_ );
260268

@@ -270,7 +278,7 @@ Expected<void> RadiusCompensator::postprocessMesh()
270278
auto edgeBounds = findRegionBoundaryUndirectedEdgesInsideMesh( mesh_.topology, *faceRegion_ );
271279
RemeshSettings rParams;
272280
rParams.finalRelaxIters = 2;
273-
rParams.targetEdgeLen = mesh_.averageEdgeLength();
281+
rParams.targetEdgeLen = params_.remeshTargetEdgeLength <= 0.0f ? mesh_.averageEdgeLength() : params_.remeshTargetEdgeLength;
274282
rParams.region = params_.region;
275283
rParams.notFlippable = &edgeBounds;
276284
rParams.progressCallback = subprogress( params_.callback, 0.8f, 1.0f );
@@ -387,19 +395,24 @@ void RadiusCompensator::iteratePixelsInRadius_( const Vector2i& pixelCoord, cons
387395
}
388396
}
389397

390-
float RadiusCompensator::sumCompensationVolume_( const Vector3f& toolCenter )
398+
float RadiusCompensator::sumCompensationCost_( const Vector3f& toolCenter )
391399
{
392-
float sumVolume = 0.0f;
400+
double sumVolume = 0.0;
401+
double sumArea = 0.0;
393402
auto toolPixel = Vector2i( to2dim( toDmXf_( toolCenter ) ) );
403+
const float cTolerance = cDMTolearance * params_.toolRadius;
394404
iteratePixelsInRadius_( toolPixel, [&] ( const Vector2i& pixelCoord )->bool
395405
{
396406
auto value = dm_.getValue( pixelCoord.x, pixelCoord.y );
397407
auto height = calcCompensatedHeightAtPixel_( pixelCoord, toolCenter );
398-
if ( height > value )
399-
sumVolume += ( height - value );
408+
if ( height + cTolerance >= value )
409+
{
410+
sumVolume += std::max( height - value, 0.0f );
411+
sumArea += 1.0;
412+
}
400413
return true;
401414
} );
402-
return sumVolume;
415+
return sumArea == 0.0 ? -1.0f : float( sumVolume / sumArea );
403416
}
404417

405418
Expected<void> compensateRadius( Mesh& mesh, const CompensateRadiusParams& params )

source/MRMesh/MRRadiusCompensation.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,18 @@ struct CompensateRadiusParams
1717
float toolRadius{ 0.0f };
1818

1919
/// resolution of distance map that is used for compensation
20-
Vector2i distanceMapResolution = Vector2i( 100, 100 );
20+
Vector2i distanceMapResolution = Vector2i( 150, 150 );
2121

2222
/// region of the mesh that will be compensated
2323
/// it should not contain closed components
2424
/// it is updated during algorithm
2525
/// also please note that boundaries of the region are fixed
2626
FaceBitSet* region{ nullptr };
2727

28+
/// this value will be used for post-process re-meshing
29+
/// value less or equal to zero will use average mesh edge length
30+
float remeshTargetEdgeLength{ -1.0f };
31+
2832
ProgressCallback callback;
2933
};
3034

0 commit comments

Comments
 (0)