Developer Documentation
HexahedralMeshTopologyKernel.cc
1 /*===========================================================================*\
2  * *
3  * OpenVolumeMesh *
4  * Copyright (C) 2011 by Computer Graphics Group, RWTH Aachen *
5  * www.openvolumemesh.org *
6  * *
7  *---------------------------------------------------------------------------*
8  * This file is part of OpenVolumeMesh. *
9  * *
10  * OpenVolumeMesh is free software: you can redistribute it and/or modify *
11  * it under the terms of the GNU Lesser General Public License as *
12  * published by the Free Software Foundation, either version 3 of *
13  * the License, or (at your option) any later version with the *
14  * following exceptions: *
15  * *
16  * If other files instantiate templates or use macros *
17  * or inline functions from this file, or you compile this file and *
18  * link it with other files to produce an executable, this file does *
19  * not by itself cause the resulting executable to be covered by the *
20  * GNU Lesser General Public License. This exception does not however *
21  * invalidate any other reasons why the executable file might be *
22  * covered by the GNU Lesser General Public License. *
23  * *
24  * OpenVolumeMesh is distributed in the hope that it will be useful, *
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
27  * GNU Lesser General Public License for more details. *
28  * *
29  * You should have received a copy of the GNU LesserGeneral Public *
30  * License along with OpenVolumeMesh. If not, *
31  * see <http://www.gnu.org/licenses/>. *
32  * *
33 \*===========================================================================*/
34 
35 /*===========================================================================*\
36  * *
37  * $Revision$ *
38  * $Date$ *
39  * $LastChangedBy$ *
40  * *
41 \*===========================================================================*/
42 
43 #include "HexahedralMeshTopologyKernel.hh"
44 
45 namespace OpenVolumeMesh {
46 
47 
48 FaceHandle HexahedralMeshTopologyKernel::add_face(const std::vector<HalfEdgeHandle>& _halfedges, bool _topologyCheck) {
49 
50  if(_halfedges.size() != 4) {
51 #ifndef NDEBUG
52  std::cerr << "HexahedralMeshTopologyKernel::add_face(): Face valence is not four! Returning" << std::endl;
53  std::cerr << "invalid handle." << std::endl;
54 #endif
55  return TopologyKernel::InvalidFaceHandle;
56  }
57 
58  return TopologyKernel::add_face(_halfedges, _topologyCheck);
59 }
60 
61 //========================================================================================
62 
63 
65 HexahedralMeshTopologyKernel::add_face(const std::vector<VertexHandle>& _vertices) {
66 
67  if(_vertices.size() != 4) {
68 #ifndef NDEBUG
69  std::cerr << "HexahedralMeshTopologyKernel::add_face(): Face valence is not four! Returning" << std::endl;
70  std::cerr << "invalid handle." << std::endl;
71 #endif
72  return TopologyKernel::InvalidFaceHandle;
73  }
74 
75  return TopologyKernel::add_face(_vertices);
76 }
77 
78 //========================================================================================
79 
80 
82 HexahedralMeshTopologyKernel::add_cell(const std::vector<HalfFaceHandle>& _halffaces, bool _topologyCheck) {
83 
84  if(_halffaces.size() != 6) {
85 // To make this consistent with add_face
86 #ifndef NDEBUG
87  std::cerr << "Cell valence is not six! Aborting." << std::endl;
88 #endif
89  return TopologyKernel::InvalidCellHandle;
90  }
91  for(std::vector<HalfFaceHandle>::const_iterator it = _halffaces.begin();
92  it != _halffaces.end(); ++it) {
93  if(TopologyKernel::halfface(*it).halfedges().size() != 4) {
94 #ifndef NDEBUG
95  std::cerr << "Incident face does not have valence four! Aborting." << std::endl;
96 #endif
97  return TopologyKernel::InvalidCellHandle;
98  }
99  }
100 
101  // Create new halffaces vector
102  std::vector<HalfFaceHandle> ordered_halffaces;
103 
104  // The user wants the faces to be reordered
105  if(_topologyCheck) {
106 
107  // Are faces in correct ordering?
108  bool ordered = check_halfface_ordering(_halffaces);
109 
110  if(!ordered) {
111 
112 #ifndef NDEBUG
113  std::cerr << "The specified half-faces are not in correct order. Trying automatic re-ordering." << std::endl;
114 #endif
115 
116  // Ordering array (see below for details)
117  const int orderTop[] = {2, 4, 3, 5};
118  //const int orderBot[] = {3, 4, 2, 5};
119 
120  ordered_halffaces.resize(6, TopologyKernel::InvalidHalfFaceHandle);
121 
122  // Create top side
123  ordered_halffaces[0] = _halffaces[0];
124 
125  // Go over all incident halfedges
126  std::vector<HalfEdgeHandle> hes = TopologyKernel::halfface(ordered_halffaces[0]).halfedges();
127  unsigned int idx = 0;
128  for(std::vector<HalfEdgeHandle>::const_iterator he_it = hes.begin();
129  he_it != hes.end(); ++he_it) {
130 
131  HalfFaceHandle ahfh = get_adjacent_halfface(ordered_halffaces[0], *he_it, _halffaces);
132  if(ahfh == TopologyKernel::InvalidHalfFaceHandle) {
133 #ifndef NDEBUG
134  std::cerr << "The current halfface is invalid!" << std::endl;
135 #endif
136  continue;
137  }
138  ordered_halffaces[orderTop[idx]] = ahfh;
139  ++idx;
140  }
141 
142  // Now set bottom-halfface
143  HalfFaceHandle cur_hf = ordered_halffaces[0];
144  HalfEdgeHandle cur_he = *(TopologyKernel::halfface(cur_hf).halfedges().begin());
145  cur_hf = get_adjacent_halfface(cur_hf, cur_he, _halffaces);
146  cur_he = TopologyKernel::opposite_halfedge_handle(cur_he);
147  cur_he = TopologyKernel::next_halfedge_in_halfface(cur_he, cur_hf);
148  cur_he = TopologyKernel::next_halfedge_in_halfface(cur_he, cur_hf);
149  cur_hf = get_adjacent_halfface(cur_hf, cur_he, _halffaces);
150 
151  if(cur_hf != TopologyKernel::InvalidHalfFaceHandle) {
152  ordered_halffaces[1] = cur_hf;
153  } else {
154 #ifndef NDEBUG
155  std::cerr << "The current halfface is invalid!" << std::endl;
156 #endif
157  return TopologyKernel::InvalidCellHandle;
158  }
159 
160  } else {
161  // Assume right ordering at the user's risk
162  ordered_halffaces = _halffaces;
163  }
164 
165  } else {
166  // Just copy the original ones
167  ordered_halffaces = _halffaces;
168  }
169 
170  return TopologyKernel::add_cell(ordered_halffaces, _topologyCheck);
171 }
172 
173 //========================================================================================
174 
175 bool HexahedralMeshTopologyKernel::check_halfface_ordering(const std::vector<HalfFaceHandle>& _hfs) const {
176 
177  /*
178  * The test works as follows: Test for both the first and second face in the list,
179  * whether the following order holds (clockwise):
180  *
181  * View from above, outside.
182  * ____
183  * | 4 |
184  * ____|____|____
185  * | 5 | 1 | 6 |
186  * |____|____|____|
187  * | 3 |
188  * |____|
189  *
190  * View from below, outside.
191  * ____
192  * | 3 |
193  * ____|____|____
194  * | 5 | 2 | 6 |
195  * |____|____|____|
196  * | 4 |
197  * |____|
198  */
199 
200  const int orderTop[] = {2, 4, 3, 5};
201  const int orderBot[] = {3, 4, 2, 5};
202 
203  HalfFaceHandle hfhTop = _hfs[0];
204  HalfFaceHandle hfhBot = _hfs[1];
205 
206  std::vector<HalfEdgeHandle> halfedgesTop = TopologyKernel::halfface(_hfs[0]).halfedges();
207  std::vector<HalfEdgeHandle> halfedgesBot = TopologyKernel::halfface(_hfs[1]).halfedges();
208 
209  int offsetTop = -1;
210  int offsetBot = -1;
211 
212  // Traverse halfedges top
213  for(std::vector<HalfEdgeHandle>::const_iterator it = halfedgesTop.begin();
214  it != halfedgesTop.end(); ++it) {
215 
216  HalfFaceHandle ahfh = get_adjacent_halfface(hfhTop, *it, _hfs);
217 
218  if(offsetTop == -1) {
219  if(ahfh == _hfs[2]) offsetTop = 0;
220  else if(ahfh == _hfs[4]) offsetTop = 1;
221  else if(ahfh == _hfs[3]) offsetTop = 2;
222  else if(ahfh == _hfs[5]) offsetTop = 3;
223  } else {
224  offsetTop = (offsetTop + 1) % 4;
225  if(ahfh != _hfs[orderTop[offsetTop]]) {
226 #ifndef NDEBUG
227  std::cerr << "Faces not in right order!" << std::endl;
228 #endif
229  return false;
230  }
231  }
232  }
233 
234  if(offsetTop == -1) {
235 #ifndef NDEBUG
236  std::cerr << "Faces not in right order!" << std::endl;
237 #endif
238  return false;
239  }
240 
241  // Traverse halfedges bottom
242  for(std::vector<HalfEdgeHandle>::const_iterator it = halfedgesBot.begin();
243  it != halfedgesBot.end(); ++it) {
244 
245  HalfFaceHandle ahfh = get_adjacent_halfface(hfhBot, *it, _hfs);
246 
247  if(offsetBot == -1) {
248  if(ahfh == _hfs[3]) offsetBot = 0;
249  else if(ahfh == _hfs[4]) offsetBot = 1;
250  else if(ahfh == _hfs[2]) offsetBot = 2;
251  else if(ahfh == _hfs[5]) offsetBot = 3;
252  } else {
253  offsetBot = (offsetBot + 1) % 4;
254  if(ahfh != _hfs[orderBot[offsetBot]]) {
255 #ifndef NDEBUG
256  std::cerr << "Faces not in right order!" << std::endl;
257 #endif
258  return false;
259  }
260  }
261  }
262 
263  if(offsetBot == -1) {
264 #ifndef NDEBUG
265  std::cerr << "Faces not in right order!" << std::endl;
266 #endif
267  return false;
268  }
269 
270  return true;
271 }
272 
273 //========================================================================================
274 
276 HexahedralMeshTopologyKernel::add_cell(const std::vector<VertexHandle>& _vertices, bool _topologyCheck) {
277 
278  // debug mode checks
279  assert(TopologyKernel::has_full_bottom_up_incidences());
280  assert(_vertices.size() == 8);
281 
282  // release mode checks
283  if(!TopologyKernel::has_full_bottom_up_incidences()) {
284  return CellHandle(-1);
285  }
286 
287  if(_vertices.size() != 8) {
288  return CellHandle(-1);
289  }
290 
291  HalfFaceHandle hf0, hf1, hf2, hf3, hf4, hf5;
292 
293  std::vector<VertexHandle> vs;
294 
295  // Half-face XF
296  vs.push_back(_vertices[3]);
297  vs.push_back(_vertices[2]);
298  vs.push_back(_vertices[1]);
299  vs.push_back(_vertices[0]);
300  hf0 = TopologyKernel::halfface_extensive(vs); vs.clear();
301 
302  // Half-face XB
303  vs.push_back(_vertices[7]);
304  vs.push_back(_vertices[6]);
305  vs.push_back(_vertices[5]);
306  vs.push_back(_vertices[4]);
307  hf1 = TopologyKernel::halfface_extensive(vs); vs.clear();
308 
309  // Half-face YF
310  vs.push_back(_vertices[1]);
311  vs.push_back(_vertices[2]);
312  vs.push_back(_vertices[6]);
313  vs.push_back(_vertices[7]);
314  hf2 = TopologyKernel::halfface_extensive(vs); vs.clear();
315 
316  // Half-face YB
317  vs.push_back(_vertices[4]);
318  vs.push_back(_vertices[5]);
319  vs.push_back(_vertices[3]);
320  vs.push_back(_vertices[0]);
321  hf3 = TopologyKernel::halfface_extensive(vs); vs.clear();
322 
323  // Half-face ZF
324  vs.push_back(_vertices[1]);
325  vs.push_back(_vertices[7]);
326  vs.push_back(_vertices[4]);
327  vs.push_back(_vertices[0]);
328  hf4 = TopologyKernel::halfface_extensive(vs); vs.clear();
329 
330  // Half-face ZB
331  vs.push_back(_vertices[2]);
332  vs.push_back(_vertices[3]);
333  vs.push_back(_vertices[5]);
334  vs.push_back(_vertices[6]);
336 
337  if(!hf0.is_valid()) {
338 
339  vs.clear();
340  vs.push_back(_vertices[3]); vs.push_back(_vertices[2]);
341  vs.push_back(_vertices[1]); vs.push_back(_vertices[0]);
343  hf0 = halfface_handle(fh, 0);
344  }
345 
346  if(!hf1.is_valid()) {
347 
348  vs.clear();
349  vs.push_back(_vertices[7]); vs.push_back(_vertices[6]);
350  vs.push_back(_vertices[5]); vs.push_back(_vertices[4]);
352  hf1 = halfface_handle(fh, 0);
353  }
354 
355  if(!hf2.is_valid()) {
356 
357  vs.clear();
358  vs.push_back(_vertices[1]); vs.push_back(_vertices[2]);
359  vs.push_back(_vertices[6]); vs.push_back(_vertices[7]);
361  hf2 = halfface_handle(fh, 0);
362  }
363 
364  if(!hf3.is_valid()) {
365 
366  vs.clear();
367  vs.push_back(_vertices[4]); vs.push_back(_vertices[5]);
368  vs.push_back(_vertices[3]); vs.push_back(_vertices[0]);
370  hf3 = halfface_handle(fh, 0);
371  }
372 
373  if(!hf4.is_valid()) {
374 
375  vs.clear();
376  vs.push_back(_vertices[1]); vs.push_back(_vertices[7]);
377  vs.push_back(_vertices[4]); vs.push_back(_vertices[0]);
379  hf4 = halfface_handle(fh, 0);
380  }
381 
382  if(!hf5.is_valid()) {
383 
384  vs.clear();
385  vs.push_back(_vertices[2]); vs.push_back(_vertices[3]);
386  vs.push_back(_vertices[5]); vs.push_back(_vertices[6]);
388  hf5 = halfface_handle(fh, 0);
389  }
390 
391  assert(hf0.is_valid()); assert(hf1.is_valid()); assert(hf2.is_valid());
392  assert(hf3.is_valid()); assert(hf4.is_valid()); assert(hf5.is_valid());
393 
394 
395  std::vector<HalfFaceHandle> hfs;
396  hfs.push_back(hf0); hfs.push_back(hf1); hfs.push_back(hf2);
397  hfs.push_back(hf3); hfs.push_back(hf4); hfs.push_back(hf5);
398 
399  if (_topologyCheck) {
400  /*
401  * Test if all halffaces are connected and form a two-manifold
402  * => Cell is closed
403  *
404  * This test is simple: The number of involved half-edges has to be
405  * exactly twice the number of involved edges.
406  */
407 
408  std::set<HalfEdgeHandle> incidentHalfedges;
409  std::set<EdgeHandle> incidentEdges;
410 
411  for(std::vector<HalfFaceHandle>::const_iterator it = hfs.begin(),
412  end = hfs.end(); it != end; ++it) {
413 
414  OpenVolumeMeshFace hface = halfface(*it);
415  for(std::vector<HalfEdgeHandle>::const_iterator he_it = hface.halfedges().begin(),
416  he_end = hface.halfedges().end(); he_it != he_end; ++he_it) {
417  incidentHalfedges.insert(*he_it);
418  incidentEdges.insert(edge_handle(*he_it));
419  }
420  }
421 
422  if(incidentHalfedges.size() != (incidentEdges.size() * 2u)) {
423 #ifndef NDEBUG
424  std::cerr << "The specified halffaces are not connected!" << std::endl;
425 #endif
426  return InvalidCellHandle;
427  }
428  // The halffaces are now guaranteed to form a two-manifold
429 
430  if(has_face_bottom_up_incidences()) {
431 
432  for(std::vector<HalfFaceHandle>::const_iterator it = hfs.begin(),
433  end = hfs.end(); it != end; ++it) {
434  if(incident_cell(*it) != InvalidCellHandle) {
435 #ifndef NDEBUG
436  std::cerr << "Warning: One of the specified half-faces is already incident to another cell!" << std::endl;
437 #endif
438  return InvalidCellHandle;
439  }
440  }
441 
442  }
443 
444  }
445 
446  return TopologyKernel::add_cell(hfs, false);
447 }
448 
449 //========================================================================================
450 
451 const HalfFaceHandle&
452 HexahedralMeshTopologyKernel::get_adjacent_halfface(const HalfFaceHandle& _hfh, const HalfEdgeHandle& _heh,
453  const std::vector<HalfFaceHandle>& _halffaces) const {
454 
455  // Search for halfface that is incident to the opposite
456  // halfedge of _heh
457  HalfEdgeHandle o_he = TopologyKernel::opposite_halfedge_handle(_heh);
458 
459  for(std::vector<HalfFaceHandle>::const_iterator it = _halffaces.begin();
460  it != _halffaces.end(); ++it) {
461  if(*it == _hfh) continue;
462  std::vector<HalfEdgeHandle> halfedges = TopologyKernel::halfface(*it).halfedges();
463  for(std::vector<HalfEdgeHandle>::const_iterator h_it = halfedges.begin();
464  h_it != halfedges.end(); ++h_it) {
465  if(*h_it == o_he) return *it;
466  }
467  }
468 
469  return TopologyKernel::InvalidHalfFaceHandle;
470 }
471 
472 } // Namespace OpenVolumeMesh
virtual FaceHandle add_face(const std::vector< HalfEdgeHandle > &_halfedges, bool _topologyCheck=false)
Add face via incident edges.
CellHandle add_cell(const std::vector< HalfFaceHandle > &_halffaces, bool _topologyCheck=false) override
Overridden function.
CellHandle incident_cell(const HalfFaceHandle &_halfFaceHandle) const
Get cell that is incident to the given halfface.
static EdgeHandle edge_handle(const HalfEdgeHandle &_h)
Handle conversion.
static HalfFaceHandle halfface_handle(const FaceHandle &_h, const unsigned char _subIdx)
Conversion function.
HalfEdgeHandle next_halfedge_in_halfface(const HalfEdgeHandle &_heh, const HalfFaceHandle &_hfh) const
Get next halfedge within a halfface.
FaceHandle add_face(const std::vector< HalfEdgeHandle > &_halfedges, bool _topologyCheck=false) override
Add face via incident edges.
Face halfface(const HalfFaceHandle &_halfFaceHandle) const
Get face that corresponds to halfface with handle _halfFaceHandle.
virtual CellHandle add_cell(const std::vector< HalfFaceHandle > &_halffaces, bool _topologyCheck=false)
Add cell via incident halffaces.
HalfFaceHandle halfface_extensive(const std::vector< VertexHandle > &_vs) const