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