Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Other way to get valid hole contours in flat filling #3386

Merged
merged 3 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
58 changes: 51 additions & 7 deletions source/MRMesh/MR2DContoursTriangulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,9 @@ class SweepLineQueue
const HolesVertIds* holesVertId = nullptr,
bool abortWhenIntersect = false,
WindingMode mode = WindingMode::NonZero,
bool needOutline = false // if set do not do real triangulation, just marks inside faces as present,
bool needOutline = false, // if set do not do real triangulation, just marks inside faces as present,
// also does not merge same vertices
std::vector<EdgePath>* outBoundaries = nullptr // optional out EdgePaths that corresponds to initial contours
);

size_t vertSize() const { return tp_.vertSize(); }
Expand All @@ -146,6 +147,8 @@ class SweepLineQueue
bool needOutline_ = false;
// if set fails on first found intersection
bool abortWhenIntersect_ = false;
// optional out EdgePaths that corresponds to initial contours
std::vector<EdgePath>* outBoundaries_ = nullptr;
// make base mesh only containing input contours as edge loops
void initMeshByContours_( const Contours2d& contours );
// merge same points on base mesh
Expand Down Expand Up @@ -278,9 +281,11 @@ SweepLineQueue::SweepLineQueue(
const HolesVertIds* holesVertId,
bool abortWhenIntersect,
WindingMode mode,
bool needOutline ) :
bool needOutline,
std::vector<EdgePath>* outBoundaries ) :
needOutline_{ needOutline },
abortWhenIntersect_{ abortWhenIntersect },
outBoundaries_{ outBoundaries },
windingMode_{ mode }
{
Box3d box;
Expand Down Expand Up @@ -898,16 +903,30 @@ void SweepLineQueue::initMeshByContours_( const Contours2d& contours )
}
}
}

int boundId = -1;
if ( outBoundaries_ )
outBoundaries_->resize( contours.size() );

int firstVert = 0;
for ( const auto& c : contours )
{
++boundId;
if ( c.size() <= 3 )
continue;

int size = int( c.size() ) - 1;

if ( outBoundaries_ )
( *outBoundaries_ )[boundId].resize( size );

for ( int i = 0; i < size; ++i )
tp_.setOrg( tp_.makeEdge(), VertId( firstVert + i ) );
{
auto newEdgeId = tp_.makeEdge();
tp_.setOrg( newEdgeId, VertId( firstVert + i ) );
if ( outBoundaries_ )
( *outBoundaries_ )[boundId][i] = newEdgeId;
}
const auto& edgePerVert = tp_.edgePerVertex();
for ( int i = 0; i < size; ++i )
tp_.splice( edgePerVert[VertId( firstVert + i )], edgePerVert[VertId( firstVert + ( ( i + int( size ) - 1 ) % size ) )].sym() );
Expand Down Expand Up @@ -1037,6 +1056,31 @@ void SweepLineQueue::removeMultipleAfterMerge_()
}
assert( multiplesFromThis.size() > 1 );

if ( outBoundaries_ )
{
auto& bunds = *outBoundaries_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bunds -> bounds?

auto getBoundId = [&bunds] ( EdgeId e )->std::pair<int, int>
{
int i0 = 0;
auto i1 = int( e.undirected() );
while ( i1 >= bunds[i0].size() )
{
++i0;
i1 -= int( bunds[i0].size() );
}
assert( e.undirected() == bunds[i0][i1].undirected() );
return { i0,i1 };
};
auto [bf0, bf1] = getBoundId( multiplesFromThis.front() );
auto bf = bunds[bf0][bf1];
for ( int i = 1; i < multiplesFromThis.size(); ++i )
{
auto [bi0, bi1] = getBoundId( multiplesFromThis[i] );
auto& bi = bunds[bi0][bi1];
bi = multiplesFromThis[i] == bi ? bf : bf.sym();
}
}

auto& edgeInfo = windingInfo_[multiplesFromThis.front().undirected()];
edgeInfo.windingModifier = 1;
bool uniqueIsOdd = int( multiplesFromThis.front() ) & 1;
Expand Down Expand Up @@ -1298,18 +1342,18 @@ Mesh triangulateContours( const Contours2f& contours, const HolesVertIds* holeVe
return triangulateContours( contsd, holeVertsIds );
}

std::optional<Mesh> triangulateDisjointContours( const Contours2d& contours, const HolesVertIds* holeVertsIds /*= nullptr*/ )
std::optional<Mesh> triangulateDisjointContours( const Contours2d& contours, const HolesVertIds* holeVertsIds /*= nullptr*/, std::vector<EdgePath>* outBoundaries /*= nullptr*/ )
{
if ( contours.empty() )
return Mesh();
SweepLineQueue triangulator( contours, holeVertsIds, true );
SweepLineQueue triangulator( contours, holeVertsIds, true, WindingMode::NonZero, false, outBoundaries );
return triangulator.run();
}

