Developer Documentation
unittests_tutorials.cc
1 
2 #include <gtest/gtest.h>
3 #include <Unittests/unittests_common.hh>
4 #include <string>
5 #include <map>
6 #include "generate_cube.hh"
7 #include "fill_props.hh"
8 
9 /*
10  * ====================================================================
11  * Definition of custom properties related classes
12  * ====================================================================
13  */
14 
15 struct MyData
16 {
17  int ival;
18  double dval;
19  bool bval;
20  OpenMesh::Vec4f vec4fval;
21 
22  MyData()
23  : ival(0), dval(0.0), bval(false)
24  { }
25 
26  MyData( const MyData& _cpy )
27  : ival(_cpy.ival), dval(_cpy.dval), bval(_cpy.bval),
28  vec4fval(_cpy.vec4fval)
29  { }
30 
31  // ---------- assignment
32 
33  MyData& operator = (const MyData& _rhs)
34  {
35  ival = _rhs.ival;
36  dval = _rhs.dval;
37  bval = _rhs.bval;
38  vec4fval = _rhs.vec4fval;
39  return *this;
40  }
41 
42  MyData& operator = (int _rhs) { ival = _rhs; return *this; }
43  MyData& operator = (double _rhs) { dval = _rhs; return *this; }
44  MyData& operator = (bool _rhs) { bval = _rhs; return *this; }
45  MyData& operator = (const OpenMesh::Vec4f& _rhs)
46  { vec4fval = _rhs; return *this; }
47 
48  // ---------- comparison
49 
50  bool operator == (const MyData& _rhs) const
51  {
52  return ival == _rhs.ival
53  && dval == _rhs.dval
54  && bval == _rhs.bval
55  && vec4fval == _rhs.vec4fval;
56  }
57  bool operator != (const MyData& _rhs) const { return !(*this == _rhs); }
58 };
59 
60 typedef std::map< std::string, unsigned int > MyMap;
61 
62 namespace OpenMesh {
63  namespace IO {
64  // support persistence for struct MyData
65  template <> struct binary<MyData>
66  {
67  typedef MyData value_type;
68  static const bool is_streamable = true;
69 
70  // return binary size of the value
71  static size_t size_of(void)
72  {
73  return sizeof(int)+sizeof(double)+sizeof(bool)+sizeof(OpenMesh::Vec4f);
74  }
75 
76  static size_t size_of(const value_type&)
77  {
78  return size_of();
79  }
80 
81  static size_t store(std::ostream& _os, const value_type& _v, bool _swap=false)
82  {
83  size_t bytes;
84  bytes = IO::store( _os, _v.ival, _swap );
85  bytes += IO::store( _os, _v.dval, _swap );
86  bytes += IO::store( _os, _v.bval, _swap );
87  bytes += IO::store( _os, _v.vec4fval, _swap );
88  return _os.good() ? bytes : 0;
89  }
90 
91  static size_t restore( std::istream& _is, value_type& _v, bool _swap=false)
92  {
93  size_t bytes;
94  bytes = IO::restore( _is, _v.ival, _swap );
95  bytes += IO::restore( _is, _v.dval, _swap );
96  bytes += IO::restore( _is, _v.bval, _swap );
97  bytes += IO::restore( _is, _v.vec4fval, _swap );
98  return _is.good() ? bytes : 0;
99  }
100  };
101 
102  template <> struct binary< MyMap >
103  {
104  typedef MyMap value_type;
105  static const bool is_streamable = true;
106 
107  // return generic binary size of self, if known
108  static size_t size_of(void) { return UnknownSize; }
109 
110  // return binary size of the value
111  static size_t size_of(const value_type& _v)
112  {
113  if (_v.empty())
114  return sizeof(unsigned int);
115 
116  value_type::const_iterator it = _v.begin();
117  unsigned int N = _v.size();
118  size_t bytes = IO::size_of(N);
119  for(;it!=_v.end(); ++it)
120  {
121  bytes += IO::size_of( it->first );
122  bytes += IO::size_of( it->second );
123  }
124  return bytes;
125  }
126 
127  static
128  size_t store(std::ostream& _os, const value_type& _v, bool _swap=false)
129  {
130  size_t bytes = 0;
131  unsigned int N = _v.size();
132  value_type::const_iterator it = _v.begin();
133  bytes += IO::store( _os, N, _swap );
134  for (; it != _v.end() && _os.good(); ++it)
135  {
136  bytes += IO::store( _os, it->first, _swap );
137  bytes += IO::store( _os, it->second, _swap );
138  }
139  return _os.good() ? bytes : 0;
140  }
141 
142  static
143  size_t restore( std::istream& _is, value_type& _v, bool _swap=false)
144  {
145  size_t bytes = 0;
146  unsigned int N = 0;
147  _v.clear();
148  bytes += IO::restore( _is, N, _swap );
149  value_type::key_type key;
150  value_type::mapped_type val;
151  for (size_t i=0; i<N && _is.good(); ++i)
152  {
153  bytes += IO::restore( _is, key, _swap );
154  bytes += IO::restore( _is, val, _swap );
155  _v[key] = val;
156  }
157  return _is.good() ? bytes : 0;
158  }
159  };
160 
161  }
162 }
163 
164 namespace {
165 
166 class OpenMeshTutorials: public OpenMeshBase {
167 
168  protected:
169 
170  // This function is called before each test is run
171  virtual void SetUp() {
172 
173  // Do some initial stuff with the member data here...
174  }
175 
176  // This function is called after all tests are through
177  virtual void TearDown() {
178 
179  // Do some final stuff with the member data here...
180  }
181 
182  // Member already defined in OpenMeshBase
183  //Mesh mesh_;
184 };
185 
186 /*
187  * ====================================================================
188  * Classes for unittests
189  * ====================================================================
190  */
191 
192 template <class Mesh> class SmootherT
193 {
194 public:
195  typedef typename Mesh::Point cog_t;
196  typedef OpenMesh::VPropHandleT< cog_t > Property_cog;
197 
198 public:
199  // construct with a given mesh
200  SmootherT(Mesh& _mesh)
201  : mesh_(_mesh)
202  {
203  mesh_.add_property( cog_ );
204  }
205  ~SmootherT()
206  {
207  mesh_.remove_property( cog_ );
208  }
209  // smooth mesh _iterations times
210  void smooth(unsigned int _iterations)
211  {
212  for (unsigned int i=0; i < _iterations; ++i)
213  {
214  std::for_each(mesh_.vertices_begin(),
215  mesh_.vertices_end(),
216  ComputeCOG(mesh_, cog_));
217  std::for_each(mesh_.vertices_begin(),
218  mesh_.vertices_end(),
219  SetCOG(mesh_, cog_));
220  }
221  }
222 
223 private:
224  //--- private classes ---
225  class ComputeCOG
226  {
227  public:
228  ComputeCOG(Mesh& _mesh, Property_cog& _cog)
229  : mesh_(_mesh), cog_(_cog)
230  {}
231  void operator()(const typename Mesh::VertexHandle& _vh)
232  {
233  typename Mesh::VertexVertexIter vv_it;
234  typename Mesh::Scalar valence(0.0);
235 
236  mesh_.property(cog_, _vh) = typename Mesh::Point(0.0, 0.0, 0.0);
237  for (vv_it=mesh_.vv_iter(_vh); vv_it.is_valid(); ++vv_it)
238  {
239  mesh_.property(cog_, _vh) += mesh_.point( *vv_it );
240  ++valence;
241  }
242  mesh_.property(cog_, _vh ) /= valence;
243  }
244  private:
245  Mesh& mesh_;
246  Property_cog& cog_;
247  };
248 
249  class SetCOG
250  {
251  public:
252  SetCOG(Mesh& _mesh, Property_cog& _cog)
253  : mesh_(_mesh), cog_(_cog)
254  {}
255  void operator()(const typename Mesh::VertexHandle& _vh)
256  {
257  if (!mesh_.is_boundary(_vh))
258  mesh_.set_point( _vh, mesh_.property(cog_, _vh) );
259  }
260  private:
261  Mesh& mesh_;
262  Property_cog& cog_;
263  };
264 
265  //--- private elements ---
266  Mesh& mesh_;
267  Property_cog cog_;
268 };
269 
270 
271 /*
272  * ====================================================================
273  * Specify our traits
274  * ====================================================================
275  */
276 
277 struct MyTraits : public OpenMesh::DefaultTraits
278 {
280 };
281 
282 // Define my personal fancy traits
283 struct MyFancyTraits : OpenMesh::DefaultTraits
284 {
285  // Let Point and Normal be a vector of doubles
286  typedef OpenMesh::Vec3d Point;
287  typedef OpenMesh::Vec3d Normal;
288  // Already defined in OpenMesh::DefaultTraits
289  // HalfedgeAttributes( OpenMesh::Attributes::PrevHalfedge );
290 
291  // Uncomment next line to disable attribute PrevHalfedge
292  // HalfedgeAttributes( OpenMesh::Attributes::None );
293  //
294  // or
295  //
296  // HalfedgeAttributes( 0 );
297 };
298 
299 struct MyTraitsWithCOG : public OpenMesh::DefaultTraits
300 {
301  // store barycenter of neighbors in this member
303  {
304  private:
305  Point cog_;
306  public:
307  VertexT() : cog_( Point(0.0f, 0.0f, 0.0f ) ) { }
308  const Point& cog() const { return cog_; }
309  void set_cog(const Point& _p) { cog_ = _p; }
310  };
311 };
312 
313 struct MyTraitsWithStatus : public OpenMesh::DefaultTraits
314 {
318 };
319 
320 /*
321  * ====================================================================
322  * Specify our meshes
323  * ====================================================================
324  */
326 typedef OpenMesh::TriMesh_ArrayKernelT<MyTraits> MyMeshWithTraits;
328 typedef OpenMesh::TriMesh_ArrayKernelT<MyFancyTraits> MyFancyTriMesh;
329 typedef OpenMesh::TriMesh_ArrayKernelT<MyTraitsWithCOG> MyTriMeshWithCOG;
331 
332 /*
333  * ====================================================================
334  * Define tests below
335  * ====================================================================
336  */
337 
338 /*
339  */
340 TEST_F(OpenMeshTutorials, building_a_cube) {
341 
342  MyMesh mesh;
343 
344  // generate vertices
345  MyMesh::VertexHandle vhandle[8];
346  vhandle[0] = mesh.add_vertex(MyMesh::Point(-1, -1, 1));
347  vhandle[1] = mesh.add_vertex(MyMesh::Point( 1, -1, 1));
348  vhandle[2] = mesh.add_vertex(MyMesh::Point( 1, 1, 1));
349  vhandle[3] = mesh.add_vertex(MyMesh::Point(-1, 1, 1));
350  vhandle[4] = mesh.add_vertex(MyMesh::Point(-1, -1, -1));
351  vhandle[5] = mesh.add_vertex(MyMesh::Point( 1, -1, -1));
352  vhandle[6] = mesh.add_vertex(MyMesh::Point( 1, 1, -1));
353  vhandle[7] = mesh.add_vertex(MyMesh::Point(-1, 1, -1));
354 
355  // generate (quadrilateral) faces
356  std::vector<MyMesh::VertexHandle> face_vhandles;
357  face_vhandles.clear();
358  face_vhandles.push_back(vhandle[0]);
359  face_vhandles.push_back(vhandle[1]);
360  face_vhandles.push_back(vhandle[2]);
361  face_vhandles.push_back(vhandle[3]);
362  mesh.add_face(face_vhandles);
363 
364  face_vhandles.clear();
365  face_vhandles.push_back(vhandle[7]);
366  face_vhandles.push_back(vhandle[6]);
367  face_vhandles.push_back(vhandle[5]);
368  face_vhandles.push_back(vhandle[4]);
369  mesh.add_face(face_vhandles);
370  face_vhandles.clear();
371  face_vhandles.push_back(vhandle[1]);
372  face_vhandles.push_back(vhandle[0]);
373  face_vhandles.push_back(vhandle[4]);
374  face_vhandles.push_back(vhandle[5]);
375  mesh.add_face(face_vhandles);
376  face_vhandles.clear();
377  face_vhandles.push_back(vhandle[2]);
378  face_vhandles.push_back(vhandle[1]);
379  face_vhandles.push_back(vhandle[5]);
380  face_vhandles.push_back(vhandle[6]);
381  mesh.add_face(face_vhandles);
382  face_vhandles.clear();
383  face_vhandles.push_back(vhandle[3]);
384  face_vhandles.push_back(vhandle[2]);
385  face_vhandles.push_back(vhandle[6]);
386  face_vhandles.push_back(vhandle[7]);
387  mesh.add_face(face_vhandles);
388  face_vhandles.clear();
389  face_vhandles.push_back(vhandle[0]);
390  face_vhandles.push_back(vhandle[3]);
391  face_vhandles.push_back(vhandle[7]);
392  face_vhandles.push_back(vhandle[4]);
393  mesh.add_face(face_vhandles);
394 
395  bool ok = OpenMesh::IO::write_mesh(mesh, "output.off");
396 
397  EXPECT_TRUE(ok) << "Cannot write mesh to file 'output.off'";
398 }
399 
400 TEST_F(OpenMeshTutorials, using_iterators_and_circulators) {
401  MyMesh mesh;
402 
403  bool ok = OpenMesh::IO::read_mesh(mesh, "output.off");
404 
405  EXPECT_TRUE(ok) << "Cannot read mesh from file 'output.off'";
406 
407  // this vector stores the computed centers of gravity
408  std::vector<MyMesh::Point> cogs;
409  std::vector<MyMesh::Point>::iterator cog_it;
410  cogs.reserve(mesh.n_vertices());
411 
412  // smoothing mesh N times
413  MyMesh::VertexIter v_it, v_end(mesh.vertices_end());
415  MyMesh::Point cog;
416  MyMesh::Scalar valence;
417  unsigned int i, N(100);
418  for (i=0; i < N; ++i)
419  {
420  cogs.clear();
421  for (v_it = mesh.vertices_begin(); v_it != v_end; ++v_it)
422  {
423  cog[0] = cog[1] = cog[2] = valence = 0.0;
424 
425  for (vv_it = mesh.vv_iter( *v_it ); vv_it.is_valid(); ++vv_it)
426  {
427  cog += mesh.point( *vv_it );
428  ++valence;
429  }
430  cogs.push_back(cog / valence);
431  }
432 
433  for (v_it = mesh.vertices_begin(), cog_it = cogs.begin();
434  v_it != v_end; ++v_it, ++cog_it)
435  if ( !mesh.is_boundary( *v_it ) )
436  mesh.set_point( *v_it, *cog_it );
437  }
438 
439  // write mesh
440  ok = OpenMesh::IO::write_mesh(mesh, "smoothed_output.off");
441 
442  EXPECT_TRUE(ok) << "Cannot write mesh to file 'smoothed_output.off'";
443 }
444 
445 TEST_F(OpenMeshTutorials, using_custom_properties) {
446  MyMesh mesh;
447 
448  bool ok = OpenMesh::IO::read_mesh(mesh, "output.off");
449  EXPECT_TRUE(ok) << "Cannot read mesh from file 'output.off'";
450 
451  // this vertex property stores the computed centers of gravity
453  mesh.add_property(cogs);
454 
455  // smoothing mesh N times
456  MyMesh::VertexIter v_it, v_end(mesh.vertices_end());
458  MyMesh::Point cog;
459  MyMesh::Scalar valence;
460  unsigned int i, N(100);
461 
462  for (i=0; i < N; ++i)
463  {
464  for (v_it = mesh.vertices_begin(); v_it != v_end; ++v_it)
465  {
466  mesh.property(cogs,*v_it).vectorize(0.0f);
467  valence = 0.0;
468 
469  for (vv_it = mesh.vv_iter( *v_it ); vv_it.is_valid(); ++vv_it)
470  {
471  mesh.property(cogs,*v_it) += mesh.point( *vv_it );
472  ++valence;
473  }
474  mesh.property(cogs,*v_it) /= valence;
475  }
476 
477  for (v_it = mesh.vertices_begin(); v_it != v_end; ++v_it)
478  if ( !mesh.is_boundary( *v_it ) )
479  mesh.set_point( *v_it, mesh.property(cogs,*v_it) );
480  }
481 
482  // write mesh
483  ok = OpenMesh::IO::write_mesh(mesh, "smoothed_custom_properties_output.off");
484 
485  EXPECT_TRUE(ok) << "Cannot write mesh to file 'smoothed_custom_properties_output.off'";
486 }
487 
488 TEST_F(OpenMeshTutorials, using_STL_algorithms) {
489  MyMeshWithTraits mesh;
490 
491  bool ok = OpenMesh::IO::read_mesh(mesh, "output.off");
492  EXPECT_TRUE(ok) << "Cannot read mesh from file 'output.off'";
493 
494  SmootherT<MyMeshWithTraits> smoother(mesh);
495  smoother.smooth(100);
496 
497  // write mesh
498  ok = OpenMesh::IO::write_mesh(mesh, "smoothed_STL_output.off");
499 
500  EXPECT_TRUE(ok) << "Cannot write mesh to file 'smoothed_STL_output.off'";
501 }
502 
503 TEST_F(OpenMeshTutorials, using_standard_properties) {
504  MyTriMesh mesh;
505 
506  mesh.request_vertex_normals();
507  EXPECT_TRUE(mesh.has_vertex_normals()) << "Standard vertex property 'Normals' not available";
508 
510  bool ok = OpenMesh::IO::read_mesh(mesh, "output.off", opt);
511  EXPECT_TRUE(ok) << "Cannot read mesh from file 'output.off'";
512 
513  // If the file did not provide vertex normals, then calculate them
514  if ( !opt.check( OpenMesh::IO::Options::VertexNormal ) )
515  {
516  // we need face normals to update the vertex normals
517  mesh.request_face_normals();
518  // let the mesh update the normals
519  mesh.update_normals();
520  // dispose the face normals, as we don't need them anymore
521  mesh.release_face_normals();
522  }
523 
524  // move all vertices one unit length along it's normal direction
525  for (MyMesh::VertexIter v_it = mesh.vertices_begin();
526  v_it != mesh.vertices_end(); ++v_it)
527  {
528  mesh.set_point( *v_it, mesh.point(*v_it)+mesh.normal(*v_it) );
529  }
530 
531  // don't need the normals anymore? Remove them!
532  mesh.release_vertex_normals();
533  // just check if it really works
534  EXPECT_FALSE(mesh.has_vertex_normals()) << "Shouldn't have any vertex normals anymore";
535 }
536 
537 TEST_F(OpenMeshTutorials, using_mesh_attributes_and_traits) {
538  MyFancyTriMesh mesh;
539 
540  // Just make sure that point element type is double
542  typeid(double)) << "Data type is wrong";
543 
544  // Make sure that normal element type is double
546  typeid(double)) << "Data type is wrong";
547 
548  // Add vertex normals as default property (ref. previous tutorial)
549  mesh.request_vertex_normals();
550  // Add face normals as default property
551  mesh.request_face_normals();
552 
553  // load a mesh
555  bool ok = OpenMesh::IO::read_mesh(mesh, "output.off", opt);
556  EXPECT_TRUE(ok) << "Cannot read mesh from file 'output.off'";
557 
558  // If the file did not provide vertex normals, then calculate them
559  if ( !opt.check( OpenMesh::IO::Options::VertexNormal ) &&
560  mesh.has_face_normals() && mesh.has_vertex_normals() )
561  {
562  // let the mesh update the normals
563  mesh.update_normals();
564  }
565 
566  // move all vertices one unit length along it's normal direction
567  for (MyMesh::VertexIter v_it = mesh.vertices_begin();
568  v_it != mesh.vertices_end(); ++v_it)
569  {
570  mesh.set_point( *v_it, mesh.point(*v_it)+mesh.normal(*v_it) );
571  }
572 }
573 
574 TEST_F(OpenMeshTutorials, extending_the_mesh_using_traits) {
575  MyTriMeshWithCOG mesh;
576 
577  bool ok = OpenMesh::IO::read_mesh(mesh, "output.off");
578  EXPECT_TRUE(ok) << "Cannot read mesh from file 'output.off'";
579 
580  // smoothing mesh N times
581  MyTriMeshWithCOG::VertexIter v_it, v_end(mesh.vertices_end());
582  MyTriMeshWithCOG::VertexVertexIter vv_it;
583  MyTriMeshWithCOG::Point cog;
584  MyTriMeshWithCOG::Scalar valence;
585  unsigned int i, N(100);
586 
587  for (i=0; i < N; ++i)
588  {
589  for (v_it = mesh.vertices_begin(); v_it != v_end; ++v_it)
590  {
591  cog[0] = cog[1] = cog[2] = valence = 0.0;
592 
593  for (vv_it = mesh.vv_iter(*v_it); vv_it.is_valid(); ++vv_it)
594  {
595  cog += mesh.point( *vv_it );
596  ++valence;
597  }
598  mesh.data(*v_it).set_cog(cog / valence);
599  }
600 
601  for (v_it = mesh.vertices_begin(); v_it != v_end; ++v_it)
602  if (!mesh.is_boundary(*v_it))
603  mesh.set_point( *v_it, mesh.data(*v_it).cog());
604  }
605 
606  // write mesh
607  ok = OpenMesh::IO::write_mesh(mesh, "smoothed_extended_output.off");
608 
609  EXPECT_TRUE(ok) << "Cannot write mesh to file 'smoothed_extended_output.off'";
610 }
611 
612 
613 TEST_F(OpenMeshTutorials, deleting_geometry_elements) {
614  Mesh mesh;
615 
616  // the request has to be called before a vertex/face/edge can be deleted. it grants access to the status attribute
617  mesh.request_face_status();
618  mesh.request_edge_status();
619  mesh.request_vertex_status();
620 
621  // generate vertices
622  MyMeshWithStatus::VertexHandle vhandle[8];
623  MyMeshWithStatus::FaceHandle fhandle[6];
624 
625  vhandle[0] = mesh.add_vertex(MyMesh::Point(-1, -1, 1));
626  vhandle[1] = mesh.add_vertex(MyMesh::Point( 1, -1, 1));
627  vhandle[2] = mesh.add_vertex(MyMesh::Point( 1, 1, 1));
628  vhandle[3] = mesh.add_vertex(MyMesh::Point(-1, 1, 1));
629  vhandle[4] = mesh.add_vertex(MyMesh::Point(-1, -1, -1));
630  vhandle[5] = mesh.add_vertex(MyMesh::Point( 1, -1, -1));
631  vhandle[6] = mesh.add_vertex(MyMesh::Point( 1, 1, -1));
632  vhandle[7] = mesh.add_vertex(MyMesh::Point(-1, 1, -1));
633 
634  // generate (quadrilateral) faces
635  std::vector<MyMesh::VertexHandle> tmp_face_vhandles;
636  tmp_face_vhandles.clear();
637  tmp_face_vhandles.push_back(vhandle[0]);
638  tmp_face_vhandles.push_back(vhandle[1]);
639  tmp_face_vhandles.push_back(vhandle[2]);
640  tmp_face_vhandles.push_back(vhandle[3]);
641  fhandle[0] = mesh.add_face(tmp_face_vhandles);
642 
643  tmp_face_vhandles.clear();
644  tmp_face_vhandles.push_back(vhandle[7]);
645  tmp_face_vhandles.push_back(vhandle[6]);
646  tmp_face_vhandles.push_back(vhandle[5]);
647  tmp_face_vhandles.push_back(vhandle[4]);
648  fhandle[1] = mesh.add_face(tmp_face_vhandles);
649 
650  tmp_face_vhandles.clear();
651  tmp_face_vhandles.push_back(vhandle[1]);
652  tmp_face_vhandles.push_back(vhandle[0]);
653  tmp_face_vhandles.push_back(vhandle[4]);
654  tmp_face_vhandles.push_back(vhandle[5]);
655  fhandle[2] = mesh.add_face(tmp_face_vhandles);
656 
657  tmp_face_vhandles.clear();
658  tmp_face_vhandles.push_back(vhandle[2]);
659  tmp_face_vhandles.push_back(vhandle[1]);
660  tmp_face_vhandles.push_back(vhandle[5]);
661  tmp_face_vhandles.push_back(vhandle[6]);
662  fhandle[3] = mesh.add_face(tmp_face_vhandles);
663  tmp_face_vhandles.clear();
664  tmp_face_vhandles.push_back(vhandle[3]);
665  tmp_face_vhandles.push_back(vhandle[2]);
666  tmp_face_vhandles.push_back(vhandle[6]);
667  tmp_face_vhandles.push_back(vhandle[7]);
668  fhandle[4] = mesh.add_face(tmp_face_vhandles);
669 
670  tmp_face_vhandles.clear();
671  tmp_face_vhandles.push_back(vhandle[0]);
672  tmp_face_vhandles.push_back(vhandle[3]);
673  tmp_face_vhandles.push_back(vhandle[7]);
674  tmp_face_vhandles.push_back(vhandle[4]);
675  fhandle[5] = mesh.add_face(tmp_face_vhandles);
676 
677  // And now delete all faces and vertices
678  // except face (vh[7], vh[6], vh[5], vh[4])
679  // whose handle resides in fhandle[1]
680 
681  EXPECT_FALSE(mesh.status(fhandle[0]).deleted()) << "face shouldn't be deleted";
682  EXPECT_FALSE(mesh.status(fhandle[1]).deleted()) << "face shouldn't be deleted";
683  EXPECT_FALSE(mesh.status(fhandle[2]).deleted()) << "face shouldn't be deleted";
684  EXPECT_FALSE(mesh.status(fhandle[3]).deleted()) << "face shouldn't be deleted";
685  EXPECT_FALSE(mesh.status(fhandle[4]).deleted()) << "face shouldn't be deleted";
686  EXPECT_FALSE(mesh.status(fhandle[5]).deleted()) << "face shouldn't be deleted";
687 
688  // Delete face 0
689  mesh.delete_face(fhandle[0], false);
690  // ... face 2
691  mesh.delete_face(fhandle[2], false);
692  // ... face 3
693  mesh.delete_face(fhandle[3], false);
694  // ... face 4
695  mesh.delete_face(fhandle[4], false);
696  // ... face 5
697  mesh.delete_face(fhandle[5], false);
698 
699  EXPECT_TRUE(mesh.status(fhandle[0]).deleted()) << "face should be deleted";
700  EXPECT_FALSE(mesh.status(fhandle[1]).deleted()) << "face shouldn't be deleted";
701  EXPECT_TRUE(mesh.status(fhandle[2]).deleted()) << "face should be deleted";
702  EXPECT_TRUE(mesh.status(fhandle[3]).deleted()) << "face should be deleted";
703  EXPECT_TRUE(mesh.status(fhandle[4]).deleted()) << "face should be deleted";
704  EXPECT_TRUE(mesh.status(fhandle[5]).deleted()) << "face should be deleted";
705 
706  // If isolated vertices result in a face deletion
707  // they have to be deleted manually. If you want this
708  // to happen automatically, change the second parameter
709  // to true.
710  // Now delete the isolated vertices 0, 1, 2 and 3
711 
712  EXPECT_FALSE(mesh.status(vhandle[0]).deleted()) << "vertex shouldn't be deleted";
713  EXPECT_FALSE(mesh.status(vhandle[1]).deleted()) << "vertex shouldn't be deleted";
714  EXPECT_FALSE(mesh.status(vhandle[2]).deleted()) << "vertex shouldn't be deleted";
715  EXPECT_FALSE(mesh.status(vhandle[3]).deleted()) << "vertex shouldn't be deleted";
716 
717 
718  mesh.delete_vertex(vhandle[0], false);
719  mesh.delete_vertex(vhandle[1], false);
720  mesh.delete_vertex(vhandle[2], false);
721  mesh.delete_vertex(vhandle[3], false);
722 
723 
724  EXPECT_TRUE(mesh.status(vhandle[0]).deleted()) << "vertex should be deleted";
725  EXPECT_TRUE(mesh.status(vhandle[1]).deleted()) << "vertex should be deleted";
726  EXPECT_TRUE(mesh.status(vhandle[2]).deleted()) << "vertex should be deleted";
727  EXPECT_TRUE(mesh.status(vhandle[3]).deleted()) << "vertex should be deleted";
728 
729  // Delete all elements that are marked as deleted
730  // from memory.
731  mesh.garbage_collection();
732 
733  // write mesh
734  bool ok = OpenMesh::IO::write_mesh(mesh, "deleted_output.off");
735 
736  EXPECT_TRUE(ok) << "Cannot write mesh to file 'deleted_output.off'";
737 }
738 
739 
740 TEST_F(OpenMeshTutorials, storing_custom_properties) {
741  MyMesh mesh;
742 
743  // generate a geometry
744  generate_cube<MyMesh>(mesh);
745 
746  // define some custom properties
747  OpenMesh::VPropHandleT<float> vprop_float;
748  OpenMesh::EPropHandleT<bool> eprop_bool;
750  OpenMesh::HPropHandleT<MyData> hprop_mydata;
752 
753  // registrate them at the mesh object
754  mesh.add_property(vprop_float, "vprop_float");
755  mesh.add_property(eprop_bool, "eprop_bool");
756  mesh.add_property(fprop_string, "fprop_string");
757  mesh.add_property(hprop_mydata, "hprop_mydata");
758  mesh.add_property(mprop_map, "mprop_map");
759 
760  //fill the props
761  fill_props(mesh, vprop_float);
762  fill_props(mesh, eprop_bool);
763  fill_props(mesh, fprop_string);
764  fill_props(mesh, hprop_mydata);
765  fill_props(mesh, mprop_map);
766 
767  EXPECT_TRUE(fill_props(mesh, vprop_float, true)) << "property not filled correctly";
768  EXPECT_TRUE(fill_props(mesh, eprop_bool, true)) << "property not filled correctly";
769  EXPECT_TRUE(fill_props(mesh, fprop_string, true)) << "property not filled correctly";
770  EXPECT_TRUE(fill_props(mesh, hprop_mydata, true)) << "property not filled correctly";
771  EXPECT_TRUE(fill_props(mesh, mprop_map, true)) << "property not filled correctly";
772 
773  //Set persistent flag
774  mesh.property(vprop_float).set_persistent(true);
775  EXPECT_TRUE(mesh.property(vprop_float).persistent()) << "property should be persistent";
776  mesh.property(eprop_bool).set_persistent(true);
777  EXPECT_TRUE(mesh.property(eprop_bool).persistent()) << "property should be persistent";
778  mesh.property(fprop_string).set_persistent(true);
779  EXPECT_TRUE(mesh.property(fprop_string).persistent()) << "property should be persistent";
780  mesh.property(hprop_mydata).set_persistent(true);
781  EXPECT_TRUE(mesh.property(hprop_mydata).persistent()) << "property should be persistent";
782  mesh.mproperty(mprop_map).set_persistent(true);
783  EXPECT_TRUE(mesh.mproperty(mprop_map).persistent()) << "property should be persistent";
784 
785  // write mesh
786  bool ok = OpenMesh::IO::write_mesh( mesh, "persistence-check.om" );
787  EXPECT_TRUE(ok) << "Cannot write mesh to file 'persistent-check.om'";
788 
789  // clear mesh
790  mesh.clear();
791 
792  //Read back mesh
793  ok = OpenMesh::IO::read_mesh( mesh, "persistence-check.om" );
794  EXPECT_TRUE(ok) << "Cannot read mesh from file 'persistent-check.om'";
795 
796  // check props
797  EXPECT_TRUE(fill_props(mesh, vprop_float, true)) << "property not filled correctly";
798  EXPECT_TRUE(fill_props(mesh, eprop_bool, true)) << "property not filled correctly";
799  EXPECT_TRUE(fill_props(mesh, fprop_string, true)) << "property not filled correctly";
800  EXPECT_TRUE(fill_props(mesh, hprop_mydata, true)) << "property not filled correctly";
801  EXPECT_TRUE(fill_props(mesh, mprop_map, true)) << "property not filled correctly";
802 }
803 
804 /*Testcase for code snippet from flipping edges in triangle meshes
805  * */
806 TEST_F(OpenMeshTutorials, flipping_edges) {
807  Mesh mesh;
808  // Add some vertices
809  Mesh::VertexHandle vhandle[4];
810  vhandle[0] = mesh.add_vertex(MyMesh::Point(0, 0, 0));
811  vhandle[1] = mesh.add_vertex(MyMesh::Point(0, 1, 0));
812  vhandle[2] = mesh.add_vertex(MyMesh::Point(1, 1, 0));
813  vhandle[3] = mesh.add_vertex(MyMesh::Point(1, 0, 0));
814  // Add two faces
815  std::vector<Mesh::VertexHandle> face_vhandles;
816  face_vhandles.push_back(vhandle[2]);
817  face_vhandles.push_back(vhandle[1]);
818  face_vhandles.push_back(vhandle[0]);
819  mesh.add_face(face_vhandles);
820  face_vhandles.clear();
821  face_vhandles.push_back(vhandle[2]);
822  face_vhandles.push_back(vhandle[0]);
823  face_vhandles.push_back(vhandle[3]);
824  mesh.add_face(face_vhandles);
825  // Now the edge adjacent to the two faces connects
826  // vertex vhandle[0] and vhandle[2].
827  // Find this edge and then flip it
828  for(Mesh::EdgeIter it = mesh.edges_begin(); it != mesh.edges_end(); ++it) {
829  if(!mesh.is_boundary(*it)) {
830  // Flip edge
831  EXPECT_EQ(vhandle[2].idx(), mesh.to_vertex_handle(mesh.halfedge_handle(*it,0)).idx()) << "expected vertex handle 2!" ;
832  EXPECT_EQ(vhandle[0].idx(), mesh.to_vertex_handle(mesh.halfedge_handle(*it,1)).idx()) << "expected vertex handle 0!" ;
833  mesh.flip(*it);
834  EXPECT_EQ(vhandle[1].idx(), mesh.to_vertex_handle(mesh.halfedge_handle(*it,0)).idx()) << "expected vertex handle 1 (did the flip work?)!" ;
835  EXPECT_EQ(vhandle[3].idx(), mesh.to_vertex_handle(mesh.halfedge_handle(*it,1)).idx()) << "expected vertex handle 3 (did the flip work?)!" ;
836  }
837  }
838  // The edge now connects vertex vhandle[1] and vhandle[3].
839 }
840 
841 /*Testcase for code snippet from collapsing edges in triangle meshes
842  * */
843 TEST_F(OpenMeshTutorials, collapsing_edges) {
844  PolyMesh mesh;
845  mesh.request_vertex_status();
846  mesh.request_edge_status();
847  // Add some vertices as in the illustration above
848  PolyMesh::VertexHandle vhandle[7];
849  vhandle[0] = mesh.add_vertex(MyMesh::Point(-1, 1, 0));
850  vhandle[1] = mesh.add_vertex(MyMesh::Point(-1, 3, 0));
851  vhandle[2] = mesh.add_vertex(MyMesh::Point(0, 0, 0));
852  vhandle[3] = mesh.add_vertex(MyMesh::Point(0, 2, 0));
853  vhandle[4] = mesh.add_vertex(MyMesh::Point(0, 4, 0));
854  vhandle[5] = mesh.add_vertex(MyMesh::Point(1, 1, 0));
855  vhandle[6] = mesh.add_vertex(MyMesh::Point(1, 3, 0));
856  // Add three quad faces
857  std::vector<PolyMesh::VertexHandle> face_vhandles;
858  face_vhandles.push_back(vhandle[1]);
859  face_vhandles.push_back(vhandle[0]);
860  face_vhandles.push_back(vhandle[2]);
861  face_vhandles.push_back(vhandle[3]);
862  mesh.add_face(face_vhandles);
863  face_vhandles.clear();
864  face_vhandles.push_back(vhandle[1]);
865  face_vhandles.push_back(vhandle[3]);
866  face_vhandles.push_back(vhandle[5]);
867  face_vhandles.push_back(vhandle[4]);
868  mesh.add_face(face_vhandles);
869  face_vhandles.clear();
870  face_vhandles.push_back(vhandle[3]);
871  face_vhandles.push_back(vhandle[2]);
872  face_vhandles.push_back(vhandle[6]);
873  face_vhandles.push_back(vhandle[5]);
874  mesh.add_face(face_vhandles);
875  // Now find the edge between vertex vhandle[2]
876  // and vhandle[3]
877  for(PolyMesh::HalfedgeIter it = mesh.halfedges_begin(); it != mesh.halfedges_end(); ++it) {
878  if( mesh.to_vertex_handle(*it) == vhandle[3] &&
879  mesh.from_vertex_handle(*it) == vhandle[2])
880  {
881  // Collapse edge
882  mesh.collapse(*it);
883  break;
884  }
885  }
886  // Our mesh now looks like in the illustration above after the collapsing.
887 }
888 
889 }
Add storage for previous halfedge (halfedges). The bit is set by default in the DefaultTraits.
Definition: Attributes.hh:89
#define VertexTraits
Macro for defining the vertex traits. See Specifying your MyMesh.
Definition: Traits.hh:96
Add normals to mesh item (vertices/faces)
Definition: Attributes.hh:87
Has (r) / store (w) vertex normals.
Definition: Options.hh:109
#define HalfedgeAttributes(_i)
Macro for defining the halfedge attributes. See Specifying your MyMesh.
Definition: Traits.hh:87
T::value_type value_type
Type of the scalar value.
#define VertexAttributes(_i)
Macro for defining the vertex attributes. See Specifying your MyMesh.
Definition: Traits.hh:84
bool read_mesh(Mesh &_mesh, const std::string &_filename)
Read a mesh from file _filename.
Definition: MeshIO.hh:104
Add status to mesh item (all items)
Definition: Attributes.hh:90
Set options for reader/writer modules.
Definition: Options.hh:95
Kernel::VertexHandle VertexHandle
Handle for referencing the corresponding item.
Definition: PolyMeshT.hh:139
Kernel::VertexVertexIter VertexVertexIter
Circulator.
Definition: PolyMeshT.hh:165
VertexHandle add_vertex(const Point &_p)
Alias for new_vertex(const Point&).
Definition: PolyMeshT.hh:236
#define FaceAttributes(_i)
Macro for defining the face attributes. See Specifying your MyMesh.
Definition: Traits.hh:93
Kernel::Point Point
Coordinate type.
Definition: PolyMeshT.hh:115
#define EdgeAttributes(_i)
Macro for defining the edge attributes. See Specifying your MyMesh.
Definition: Traits.hh:90
Kernel::Scalar Scalar
Scalar type.
Definition: PolyMeshT.hh:113
bool write_mesh(const Mesh &_mesh, const std::string &_filename, Options _opt=Options::Default, std::streamsize _precision=6)
Write a mesh to the file _filename.
Definition: MeshIO.hh:199