Developer Documentation
unittests_smart_handles.cc
1 #include <gtest/gtest.h>
2 #include <Unittests/unittests_common.hh>
3 
4 #include <OpenMesh/Core/Mesh/SmartHandles.hh>
5 
6 #include <iostream>
7 #include <chrono>
8 
9 namespace {
10 
11 class OpenMeshSmartHandles : public OpenMeshBase {
12 
13 protected:
14 
15  // This function is called before each test is run
16  virtual void SetUp() {
17 
18  mesh_.clear();
19 
20  // Add some vertices
21  Mesh::VertexHandle vhandle[8];
22  vhandle[0] = mesh_.add_vertex(Mesh::Point(-1, -1, 1));
23  vhandle[1] = mesh_.add_vertex(Mesh::Point( 1, -1, 1));
24  vhandle[2] = mesh_.add_vertex(Mesh::Point( 1, 1, 1));
25  vhandle[3] = mesh_.add_vertex(Mesh::Point(-1, 1, 1));
26  vhandle[4] = mesh_.add_vertex(Mesh::Point(-1, -1, -1));
27  vhandle[5] = mesh_.add_vertex(Mesh::Point( 1, -1, -1));
28  vhandle[6] = mesh_.add_vertex(Mesh::Point( 1, 1, -1));
29  vhandle[7] = mesh_.add_vertex(Mesh::Point(-1, 1, -1));
30 
31  // Add six faces to form a cube
32  std::vector<Mesh::VertexHandle> face_vhandles;
33 
34  face_vhandles.clear();
35  face_vhandles.push_back(vhandle[0]);
36  face_vhandles.push_back(vhandle[1]);
37  face_vhandles.push_back(vhandle[3]);
38  mesh_.add_face(face_vhandles);
39 
40  face_vhandles.clear();
41  face_vhandles.push_back(vhandle[1]);
42  face_vhandles.push_back(vhandle[2]);
43  face_vhandles.push_back(vhandle[3]);
44  mesh_.add_face(face_vhandles);
45 
46  //=======================
47 
48  face_vhandles.clear();
49  face_vhandles.push_back(vhandle[7]);
50  face_vhandles.push_back(vhandle[6]);
51  face_vhandles.push_back(vhandle[5]);
52  mesh_.add_face(face_vhandles);
53 
54  face_vhandles.clear();
55  face_vhandles.push_back(vhandle[7]);
56  face_vhandles.push_back(vhandle[5]);
57  face_vhandles.push_back(vhandle[4]);
58  mesh_.add_face(face_vhandles);
59 
60  //=======================
61 
62  face_vhandles.clear();
63  face_vhandles.push_back(vhandle[1]);
64  face_vhandles.push_back(vhandle[0]);
65  face_vhandles.push_back(vhandle[4]);
66  mesh_.add_face(face_vhandles);
67 
68  face_vhandles.clear();
69  face_vhandles.push_back(vhandle[1]);
70  face_vhandles.push_back(vhandle[4]);
71  face_vhandles.push_back(vhandle[5]);
72  mesh_.add_face(face_vhandles);
73 
74  //=======================
75 
76  face_vhandles.clear();
77  face_vhandles.push_back(vhandle[2]);
78  face_vhandles.push_back(vhandle[1]);
79  face_vhandles.push_back(vhandle[5]);
80  mesh_.add_face(face_vhandles);
81 
82  face_vhandles.clear();
83  face_vhandles.push_back(vhandle[2]);
84  face_vhandles.push_back(vhandle[5]);
85  face_vhandles.push_back(vhandle[6]);
86  mesh_.add_face(face_vhandles);
87 
88 
89  //=======================
90 
91  face_vhandles.clear();
92  face_vhandles.push_back(vhandle[3]);
93  face_vhandles.push_back(vhandle[2]);
94  face_vhandles.push_back(vhandle[6]);
95  mesh_.add_face(face_vhandles);
96 
97  face_vhandles.clear();
98  face_vhandles.push_back(vhandle[3]);
99  face_vhandles.push_back(vhandle[6]);
100  face_vhandles.push_back(vhandle[7]);
101  mesh_.add_face(face_vhandles);
102 
103  //=======================
104 
105  face_vhandles.clear();
106  face_vhandles.push_back(vhandle[0]);
107  face_vhandles.push_back(vhandle[3]);
108  face_vhandles.push_back(vhandle[7]);
109  mesh_.add_face(face_vhandles);
110 
111  face_vhandles.clear();
112  face_vhandles.push_back(vhandle[0]);
113  face_vhandles.push_back(vhandle[7]);
114  face_vhandles.push_back(vhandle[4]);
115  mesh_.add_face(face_vhandles);
116 
117 
118  // Test setup:
119  //
120  //
121  // 3 ======== 2
122  // / /|
123  // / / | z
124  // 0 ======== 1 | |
125  // | | | | y
126  // | 7 | 6 | /
127  // | | / | /
128  // | |/ |/
129  // 4 ======== 5 -------> x
130  //
131 
132  // Check setup
133  EXPECT_EQ(18u, mesh_.n_edges() ) << "Wrong number of Edges";
134  EXPECT_EQ(36u, mesh_.n_halfedges() ) << "Wrong number of HalfEdges";
135  EXPECT_EQ(8u, mesh_.n_vertices() ) << "Wrong number of vertices";
136  EXPECT_EQ(12u, mesh_.n_faces() ) << "Wrong number of faces";
137  }
138 
139  // This function is called after all tests are through
140  virtual void TearDown() {
141 
142  // Do some final stuff with the member data here...
143 
144  mesh_.clear();
145  }
146 
147  // Member already defined in OpenMeshBase
148  //Mesh mesh_;
149 };
150 
151 /*
152  * ====================================================================
153  * Define tests below
154  * ====================================================================
155  */
156 
157 
158 
159 /* Test if navigation operations on smart handles yield the expected element
160  */
161 TEST_F(OpenMeshSmartHandles, SimpleNavigation)
162 {
163  for (auto vh : mesh_.vertices())
164  {
165  EXPECT_EQ(mesh_.halfedge_handle(vh), vh.halfedge()) << "outgoing halfedge of vertex does not match";
166  }
167 
168  for (auto heh : mesh_.halfedges())
169  {
170  EXPECT_EQ(mesh_.next_halfedge_handle(heh), heh.next()) << "next halfedge of halfedge does not match";
171  EXPECT_EQ(mesh_.prev_halfedge_handle(heh), heh.prev()) << "prevt halfedge of halfedge does not match";
172  EXPECT_EQ(mesh_.opposite_halfedge_handle(heh), heh.opp()) << "opposite halfedge of halfedge does not match";
173  EXPECT_EQ(mesh_.to_vertex_handle(heh), heh.to()) << "to vertex handle of halfedge does not match";
174  EXPECT_EQ(mesh_.from_vertex_handle(heh), heh.from()) << "from vertex handle of halfedge does not match";
175  EXPECT_EQ(mesh_.face_handle(heh), heh.face()) << "face handle of halfedge does not match";
176  }
177 
178  for (auto eh : mesh_.edges())
179  {
180  EXPECT_EQ(mesh_.halfedge_handle(eh, 0), eh.h0()) << "halfedge 0 of edge does not match";
181  EXPECT_EQ(mesh_.halfedge_handle(eh, 1), eh.h1()) << "halfedge 1 of edge does not match";
182  EXPECT_EQ(mesh_.from_vertex_handle(mesh_.halfedge_handle(eh, 0)), eh.v0()) << "first vertex of edge does not match";
183  EXPECT_EQ(mesh_.to_vertex_handle (mesh_.halfedge_handle(eh, 0)), eh.v1()) << "second vertex of edge does not match";
184  }
185 
186  for (auto fh : mesh_.faces())
187  {
188  EXPECT_EQ(mesh_.halfedge_handle(fh), fh.halfedge()) << "halfedge of face does not match";
189  }
190 }
191 
192 
193 /* Test if ranges yield the same elements when using smart handles
194  */
195 TEST_F(OpenMeshSmartHandles, SimpleRanges)
196 {
197  for (auto vh : mesh_.vertices())
198  {
199  {
200  std::vector<OpenMesh::VertexHandle> handles0;
201  std::vector<OpenMesh::VertexHandle> handles1;
202  for (auto h : mesh_.vv_range(vh))
203  handles0.push_back(h);
204  for (auto h : vh.vertices())
205  handles1.push_back(h);
206  EXPECT_EQ(handles0, handles1) << "vertex range of vertex does not match";
207  }
208  {
209  std::vector<OpenMesh::HalfedgeHandle> handles0;
210  std::vector<OpenMesh::HalfedgeHandle> handles1;
211  for (auto h : mesh_.voh_range(vh))
212  handles0.push_back(h);
213  for (auto h : vh.outgoing_halfedges())
214  handles1.push_back(h);
215  EXPECT_EQ(handles0, handles1) << "outgoing halfedge range of vertex does not match";
216  }
217  {
218  std::vector<OpenMesh::HalfedgeHandle> handles0;
219  std::vector<OpenMesh::HalfedgeHandle> handles1;
220  for (auto h : mesh_.vih_range(vh))
221  handles0.push_back(h);
222  for (auto h : vh.incoming_halfedges())
223  handles1.push_back(h);
224  EXPECT_EQ(handles0, handles1) << "incoming halfedge range of vertex does not match";
225  }
226  {
227  std::vector<OpenMesh::EdgeHandle> handles0;
228  std::vector<OpenMesh::EdgeHandle> handles1;
229  for (auto h : mesh_.ve_range(vh))
230  handles0.push_back(h);
231  for (auto h : vh.edges())
232  handles1.push_back(h);
233  EXPECT_EQ(handles0, handles1) << "edge range of vertex does not match";
234  }
235  {
236  std::vector<OpenMesh::FaceHandle> handles0;
237  std::vector<OpenMesh::FaceHandle> handles1;
238  for (auto h : mesh_.vf_range(vh))
239  handles0.push_back(h);
240  for (auto h : vh.faces())
241  handles1.push_back(h);
242  EXPECT_EQ(handles0, handles1) << "face range of vertex does not match";
243  }
244  }
245 
246  for (auto fh : mesh_.faces())
247  {
248  {
249  std::vector<OpenMesh::VertexHandle> handles0;
250  std::vector<OpenMesh::VertexHandle> handles1;
251  for (auto h : mesh_.fv_range(fh))
252  handles0.push_back(h);
253  for (auto h : fh.vertices())
254  handles1.push_back(h);
255  EXPECT_EQ(handles0, handles1) << "vertex range of face does not match";
256  }
257  {
258  std::vector<OpenMesh::HalfedgeHandle> handles0;
259  std::vector<OpenMesh::HalfedgeHandle> handles1;
260  for (auto h : mesh_.fh_range(fh))
261  handles0.push_back(h);
262  for (auto h : fh.halfedges())
263  handles1.push_back(h);
264  EXPECT_EQ(handles0, handles1) << "halfedge range of face does not match";
265  }
266  {
267  std::vector<OpenMesh::EdgeHandle> handles0;
268  std::vector<OpenMesh::EdgeHandle> handles1;
269  for (auto h : mesh_.fe_range(fh))
270  handles0.push_back(h);
271  for (auto h : fh.edges())
272  handles1.push_back(h);
273  EXPECT_EQ(handles0, handles1) << "edge range of face does not match";
274  }
275  {
276  std::vector<OpenMesh::FaceHandle> handles0;
277  std::vector<OpenMesh::FaceHandle> handles1;
278  for (auto h : mesh_.ff_range(fh))
279  handles0.push_back(h);
280  for (auto h : fh.faces())
281  handles1.push_back(h);
282  EXPECT_EQ(handles0, handles1) << "face range of face does not match";
283  }
284  }
285 }
286 
287 /* Test if ranges yield the same elements when using smart handles
288  */
289 TEST_F(OpenMeshSmartHandles, RangesOfRanges)
290 {
291  for (auto vh : mesh_.vertices())
292  {
293  {
294  std::vector<OpenMesh::VertexHandle> handles0;
295  std::vector<OpenMesh::VertexHandle> handles1;
296  for (auto h : mesh_.vv_range(vh))
297  for (auto h2 : mesh_.vv_range(h))
298  handles0.push_back(h2);
299  for (auto h : vh.vertices())
300  for (auto h2 : h.vertices())
301  handles1.push_back(h2);
302  EXPECT_EQ(handles0, handles1) << "vertex range of vertex range does not match";
303  }
304  {
305  std::vector<OpenMesh::HalfedgeHandle> handles0;
306  std::vector<OpenMesh::HalfedgeHandle> handles1;
307  for (auto h : mesh_.vv_range(vh))
308  for (auto h2 : mesh_.voh_range(h))
309  handles0.push_back(h2);
310  for (auto h : vh.vertices())
311  for (auto h2 : h.outgoing_halfedges())
312  handles1.push_back(h2);
313  EXPECT_EQ(handles0, handles1) << "outgoing halfedge range of vertex range does not match";
314  }
315  {
316  std::vector<OpenMesh::HalfedgeHandle> handles0;
317  std::vector<OpenMesh::HalfedgeHandle> handles1;
318  for (auto h : mesh_.vv_range(vh))
319  for (auto h2 : mesh_.vih_range(h))
320  handles0.push_back(h2);
321  for (auto h : vh.vertices())
322  for (auto h2 : h.incoming_halfedges())
323  handles1.push_back(h2);
324  EXPECT_EQ(handles0, handles1) << "incoming halfedge range of vertex range does not match";
325  }
326  {
327  std::vector<OpenMesh::EdgeHandle> handles0;
328  std::vector<OpenMesh::EdgeHandle> handles1;
329  for (auto h : mesh_.vv_range(vh))
330  for (auto h2 : mesh_.ve_range(h))
331  handles0.push_back(h2);
332  for (auto h : vh.vertices())
333  for (auto h2 : h.edges())
334  handles1.push_back(h2);
335  EXPECT_EQ(handles0, handles1) << "edge range of vertex range does not match";
336  }
337  {
338  std::vector<OpenMesh::FaceHandle> handles0;
339  std::vector<OpenMesh::FaceHandle> handles1;
340  for (auto h : mesh_.vv_range(vh))
341  for (auto h2 : mesh_.vf_range(h))
342  handles0.push_back(h2);
343  for (auto h : vh.vertices())
344  for (auto h2 : h.faces())
345  handles1.push_back(h2);
346  EXPECT_EQ(handles0, handles1) << "face range of vertex range does not match";
347  }
348  {
349  std::vector<OpenMesh::VertexHandle> handles0;
350  std::vector<OpenMesh::VertexHandle> handles1;
351  for (auto h : mesh_.vf_range(vh))
352  for (auto h2 : mesh_.fv_range(h))
353  handles0.push_back(h2);
354  for (auto h : vh.faces())
355  for (auto h2 : h.vertices())
356  handles1.push_back(h2);
357  EXPECT_EQ(handles0, handles1) << "vertex range of face range does not match";
358  }
359  {
360  std::vector<OpenMesh::HalfedgeHandle> handles0;
361  std::vector<OpenMesh::HalfedgeHandle> handles1;
362  for (auto h : mesh_.vf_range(vh))
363  for (auto h2 : mesh_.fh_range(h))
364  handles0.push_back(h2);
365  for (auto h : vh.faces())
366  for (auto h2 : h.halfedges())
367  handles1.push_back(h2);
368  EXPECT_EQ(handles0, handles1) << "vertex range of face range does not match";
369  }
370  {
371  std::vector<OpenMesh::FaceHandle> handles0;
372  std::vector<OpenMesh::FaceHandle> handles1;
373  for (auto h : mesh_.vf_range(vh))
374  for (auto h2 : mesh_.ff_range(h))
375  handles0.push_back(h2);
376  for (auto h : vh.faces())
377  for (auto h2 : h.faces())
378  handles1.push_back(h2);
379  EXPECT_EQ(handles0, handles1) << "vertex range of face range does not match";
380  }
381  }
382 }
383 
384 
385 /* Test a chain of navigation on a cube
386  */
387 TEST_F(OpenMeshSmartHandles, ComplicatedNavigtaion)
388 {
389  for (auto vh : mesh_.vertices())
390  {
391  EXPECT_EQ(mesh_.next_halfedge_handle(
392  mesh_.opposite_halfedge_handle(
393  mesh_.halfedge_handle(vh))),
394  vh.out().opp().next());
395  EXPECT_EQ(mesh_.prev_halfedge_handle(
396  mesh_.prev_halfedge_handle(
397  mesh_.opposite_halfedge_handle(
398  mesh_.next_halfedge_handle(
399  mesh_.next_halfedge_handle(
400  mesh_.halfedge_handle(vh)))))),
401  vh.out().next().next().opp().prev().prev());
402  EXPECT_EQ(mesh_.face_handle(
403  mesh_.opposite_halfedge_handle(
404  mesh_.halfedge_handle(
405  mesh_.face_handle(
406  mesh_.opposite_halfedge_handle(
407  mesh_.next_halfedge_handle(
408  mesh_.halfedge_handle(vh))))))),
409  vh.out().next().opp().face().halfedge().opp().face());
410  }
411 }
412 
413 
414 /* Test performance of smart handles
415  */
416 TEST_F(OpenMeshSmartHandles, Performance)
417 {
418 #if DEBUG
419  int n_tests = 300000;
420 #else
421  int n_tests = 10000000;
422 #endif
423 
424  auto t_before_old = std::chrono::high_resolution_clock::now();
425 
426  std::vector<OpenMesh::HalfedgeHandle> halfedges0;
427  for (int i = 0; i < n_tests; ++i)
428  {
429  for (auto vh : mesh_.vertices())
430  {
431  auto heh = mesh_.prev_halfedge_handle(
432  mesh_.prev_halfedge_handle(
433  mesh_.opposite_halfedge_handle(
434  mesh_.next_halfedge_handle(
435  mesh_.next_halfedge_handle(
436  mesh_.halfedge_handle(vh))))));
437  if (i == 0)
438  halfedges0.push_back(heh);
439  }
440  }
441 
442  auto t_after_old = std::chrono::high_resolution_clock::now();
443 
444  std::vector<OpenMesh::HalfedgeHandle> halfedges1;
445  for (int i = 0; i < n_tests; ++i)
446  {
447  for (auto vh : mesh_.vertices())
448  {
449  auto heh = vh.out().next().next().opp().prev().prev();
450  if (i == 0)
451  halfedges1.push_back(heh);
452  }
453  }
454 
455  auto t_after_new = std::chrono::high_resolution_clock::now();
456 
457  std::cout << "Conventional navigation took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_after_old-t_before_old).count() << "ms" << std::endl;
458  std::cout << "SmartHandle navigation took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_after_new-t_after_old ).count() << "ms" << std::endl;
459 
460  EXPECT_EQ(halfedges0, halfedges1) << "halfedges do not match";
461 
462 }
463 
464 
465 /* Mix old and new api
466  */
467 TEST_F(OpenMeshSmartHandles, MixOldAndNew)
468 {
469  for (OpenMesh::SmartHalfedgeHandle heh : mesh_.halfedges())
470  {
471  heh = mesh_.opposite_halfedge_handle(heh);
472  EXPECT_TRUE((std::is_same<OpenMesh::SmartEdgeHandle, decltype(OpenMesh::PolyConnectivity::s_edge_handle(heh))>::value));
473  EXPECT_TRUE((std::is_same<OpenMesh::SmartEdgeHandle, decltype(mesh_.edge_handle(heh))>::value));
474  EXPECT_TRUE((std::is_same<OpenMesh::SmartFaceHandle, decltype(mesh_.face_handle(heh))>::value));
475  }
476 }
477 
478 
479 
480 /* comparability
481  */
482 TEST_F(OpenMeshSmartHandles, ComparisionBetweenSmartHandleAndNormalHandles)
483 {
485  OpenMesh::SmartVertexHandle svh(0, &mesh_);
486  EXPECT_EQ(vh, svh) << "Vertex handle and smart vertex handle are different";
487 
488  std::vector<OpenMesh::VertexHandle> vertices = mesh_.vertices().to_vector([](OpenMesh::SmartVertexHandle _svh) { return OpenMesh::VertexHandle(_svh); });
489 
490  std::replace(vertices.begin(), vertices.end(), OpenMesh::VertexHandle(0), OpenMesh::VertexHandle(1));
491  EXPECT_EQ(vertices[0], OpenMesh::VertexHandle(1));
492 
493  std::vector<OpenMesh::SmartVertexHandle> smart_vertices = mesh_.vertices().to_vector();
494 
495  std::replace(smart_vertices.begin(), smart_vertices.end(), OpenMesh::SmartVertexHandle(0, &mesh_), OpenMesh::SmartVertexHandle(1, &mesh_));
496  EXPECT_EQ(smart_vertices[0], OpenMesh::VertexHandle(1));
497  EXPECT_EQ(smart_vertices[0], OpenMesh::SmartVertexHandle(1, &mesh_));
498 
499  std::replace(vertices.begin(), vertices.end(), OpenMesh::SmartVertexHandle(1, &mesh_), OpenMesh::SmartVertexHandle(2, &mesh_));
500  EXPECT_EQ(vertices[0], OpenMesh::VertexHandle(2));
501 
502 }
503 
504 TEST(OpenMeshSmartHandlesNoFixture, AddingFacesPolyMesh)
505 {
507 
508  MyMesh mesh;
509 
510  std::vector<OpenMesh::SmartVertexHandle> vertices;
511  for (int i = 0; i < 4; ++i)
512  vertices.push_back(mesh.add_vertex(MyMesh::Point()));
513 
514  auto fh = mesh.add_face(vertices);
515 
516  for (auto heh : fh.halfedges())
517  {
518  heh = heh.next();
519  }
520 }
521 
522 TEST(OpenMeshSmartHandlesNoFixture, AddingFacesTriMesh)
523 {
525 
526  MyMesh mesh;
527 
528  std::vector<OpenMesh::SmartVertexHandle> vertices;
529  for (int i = 0; i < 4; ++i)
530  vertices.push_back(mesh.add_vertex(MyMesh::Point()));
531 
532  auto fh = mesh.add_face(vertices);
533 
534  for (auto heh : fh.halfedges())
535  {
536  heh = heh.next();
537  }
538 }
539 
540 TEST(OpenMeshSmartHandlesNoFixture, SplitTriMesh)
541 {
543 
544  MyMesh mesh;
545 
546  std::vector<OpenMesh::SmartVertexHandle> vertices;
547  for (int i = 0; i < 3; ++i)
548  vertices.push_back(mesh.add_vertex(MyMesh::Point()));
549 
550  auto fh = mesh.add_face(vertices);
551 
552  auto p = (MyMesh::Point());
553 
554  OpenMesh::SmartVertexHandle vh = mesh.split(fh, p);
556  OpenMesh::SmartVertexHandle vh2 = mesh.split(eh, p);
557 
558  EXPECT_NE(vh.idx(), vh2.idx()) << "This was only intended to fix an unused variable warning but cool that it caugth an actual error now";
559 
560 }
561 
562 
563 
564 
565 
566 }
SmartEdgeHandle edge() const
Returns incident edge of halfedge.
int idx() const
Get the underlying index of this handle.
Definition: Handles.hh:69
Kernel::Point Point
Coordinate type.
Definition: PolyMeshT.hh:112
SmartHalfedgeHandle halfedge(unsigned int _i) const
Returns one of the two halfedges of the edge.
Handle for a vertex entity.
Definition: Handles.hh:120
SmartVertexHandle add_vertex(const Point &_p)
Alias for new_vertex(const Point&).
Definition: PolyMeshT.hh:235
SmartVertexHandle split(EdgeHandle _eh, const Point &_p)
Edge split (= 2-to-4 split)
Definition: TriMeshT.hh:267
Smart version of VertexHandle contains a pointer to the corresponding mesh and allows easier access t...
Definition: SmartHandles.hh:84
Kernel::VertexHandle VertexHandle
Handle for referencing the corresponding item.
Definition: PolyMeshT.hh:136