std::optional<Mesh> triangulateDisjointContours( const Contours2f& contours, const HolesVertIds* holeVertsIds /*= nullptr*/ )
std::optional<Mesh> triangulateDisjointContours( const Contours2f& contours, const HolesVertIds* holeVertsIds /*= nullptr*/, std::vector<EdgePath>* outBoundaries /*= nullptr*/ )
{
const auto contsd = copyContours<Contours2d>( contours );
return triangulateDisjointContours( contsd, holeVertsIds );
return triangulateDisjointContours( contsd, holeVertsIds, outBoundaries );
}

}
Expand Down
5 changes: 3 additions & 2 deletions source/MRMesh/MR2DContoursTriangulation.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,11 @@ MRMESH_API Mesh triangulateContours( const Contours2f& contours, const HolesVert
* @brief triangulate 2d contours
* only closed contours are allowed (first point of each contour should be the same as last point of the contour)
* @param holeVertsIds if set merge only points with same vertex id, otherwise merge all points with same coordinates
* @param outBoundaries optional output EdgePaths that correspond to initial contours
* @return std::optional<Mesh> : if some contours intersect return false, otherwise return created mesh
*/
MRMESH_API std::optional<Mesh> triangulateDisjointContours( const Contours2d& contours, const HolesVertIds* holeVertsIds = nullptr );
MRMESH_API std::optional<Mesh> triangulateDisjointContours( const Contours2f& contours, const HolesVertIds* holeVertsIds = nullptr );
MRMESH_API std::optional<Mesh> triangulateDisjointContours( const Contours2d& contours, const HolesVertIds* holeVertsIds = nullptr, std::vector<EdgePath>* outBoundaries = nullptr );
MRMESH_API std::optional<Mesh> triangulateDisjointContours( const Contours2f& contours, const HolesVertIds* holeVertsIds = nullptr, std::vector<EdgePath>* outBoundaries = nullptr );

}
}
25 changes: 14 additions & 11 deletions source/MRMesh/MRFillContours2D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "MRPlane3.h"
#include "MRGTest.h"
#include <limits>
#include "MRMeshSave.h"

namespace MR
{
Expand Down Expand Up @@ -117,8 +118,10 @@ VoidOrErrStr fillContours2D( Mesh& mesh, const std::vector<EdgeId>& holeRepresen

auto holeVertIds = std::make_unique<PlanarTriangulation::HolesVertIds>(
PlanarTriangulation::findHoleVertIdsByHoleEdges( mesh.topology, paths ) );

std::vector<EdgePath> newPaths;
// make patch surface
auto fillResult = PlanarTriangulation::triangulateDisjointContours( contours2f, holeVertIds.get() );
auto fillResult = PlanarTriangulation::triangulateDisjointContours( contours2f, holeVertIds.get(), &newPaths );
holeVertIds.reset();
if ( !fillResult )
return unexpected( "Cannot triangulate contours with self-intersections" );
Expand All @@ -129,22 +132,22 @@ VoidOrErrStr fillContours2D( Mesh& mesh, const std::vector<EdgeId>& holeRepresen
for ( auto& point : patchMeshPoints )
point = planeXf( point );

// make
auto newPaths = findLeftBoundary( patchMesh.topology );

// check that patch surface borders size equal original mesh borders size
if ( paths.size() != newPaths.size() )
return unexpected( "Patch surface borders size different from original mesh borders size" );

// need to rotate to min edge to be consistent with original paths (for addPartByMask)
for ( auto& newPath : newPaths )
std::rotate( newPath.begin(), std::min_element( newPath.begin(), newPath.end() ), newPath.end() );
std::sort( newPaths.begin(), newPaths.end(), [] ( const EdgeLoop& l, const EdgeLoop& r ) { return l[0] < r[0]; } );

for ( int i = 0; i < paths.size(); ++i )
std::vector<int> invalidHoles;
invalidHoles.reserve( newPaths.size() );
for ( int i = int( paths.size() ) - 1; i >= 0; --i )
{
if ( paths[i].size() != newPaths[i].size() )
return unexpected( "Patch surface borders size different from original mesh borders size" );

// invalid holes, we could either skip them or fill other way or return error
if ( newPaths[i].empty() || patchMesh.topology.right( newPaths[i].front() ) )
{
paths.erase( paths.begin() + i );
newPaths.erase( newPaths.begin() + i );
}
}

// move patch surface border points to original position (according original mesh)
Expand Down
Loading