Skip to content

Commit 02d22c5

Browse files
franck-ledouxfranck.ledoux
andauthored
366 surface mesh reorient (#372)
* Init surface reorientation * Surface reorientation implemented in 3D * update doc for surfaceReorient --------- Co-authored-by: franck.ledoux <[email protected]>
1 parent da8ada5 commit 02d22c5

File tree

7 files changed

+6817
-1
lines changed

7 files changed

+6817
-1
lines changed

docs/mkdocs/user-guide/igalgo.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,7 @@ This module provides some basic algorithms for the incidence graph (*ig*) data s
55
- Most important algorithms are relative to retrieving and building some boundary informations: boundaryOperator, boundaryExtractor.
66
- The *GridBuilder* class provides algoriths to build 2D and 3D structured grids for unit testing purposes mainly.
77
- The *THexBuilder* create a hexahedral mesh by splitting each tet of an input tetrahedral mesh.
8-
- The *MeshQualityCompute* go through all the mesh cells and assign variables depending on a set of selected quality criteria
8+
- The *MeshQualityCompute* go through all the mesh cells and assign variables depending on a set of selected quality criteria
9+
- The *SurfaceReorient* class reorient surface mesh (in 2D and 3D) in order to have all faces oriented in the same direction. If
10+
the mesh is split in different connex components, the orientation between different components can be different.
11+
In practice, the orientation is consistent in 2D.

igalgo/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ set(GMDS_INC
1212
inc/gmds/igalgo/BoundaryOperator.h
1313
inc/gmds/igalgo/BoundaryOperator2D.h
1414
inc/gmds/igalgo/GridBuilder.h
15+
inc/gmds/igalgo/SurfaceReorient.h
1516
inc/gmds/igalgo/THexBuilder.h)
1617
set(GMDS_SRC
1718
src/BoundaryExtractor3D.cpp
1819
src/BoundaryExtractor2D.cpp
1920
src/BoundaryOperator.cpp
2021
src/BoundaryOperator2D.cpp
2122
src/GridBuilder.cpp
23+
src/SurfaceReorient.cpp
2224
src/THexBuilder.cpp)
2325

2426
#==============================================================================
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*----------------------------------------------------------------------------*/
2+
#ifndef GMDS_SURFACE_REORIENT_H
3+
#define GMDS_SURFACE_REORIENT_H
4+
/*----------------------------------------------------------------------------*/
5+
#include <gmds/ig/Mesh.h>
6+
#include "GMDSIgAlgo_export.h"
7+
/*----------------------------------------------------------------------------*/
8+
#include <map>
9+
#include <vector>
10+
/*----------------------------------------------------------------------------*/
11+
namespace gmds{
12+
13+
/*----------------------------------------------------------------------------*/
14+
/** @class SurfaceReorient
15+
* @brief Class that provides an algorithm to reorient surfaces in a consistent
16+
* manner
17+
*/
18+
class GMDSIgAlgo_API SurfaceReorient
19+
{
20+
public:
21+
22+
/*------------------------------------------------------------------------*/
23+
/** @brief Constructor.
24+
*
25+
* @param Amesh the mesh to work on
26+
* @param ADim the grid dimension: 2 or 3 (default). Dimension of 2 means
27+
* that we know we work in the 2D plane. In this case the orientation is
28+
* consistent with FEM requirements.
29+
*/
30+
SurfaceReorient(Mesh* AMesh, const TInt ADim=3);
31+
32+
/*------------------------------------------------------------------------*/
33+
/** @brief Destructor. */
34+
virtual ~SurfaceReorient();
35+
36+
/*------------------------------------------------------------------------*/
37+
/** @brief Check if the mesh fits algorithm requirements, which are:
38+
* - 3D mesh with R, N and R2N
39+
* - 2D mesh with F, N and F2N
40+
*/
41+
bool isValid() const;
42+
/*------------------------------------------------------------------------*/
43+
/** @brief Performs the reorientation algorithm.
44+
* @return the number of faces that have been reoriented
45+
*/
46+
int execute();
47+
48+
private:
49+
int orient2d();
50+
int orient3d();
51+
bool orient2d(gmds::Face& AF);
52+
static TCoord isLeft(Node& AN1, Node& AN2, Node& AN3);
53+
private:
54+
/** a mesh */
55+
Mesh* m_mesh;
56+
/** problem dimension. 2 means we have a pure (X,Y,0) plan*/
57+
TInt m_dim;
58+
};
59+
/*----------------------------------------------------------------------------*/
60+
}
61+
/*----------------------------------------------------------------------------*/
62+
#endif //GMDS_SURFACE_REORIENT_H

igalgo/src/SurfaceReorient.cpp

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/*----------------------------------------------------------------------------*/
2+
#include <gmds/igalgo/SurfaceReorient.h>
3+
/*----------------------------------------------------------------------------*/
4+
#include <sstream>
5+
/*----------------------------------------------------------------------------*/
6+
using namespace gmds;
7+
/*----------------------------------------------------------------------------*/
8+
SurfaceReorient::SurfaceReorient(Mesh* AMesh, const TInt ADim)
9+
:m_mesh(AMesh), m_dim(ADim)
10+
{}
11+
/*----------------------------------------------------------------------------*/
12+
SurfaceReorient::~SurfaceReorient()
13+
{}
14+
/*----------------------------------------------------------------------------*/
15+
bool SurfaceReorient::isValid() const
16+
{
17+
if(m_dim==3)
18+
return (m_mesh->getModel()==(DIM3|F|N|F2N|N2F));
19+
else if(m_dim==2)
20+
return (m_mesh->getModel()==(DIM3|F|N|F2N) ||
21+
m_mesh->getModel()==(DIM2|F|N|F2N));
22+
23+
//dimension error
24+
return false;
25+
}
26+
/*----------------------------------------------------------------------------*/
27+
int SurfaceReorient::execute() {
28+
return (m_dim==2)?orient2d():orient3d();
29+
}
30+
/*----------------------------------------------------------------------------*/
31+
int SurfaceReorient::orient2d()
32+
{
33+
auto nb_reorientation =0;
34+
for(auto f_id:m_mesh->faces()){
35+
Face f = m_mesh->get<Face>(f_id);
36+
if (orient2d(f))
37+
nb_reorientation++;
38+
}
39+
return nb_reorientation;
40+
}
41+
/*------------------------------------------------------------------------*/
42+
bool SurfaceReorient::orient2d(Face& AF)
43+
{
44+
bool isReoriented = false;
45+
std::vector<Node> nodes = AF.get<Node>();
46+
TCoord orientation=0;
47+
if(AF.type()==GMDS_TRIANGLE) {
48+
orientation = isLeft(nodes[0],nodes[1],nodes[2]);
49+
}
50+
else {
51+
//find the rightmost lowest vertex of the polygon
52+
unsigned int index_min=0;
53+
TCoord x_min = nodes[0].X();
54+
TCoord y_min = nodes[0].Y();
55+
for(unsigned int
56+
i=0;i<nodes.size();i++)
57+
{
58+
if(nodes[i].Y()>y_min)
59+
continue;
60+
if(nodes[i].Y()==y_min) { // just as low
61+
if(nodes[i].X()<x_min) // and to left
62+
continue;
63+
}
64+
65+
index_min =i;
66+
x_min = nodes[i].X();
67+
y_min = nodes[i].Y();
68+
}
69+
70+
if(index_min==0)
71+
orientation = isLeft(nodes[nodes.size()-1],nodes[0],nodes[1]);
72+
else if (index_min==nodes.size()-1)
73+
orientation = isLeft(nodes[index_min-1],nodes[index_min],nodes[0]);
74+
else
75+
orientation = isLeft(nodes[index_min-1],nodes[index_min],nodes[index_min+1]);
76+
}
77+
if(orientation>0.0) // clockwise or degenerated (=0)
78+
{
79+
isReoriented= true;
80+
std::vector<Node> nodes_inv;
81+
nodes_inv.resize(nodes.size());
82+
auto node_size = nodes.size();
83+
for(unsigned int i=0;i<node_size;i++)
84+
nodes_inv[i] = nodes[node_size-1-i];
85+
AF.set<Node>(nodes_inv);
86+
}
87+
return isReoriented;
88+
}
89+
/*----------------------------------------------------------------------------*/
90+
TCoord SurfaceReorient::isLeft(Node& AN1, Node& AN2, Node& AN3)
91+
{
92+
return ( (AN2.X()-AN1.X()) * (AN3.Y()-AN1.Y()) -
93+
(AN3.X()-AN1.X()) * (AN2.Y()-AN1.Y()) );
94+
}
95+
/*----------------------------------------------------------------------------*/
96+
int SurfaceReorient::orient3d()
97+
{
98+
auto nb_reorientation =0;
99+
auto mark_done = m_mesh->newMark<Face>();
100+
101+
bool keep_working =true;
102+
while (keep_working) {
103+
//We look for the first unmarked face
104+
105+
auto it_face = m_mesh->faces_begin();
106+
while (m_mesh->isMarked<Face>(*it_face,mark_done) &&
107+
it_face!=m_mesh->faces_end()){
108+
++it_face;
109+
}
110+
if(it_face==m_mesh->faces_end()){
111+
keep_working=false;
112+
}
113+
if(keep_working) {
114+
// We take the first face as the orientation reference seed
115+
auto seed_id = *it_face;
116+
Face seed = m_mesh->get<Face>(seed_id);
117+
// And now, we go through all the faces in an advancing-front manner to
118+
// orient faces according to the seed
119+
std::vector<TCellID> front;
120+
front.push_back(seed.id());
121+
m_mesh->mark(seed, mark_done);
122+
while (!front.empty()) {
123+
auto current_id = front.back();
124+
front.pop_back();
125+
auto current_face = m_mesh->get<Face>(current_id);
126+
// the current face is considered as well oriented and we orient the face sharing
127+
// an edge with it in a valid manner
128+
std::vector<Node> node_faces = current_face.get<Node>();
129+
for (auto i = 0; i < node_faces.size(); i++) {
130+
Node ni = node_faces[i];
131+
Node nj = node_faces[(i + 1) % node_faces.size()];
132+
auto fij_ids = m_mesh->getCommonFaces(ni, nj);
133+
if (fij_ids.size() == 2) {
134+
// means there is another face
135+
auto other_face_id = (fij_ids[0] == current_id) ? fij_ids[1] : fij_ids[0];
136+
auto other_face = m_mesh->get<Face>(other_face_id);
137+
if (!m_mesh->isMarked(other_face, mark_done)) {
138+
std::vector<TCellID> other_face_node_ids = other_face.getIDs<Node>();
139+
// we check if the node ni and nj are traversed in the same way.
140+
bool same_direction = false;
141+
for (auto k = 0; k < other_face_node_ids.size(); k++) {
142+
143+
auto id_k = other_face_node_ids[k];
144+
auto id_l = other_face_node_ids[(k + 1) % other_face_node_ids.size()];
145+
if (id_k == ni.id() && id_l == nj.id()) same_direction = true;
146+
}
147+
if (same_direction) {
148+
std::reverse(other_face_node_ids.begin(), other_face_node_ids.end());
149+
other_face.set<Node>(other_face_node_ids);
150+
nb_reorientation++;
151+
}
152+
m_mesh->mark(other_face, mark_done);
153+
front.push_back(other_face_id);
154+
}
155+
}
156+
}
157+
}
158+
}
159+
}
160+
m_mesh->negateMaskMark<Node>(mark_done);
161+
m_mesh->freeMark<Node>(mark_done);
162+
//warning, we must ensure that the mesh is reoriented even if it has several
163+
//connex part
164+
return nb_reorientation;
165+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*----------------------------------------------------------------------------*/
2+
#include <gtest/gtest.h>
3+
#include <gmds/ig/Mesh.h>
4+
#include <gmds/ig/MeshDoctor.h>
5+
#include <gmds/io/VTKWriter.h>
6+
#include <gmds/io/VTKReader.h>
7+
#include <gmds/igalgo/SurfaceReorient.h>
8+
#include <iostream>
9+
#include <gmds/io/IGMeshIOService.h>
10+
#include <unit_test_config.h>
11+
using namespace gmds;
12+
/*----------------------------------------------------------------------------*/
13+
TEST(SurfaceReorientTestClass, testReorient)
14+
{
15+
// WE WRITE
16+
Mesh m(MeshModel(DIM3|F|N|F2N|N2F));
17+
18+
std::string dir(TEST_SAMPLES_DIR);
19+
std::string vtk_file = dir+"/reorient3D_test.vtk";
20+
gmds::IGMeshIOService ioService(&m);
21+
gmds::VTKReader vtkReader(&ioService);
22+
vtkReader.setCellOptions(gmds::N|gmds::F);
23+
vtkReader.read(vtk_file);
24+
25+
gmds::MeshDoctor doc(&m);
26+
doc.updateUpwardConnectivity();
27+
gmds::SurfaceReorient reorient(&m);
28+
29+
ASSERT_TRUE(reorient.isValid());
30+
31+
auto nb_reorient = reorient.execute();
32+
ASSERT_TRUE(nb_reorient!=0);
33+
34+
gmds::VTKWriter w(&ioService);
35+
w.setCellOptions(gmds::N|gmds::F);
36+
w.write("toto.vtk");
37+
38+
39+
}

igalgo/tst/main_test.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "BoundaryOpTestSuite.h"
77
#include "BoundaryExtractorTestSuite.h"
88
#include "GridBuilderTestSuite.h"
9+
#include "SurfaceReorientTestSuite.h"
910
/*----------------------------------------------------------------------------*/
1011
int main(int argc, char ** argv) {
1112
::testing::InitGoogleTest(&argc, argv);

0 commit comments

Comments
 (0)