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 {
280 HalfedgeAttributes(OpenMesh::Attributes::PrevHalfedge);
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
303 VertexTraits
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 {
316 VertexAttributes(OpenMesh::Attributes::Status);
317 FaceAttributes(OpenMesh::Attributes::Status);
318 EdgeAttributes(OpenMesh::Attributes::Status);
319 };
320
321 /*
322 * ====================================================================
323 * Specify our meshes
324 * ====================================================================
325 */
326 typedef OpenMesh::PolyMesh_ArrayKernelT<> MyMesh;
327 typedef OpenMesh::TriMesh_ArrayKernelT<MyTraits> MyMeshWithTraits;
328 typedef OpenMesh::TriMesh_ArrayKernelT<MyTraits> MyTriMesh;
329 typedef OpenMesh::TriMesh_ArrayKernelT<MyFancyTraits> MyFancyTriMesh;
330 typedef OpenMesh::TriMesh_ArrayKernelT<MyTraitsWithCOG> MyTriMeshWithCOG;
331 typedef OpenMesh::PolyMesh_ArrayKernelT<MyTraitsWithStatus> MyMeshWithStatus;
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());
415 MyMesh::VertexVertexIter vv_it;
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, "output.off");
450 EXPECT_TRUE(ok) << "Cannot read mesh from file 'output.off'";
451
452 const int iterations = 100;
453
454 {
455 // Add a vertex property storing the computed centers of gravity
456 auto cog = OpenMesh::makeTemporaryProperty<OpenMesh::VertexHandle, 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, "output.off");
488 EXPECT_TRUE(ok) << "Cannot read mesh from file 'output.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
505 OpenMesh::IO::Options opt;
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
537 EXPECT_TRUE(typeid( OpenMesh::vector_traits<MyFancyTriMesh::Point>::value_type ) ==
538 typeid(double)) << "Data type is wrong";
539
540 // Make sure that normal element type is double
541 EXPECT_TRUE(typeid( OpenMesh::vector_traits<MyFancyTriMesh::Normal>::value_type ) ==
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
550 OpenMesh::IO::Options opt;
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;
745 OpenMesh::FPropHandleT<std::string> fprop_string;
746 OpenMesh::HPropHandleT<MyData> hprop_mydata;
747 OpenMesh::MPropHandleT<MyMap> mprop_map;
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 }
OpenMesh::TriMesh_ArrayKernelT< CustomTraits >
OpenMesh::EPropHandleT
Definition: Property.hh:510
OpenMesh::IO::size_of
size_t size_of(const T &_v)
Definition: StoreRestore.hh:89
OpenMesh::PolyMeshT::Point
Kernel::Point Point
Coordinate type.
Definition: PolyMeshT.hh:112
OpenMesh::PolyMeshT::add_vertex
VertexHandle add_vertex(const Point &_p)
Alias for new_vertex(const Point&).
Definition: PolyMeshT.hh:235
OpenMesh::IO::is_streamable
bool is_streamable(void)
Definition: StoreRestore.hh:81
OpenMesh::PolyMeshT::Scalar
Kernel::Scalar Scalar
Scalar type.
Definition: PolyMeshT.hh:110
OpenMesh::IO::write_mesh
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:207
OpenMesh::DefaultTraits
Definition: Traits.hh:121
OpenMesh::PolyMeshT::VertexHandle
Kernel::VertexHandle VertexHandle
Handle for referencing the corresponding item.
Definition: PolyMeshT.hh:136
VertexTraits
#define VertexTraits
Macro for defining the vertex traits. See Specifying your MyMesh.
Definition: Traits.hh:91
OpenMesh::PolyMeshT::VertexVertexIter
Kernel::VertexVertexIter VertexVertexIter
Circulator.
Definition: PolyMeshT.hh:162
MyData
Definition: unittests_tutorials.cc:16
OpenMeshBase
Definition: unittests_common.hh:26
HalfedgeAttributes
#define HalfedgeAttributes(_i)
Macro for defining the halfedge attributes. See Specifying your MyMesh.
Definition: Traits.hh:82
OpenMesh::PolyMesh_ArrayKernelT
Definition: PolyMesh_ArrayKernelT.hh:94
OpenMesh::MPropHandleT
Definition: Property.hh:538
OpenMesh::IO::binary
Definition: SR_binary.hh:100
EdgeAttributes
#define EdgeAttributes(_i)
Macro for defining the edge attributes. See Specifying your MyMesh.
Definition: Traits.hh:85
OpenMesh::IO::Options
Set options for reader/writer modules.
Definition: Options.hh:90
OpenMesh::HPropHandleT
Definition: Property.hh:496
OpenMesh::Attributes::Status
Add status to mesh item (all items)
Definition: Attributes.hh:85
OpenMesh::VectorT< float, 4 >
OpenMesh::IO::Options::VertexNormal
Has (r) / store (w) vertex normals.
Definition: Options.hh:104
OpenMesh::Attributes::PrevHalfedge
Add storage for previous halfedge (halfedges). The bit is set by default in the DefaultTraits.
Definition: Attributes.hh:84
OpenMesh::vector_traits::value_type
T::value_type value_type
Type of the scalar value.
Definition: vector_traits.hh:94
OpenMesh::IO::read_mesh
bool read_mesh(Mesh &_mesh, const std::string &_filename)
Read a mesh from file _filename.
Definition: MeshIO.hh:112
OpenMesh
Definition: MeshItems.hh:59
VertexAttributes
#define VertexAttributes(_i)
Macro for defining the vertex attributes. See Specifying your MyMesh.
Definition: Traits.hh:79
MyTraits
Definition: mymesh.cc:5
OpenMesh::FPropHandleT
Definition: Property.hh:524
OpenMesh::VPropHandleT
Definition: Property.hh:482
FaceAttributes
#define FaceAttributes(_i)
Macro for defining the face attributes. See Specifying your MyMesh.
Definition: Traits.hh:88