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