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

Add flip to surface mesh #1702

Merged
merged 24 commits into from
Oct 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3276d45
Add SurfaceMesh::isFlippable
JacquesOlivierLachaud Aug 26, 2023
d7f11db
Working on flips
JacquesOlivierLachaud Aug 26, 2023
2d4696d
Working on flips
JacquesOlivierLachaud Aug 26, 2023
905c419
Modify SurfaceMesh::myEdgeVertices data structure to authorize modifi…
JacquesOlivierLachaud Aug 26, 2023
88e24af
Rechanging edge data structure in SurfaceMesh
JacquesOlivierLachaud Aug 27, 2023
0b2125e
First version of flip
JacquesOlivierLachaud Aug 28, 2023
46cdcd7
Fix SurfaceMesh::computeEdges
JacquesOlivierLachaud Aug 28, 2023
324edd4
Complete testSurfaceMesh
JacquesOlivierLachaud Sep 1, 2023
d2e2f0c
Fix comments
JacquesOlivierLachaud Sep 3, 2023
0e6d639
Updating module doc
JacquesOlivierLachaud Sep 5, 2023
0607951
Update ChangeLog
JacquesOlivierLachaud Sep 5, 2023
16c5db1
Fix signed vs unsigned int comparisons
JacquesOlivierLachaud Sep 5, 2023
cfc4b8c
Fix unused types
JacquesOlivierLachaud Sep 5, 2023
b1106a7
Resize images and put white background
JacquesOlivierLachaud Sep 5, 2023
be3c89f
Fix testSurfaceMesh
JacquesOlivierLachaud Sep 6, 2023
61e420b
Add check of identical triangles as well as debug information in Surf…
JacquesOlivierLachaud Sep 8, 2023
6209730
Merge branch 'master' into AddFlipToSurfaceMesh
dcoeurjo Sep 9, 2023
0f69539
Fix doc according to comments
JacquesOlivierLachaud Sep 10, 2023
95b0752
Merge branch 'AddFlipToSurfaceMesh' of github.com:JacquesOlivierLacha…
JacquesOlivierLachaud Sep 10, 2023
8b33594
Fix stupid parse comment error
JacquesOlivierLachaud Sep 10, 2023
a1b50c6
Fix other comments
JacquesOlivierLachaud Sep 10, 2023
ee09158
Fix missing label
JacquesOlivierLachaud Sep 10, 2023
888101d
Update doc
JacquesOlivierLachaud Oct 20, 2023
66526eb
Merge branch 'master' into AddFlipToSurfaceMesh
dcoeurjo Oct 21, 2023
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
5 changes: 5 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
- Add a new method to store material information in obj file in MeshReader and MeshWriter.
(Bertrand Kerautret, [#1686](https://github.com/DGtal-team/DGtal/pull/1686))

- *Shapes*
- Add flips to SurfaceMesh data structure
(Jacques-Olivier Lachaud, [#1702](https://github.com/DGtal-team/DGtal/pull/1702))


- *Github*
- New `/builddoc` and `/fullbuild` commands on PR comments (David Coeurjolly,
[#1683](https://github.com/DGtal-team/DGtal/pull/1683))
Expand Down
2 changes: 1 addition & 1 deletion examples/polyscope-examples/tangency-explorer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,7 @@ int main( int argc, char* argv[] )

//Recasting to vector of vertices
positions = primalSurface->positions();

surfmesh = SurfMesh(positions.begin(),
positions.end(),
faces.begin(),
Expand Down
190 changes: 163 additions & 27 deletions src/DGtal/shapes/SurfaceMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,17 @@ namespace DGtal

/// Uses the positions of vertices to compute a normal vector to
/// each face of the mesh. It computes the barycenter,
/// triangulates implicitly the face to build the normal vector from the average of implicit triangle normals.
/// triangulates implicitly the face to build the normal vector
/// from the average of implicit triangle normals.
void computeFaceNormalsFromPositions();

/// Uses the positions of vertices to compute a normal vector to
/// the face \a f of the mesh. It computes the barycenter,
/// triangulates implicitly the face to build the normal vector
/// from the average of implicit triangle normals.
/// @param f any valid index of face.
void computeFaceNormalFromPositions( const Face f );

/// Uses the normals associated with vertices to compute a normal
/// vector to each face of the mesh. It simply averages the
/// normals at every incident vertex.
Expand Down Expand Up @@ -324,9 +332,10 @@ namespace DGtal
{ return myNeighborVertices[ v ]; }

/// @param e any edge
/// @return a const reference to the vector giving for edge \a e
/// its two vertices (as a pair (i,j), i<j).
const VertexPair& edgeVertices( Edge e ) const
/// @return a pair giving for edge \a e its two vertices (as a
/// pair (i,j), i<j).
/// @note if the edge is not valid, return {0,0}.
VertexPair edgeVertices( Edge e ) const
{ return myEdgeVertices[ e ]; }

/// @param e any edge
Expand Down Expand Up @@ -375,9 +384,10 @@ namespace DGtal
const std::vector< Vertices >& allNeighborVertices() const
{ return myNeighborVertices; }

/// @return a const reference to the vector giving for each edge
/// its two vertices (as a pair (i,j), i<j).
/// @note edges are sorted in increasing order.
/// @return a vector giving for each edge its two vertices (as a
/// pair (i,j), i<j).
/// @note Since 1.4, order is not significant (this is to allow
/// flip and modification in the surface mesh).
const std::vector< VertexPair >& allEdgeVertices() const
{ return myEdgeVertices; }

Expand Down Expand Up @@ -436,40 +446,40 @@ namespace DGtal
/// two left incident faces for instance.
Edges computeNonManifoldEdges() const;

///@return true if the boundary edges define a collection of
///manifold 1d polygonal curves (at most 2 adjecent edges per vertex).
///If checkClosed is set to true, we also check that all polygonal curves are closed.
/// @return true if the boundary edges define a collection of
/// manifold 1d polygonal curves (at most 2 adjecent edges per vertex).
/// If checkClosed is set to true, we also check that all polygonal curves are closed.
///
///The method returns false if the surface mesh has no boundary.
/// The method returns false if the surface mesh has no boundary.
///
///@param checkClosed if true, we check that each vertex has exactly two adejcent edges.
bool isBoundariesManifold(bool checkClosed = true) const
/// @param checkClosed if true, we check that each vertex has exactly two adejcent edges.
bool isBoundariesManifold( bool checkClosed = true ) const
{
//computes unordered list of boundary vertices
// computes unordered list of boundary vertices
std::map<Vertex,Vertices> adjacent;
auto MBE = this->computeManifoldBoundaryEdges();
if (MBE.size()==0) return false;
if ( MBE.size() == 0 ) return false;

for (auto e : MBE)
{
auto ij = this->edgeVertices(e);
adjacent[ij.first].push_back(ij.second);
if (adjacent[ij.first].size()>2) return false;
adjacent[ij.second].push_back(ij.first);
if (adjacent[ij.second].size()>2) return false;
adjacent[ ij.first ].push_back( ij.second );
if ( adjacent[ ij.first ] .size() > 2 ) return false;
adjacent[ ij.second ].push_back( ij.first );
if ( adjacent[ ij.second ].size() > 2 ) return false;
}
//we may check if all curves are closed.
if (checkClosed)
for(const auto &adj : adjacent)
if (adj.second.size() != 2) return false;
if ( checkClosed )
for ( const auto &adj : adjacent )
if ( adj.second.size() != 2 ) return false;
return true;
}

/// Extract the boundary of a surface mesh as a collection of sequences
/// of vertices. The boundaries must be 1d manifold polygonal curves.
/// @pre the boundaries must be manifold.
///
///@return a vector of polygonal simple curves (vector of vertices).
/// @return a vector of polygonal simple curves (vector of vertices).
std::vector<Vertices> computeManifoldBoundaryChains() const
{
std::vector<Vertices> boundaries;
Expand Down Expand Up @@ -852,6 +862,88 @@ namespace DGtal
/// intersection) or 1 (inclusion).
Scalar vertexInclusionRatio( RealPoint p, Scalar r, Index v ) const;

/// @}

//---------------------------------------------------------------------------
public:
/// @name Mesh editing services
/// @{

/// An edge is (topologically) flippable iff: (1) it does not lie
/// on the boundary, (2) it is bordered by two triangles, one that
/// to its right, one to its left, (3) the two other vertices of
/// the quad are not already neighbors, (4) the edge is not
/// bordered by the same two triangles, in opposite orientation.
///
/// @param e any edge.
/// @return 'true' if the edge \a e is topologically flippable.
///
/// @note Time complexity is O(1).
bool isFlippable( const Edge e ) const;

/// @pre `isFlippable( e )` must be true.
/// @param e any edge.
/// @return the two other vertices of the quadrilateral around the edge \a e.
VertexPair otherDiagonal( const Edge e ) const;

/**
Flip the edge \a e. Be careful that after the flip, this edge
index determines another edge, which is the other diagonal of
the quadrilateral having \a e as its diagonal.

\verbatim
l l
/ \ /|\
/ \ / | \
/ \ / | \
/ lf \ / | \
/ \ / | \
i --- e --- j ==> i lf e rf j if k < l otherwise rf and lf are swapped
\ / \ | /
\ rf / \ | /
\ / \ | /
\ / \ | /
\ / \|/
k k
\endverbatim

@param e any valid edge.

@param recompute_face_normals when 'true', recompute normals
of flipped faces with the positions of the vertices.

@pre the edge must be flippable, `isFlippable( e ) == true`

@post After the flip, the edge index \a e corresponds to the
index of the flipped edge (if you reflip it you get your
former configuration).

@note Time complexity is O(log n), due to the updating of
surrounding edges information.

@warning For performance reasons, The neighbor faces of each
face are not recomputed. One should call \ref computeNeighbors
to recompute them. However the neighbor vertices to each
vertex are recomputed.

@warning Vertex normals are not recomputed, but face normals
may be recomputed if asked for. The face normals are then the
geometric normals of triangles.
*/
void flip( const Edge e, bool recompute_face_normals = false );

/// @}

//---------------------------------------------------------------------------
public:
/// @name Look-up table computation services
/// @{

/// Computes neighboring information.
void computeNeighbors();
/// Computes edge information.
void computeEdges();

/// @}

// ----------------------- Interface --------------------------------------
Expand Down Expand Up @@ -887,6 +979,8 @@ namespace DGtal
std::vector< Vertices > myNeighborVertices;
/// For each edge, its two vertices
std::vector< VertexPair > myEdgeVertices;
/// For each vertex pair, its edge index.
std::map< VertexPair,Edge > myVertexPairEdge;
/// For each edge, its faces (one, two, or more if non manifold)
std::vector< Faces > myEdgeFaces;
/// For each edge, its faces to its right (zero if open, one, or more if
Expand All @@ -909,10 +1003,52 @@ namespace DGtal
// ------------------------- Internals ------------------------------------
protected:

/// Computes neighboring information.
void computeNeighbors();
/// Computes edge information.
void computeEdges();
/// Removes the index \a i from the vector \a v.
/// @param[inout] v a vector of indices
/// @param[in] i an index
void removeIndex( std::vector< Index >& v, Index i )
{
const std::size_t n = v.size();
for ( std::size_t j = 0; j < n; j++ )
if ( v[ j ] == i )
{
std::swap( v[ j ], v.back() );
v.resize( n - 1 );
return;
}
trace.error() << "[SurfaceMesh::removeIndex] Index " << i
<< " is not in vector:";
for ( auto e : v ) std::cerr << " " << e;
std::cerr << std::endl;
}

/// Replaces the index \a i with the index \a ri in the vector \a v.
/// @param[inout] v a vector of indices
/// @param[in] i an index
/// @param[in] ri an index
void replaceIndex( std::vector< Index >& v, Index i, Index ri )
{
const std::size_t n = v.size();
for ( std::size_t j = 0; j < n; j++ )
if ( v[ j ] == i )
{
v[ j ] = ri;
return;
}
trace.error() << "[SurfaceMesh::replaceIndex] Index " << i
<< " (subs=" << ri << ") is not in vector:";
for ( auto e : v ) std::cerr << " " << e;
std::cerr << std::endl;
}

/// Adds the index \a i to the vector \a v.
/// @param[inout] v a vector of indices
/// @param[in] i an index
void addIndex( std::vector< Index >& v, Index i )
{
v.push_back( i );
}


/// @return a random number between 0.0 and 1.0
static Scalar rand01()
Expand Down
Loading
Loading