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+ }
0 commit comments