Developer Documentation
unittests_propertymanager.cc
1 #include <gtest/gtest.h>
2 #include <Unittests/unittests_common.hh>
3 #include <OpenMesh/Core/Utils/PropertyManager.hh>
4 
5 #include <iostream>
6 
7 //#define ENABLE_PROPERTY_TIMING_OUTPUT
8 #ifdef ENABLE_PROPERTY_TIMING_OUTPUT
9 #define N_VERTICES_TIMING 1000000
10 #define TIMING_OUTPUT(X) X
11 #else
12 #define N_VERTICES_TIMING 10
13 #define TIMING_OUTPUT(X)
14 #endif
15 
16 namespace {
17 
18 class OpenMeshPropertyManager : public OpenMeshBase {
19 
20  protected:
21 
22  // This function is called before each test is run
23  virtual void SetUp() {
24  }
25 
26  // This function is called after all tests are through
27  virtual void TearDown() {
28 
29  // Do some final stuff with the member data here...
30  }
31 
32  // Member already defined in OpenMeshBase
33  //Mesh mesh_;
34 };
35 
36 /*
37  * ====================================================================
38  * General Tests
39  * ====================================================================
40  */
41 
42 /*
43  * Collapsing a tetrahedron
44  */
45 TEST_F(OpenMeshPropertyManager, set_range_bool) {
46 
47  mesh_.clear();
48 
49  // Add some vertices
50  Mesh::VertexHandle vhandle[4];
51 
52  vhandle[0] = mesh_.add_vertex(Mesh::Point(0, 0, 0));
53  vhandle[1] = mesh_.add_vertex(Mesh::Point(0, 1, 0));
54  vhandle[2] = mesh_.add_vertex(Mesh::Point(1, 1, 0));
55  vhandle[3] = mesh_.add_vertex(Mesh::Point(0, 0, 1));
56 
57  // Add two faces
58  std::vector<Mesh::VertexHandle> face_vhandles;
59 
60  face_vhandles.push_back(vhandle[0]);
61  face_vhandles.push_back(vhandle[1]);
62  face_vhandles.push_back(vhandle[2]);
63  mesh_.add_face(face_vhandles);
64 
65  face_vhandles.clear();
66 
67  face_vhandles.push_back(vhandle[0]);
68  face_vhandles.push_back(vhandle[2]);
69  face_vhandles.push_back(vhandle[3]);
70  mesh_.add_face(face_vhandles);
71 
72  {
74  OpenMesh::VPropHandleT<bool>> pm_v_bool(mesh_, "pm_v_bool");
75  pm_v_bool.set_range(mesh_.vertices_begin(), mesh_.vertices_end(), false);
76  for (int i = 0; i < 4; ++i)
77  ASSERT_FALSE(pm_v_bool[vhandle[i]]);
78  pm_v_bool.set_range(mesh_.vertices_begin(), mesh_.vertices_end(), true);
79  for (int i = 0; i < 4; ++i)
80  ASSERT_TRUE(pm_v_bool[vhandle[i]]);
81 
83  OpenMesh::EPropHandleT<bool>> pm_e_bool(mesh_, "pm_e_bool");
84  pm_e_bool.set_range(mesh_.edges_begin(), mesh_.edges_end(), false);
85  for (Mesh::EdgeIter e_it = mesh_.edges_begin(), f_end = mesh_.edges_end();
86  e_it != f_end; ++e_it)
87  ASSERT_FALSE(pm_e_bool[*e_it]);
88  pm_e_bool.set_range(mesh_.edges_begin(), mesh_.edges_end(), true);
89  for (Mesh::EdgeIter e_it = mesh_.edges_begin(), f_end = mesh_.edges_end();
90  e_it != f_end; ++e_it)
91  ASSERT_TRUE(pm_e_bool[*e_it]);
92 
94  OpenMesh::FPropHandleT<bool>> pm_f_bool(mesh_, "pm_f_bool");
95  pm_f_bool.set_range(mesh_.faces_begin(), mesh_.faces_end(), false);
96  for (Mesh::FaceIter f_it = mesh_.faces_begin(), f_end = mesh_.faces_end();
97  f_it != f_end; ++f_it)
98  ASSERT_FALSE(pm_f_bool[*f_it]);
99  pm_f_bool.set_range(mesh_.faces_begin(), mesh_.faces_end(), true);
100  for (Mesh::FaceIter f_it = mesh_.faces_begin(), f_end = mesh_.faces_end();
101  f_it != f_end; ++f_it)
102  ASSERT_TRUE(pm_f_bool[*f_it]);
103  }
104 
105  /*
106  * Same thing again, this time with C++11 ranges.
107  */
108  {
110  OpenMesh::VPropHandleT<bool>> pm_v_bool(mesh_, "pm_v_bool2");
111  pm_v_bool.set_range(mesh_.vertices(), false);
112  for (int i = 0; i < 4; ++i)
113  ASSERT_FALSE(pm_v_bool[vhandle[i]]);
114  pm_v_bool.set_range(mesh_.vertices(), true);
115  for (int i = 0; i < 4; ++i)
116  ASSERT_TRUE(pm_v_bool[vhandle[i]]);
117 
119  OpenMesh::EPropHandleT<bool>> pm_e_bool(mesh_, "pm_e_bool2");
120  pm_e_bool.set_range(mesh_.edges(), false);
121  for (Mesh::EdgeIter e_it = mesh_.edges_begin(), f_end = mesh_.edges_end();
122  e_it != f_end; ++e_it)
123  ASSERT_FALSE(pm_e_bool[*e_it]);
124  pm_e_bool.set_range(mesh_.edges(), true);
125  for (Mesh::EdgeIter e_it = mesh_.edges_begin(), f_end = mesh_.edges_end();
126  e_it != f_end; ++e_it)
127  ASSERT_TRUE(pm_e_bool[*e_it]);
128 
130  OpenMesh::FPropHandleT<bool>> pm_f_bool(mesh_, "pm_f_bool2");
131  pm_f_bool.set_range(mesh_.faces(), false);
132  for (Mesh::FaceIter f_it = mesh_.faces_begin(), f_end = mesh_.faces_end();
133  f_it != f_end; ++f_it)
134  ASSERT_FALSE(pm_f_bool[*f_it]);
135  pm_f_bool.set_range(mesh_.faces(), true);
136  for (Mesh::FaceIter f_it = mesh_.faces_begin(), f_end = mesh_.faces_end();
137  f_it != f_end; ++f_it)
138  ASSERT_TRUE(pm_f_bool[*f_it]);
139  }
140 }
141 
142 /*
143  * In sequence:
144  * - add a persistent property to a mesh
145  * - retrieve an existing property of a mesh and modify it
146  * - obtain a non-owning property handle
147  * - attempt to obtain a non-owning handle to a non-existing property (throws)
148  */
149 TEST_F(OpenMeshPropertyManager, cpp11_persistent_and_non_owning_properties) {
150  auto vh = mesh_.add_vertex({0,0,0}); // Dummy vertex to attach properties to
151 
152  const auto prop_name = "pm_v_test_property";
153 
154  ASSERT_FALSE((OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name)));
155 
156  {
157  auto prop = OpenMesh::getOrMakeProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name);
158  prop[vh] = 100;
159  // End of scope, property persists
160  }
161 
162  ASSERT_TRUE((OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name)));
163 
164  {
165  // Since a property of the same name and type already exists, this refers to the existing property.
166  auto prop = OpenMesh::getOrMakeProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name);
167  ASSERT_EQ(100, prop[vh]);
168  prop[vh] = 200;
169  // End of scope, property persists
170  }
171 
172  ASSERT_TRUE((OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name)));
173 
174  {
175  // Acquire non-owning handle to the property, knowing it exists
176  auto prop = OpenMesh::getProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name);
177  ASSERT_EQ(200, prop[vh]);
178  }
179 
180  ASSERT_TRUE((OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name)));
181 
182  {
183  // Attempt to acquire non-owning handle for a non-existing property
184  auto code_that_throws = [&](){
185  OpenMesh::getProperty<OpenMesh::VertexHandle, int>(mesh_, "wrong_prop_name");
186  };
187  ASSERT_THROW(code_that_throws(), std::runtime_error);
188  }
189 
190  ASSERT_TRUE((OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name)));
191 }
192 
193 
194 TEST_F(OpenMeshPropertyManager, property_copy_construction) {
195  for (int i = 0; i < N_VERTICES_TIMING; ++i)
196  mesh_.add_vertex(Mesh::Point());
197 
198  // unnamed
199  {
200  auto prop1 = OpenMesh::VProp<int>(mesh_);
201  for (auto vh : mesh_.vertices())
202  prop1[vh] = vh.idx()*2-13;
203 
204  auto prop2 = prop1; // prop1 and prop2 should be two different properties with the same content
205 
206  prop1.set_range(mesh_.vertices(), 0);
207 
208  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13);
209  }
210 
211  // named
212  {
213  auto prop1 = OpenMesh::VProp<int>(mesh_, "ids");
214  for (auto vh : mesh_.vertices())
215  prop1[vh] = vh.idx()*2-13;
216 
217  auto prop2 = prop1; // prop1 and prop2 should refere to the same property
218 
219  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13);
220 
221  prop1.set_range(mesh_.vertices(), 0);
222 
223  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0);
224  }
225 }
226 
227 TEST_F(OpenMeshPropertyManager, property_move_construction) {
228  for (int i = 0; i < N_VERTICES_TIMING; ++i)
229  mesh_.add_vertex(Mesh::Point());
230 
231  // unnamed
232  {
233  auto prop1 = OpenMesh::VProp<int>(mesh_);
234  for (auto vh : mesh_.vertices())
235  prop1[vh] = vh.idx()*2-13;
236 
237  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
238  auto prop2 = std::move(prop1);
239  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
240  TIMING_OUTPUT(std::cout << "move constructing property from temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
241 
242  EXPECT_FALSE(prop1.isValid()) << "prop1 should have been invalidated";
243 
244  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13);
245  }
246 
247  // named
248  {
249  auto prop1 = OpenMesh::VProp<int>(mesh_, "ids");
250  for (auto vh : mesh_.vertices())
251  prop1[vh] = vh.idx()*2-13;
252 
253  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
254  auto prop2 = std::move(prop1); // prop1 and prop2 should refere to the same property
255  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
256  TIMING_OUTPUT(std::cout << "move constructing from named took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
257 
258  EXPECT_TRUE(prop1.isValid()) << "named properties cannot be invalidated";
259 
260  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "property is not valid anymore";
261  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "did not copy property correctly";
262 
263  prop1.set_range(mesh_.vertices(), 0);
264 
265  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 0);
266  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0);
267  }
268 }
269 
270 
271 TEST_F(OpenMeshPropertyManager, property_copying_same_mesh) {
272 
273  for (int i = 0; i < N_VERTICES_TIMING; ++i)
274  mesh_.add_vertex(Mesh::Point());
275 
276  // unnamed to unnamed
277  {
278  auto prop1 = OpenMesh::VProp<int>(3, mesh_);
279  auto prop2 = OpenMesh::VProp<int>(0, mesh_);
280  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 3) << "Property not initialized correctly";
281  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
282 
283  for (auto vh : mesh_.vertices())
284  prop1[vh] = vh.idx()*2-13;
285 
286  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
287 
288  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
289  prop2 = prop1;
290  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
291  TIMING_OUTPUT(std::cout << "copying property temporary to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
292 
293  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Temporary property got destroyed";
294 
295  prop1.set_range(mesh_.vertices(), 0);
296 
297  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 0) << "Property not copied correctly";
298  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
299  }
300 
301  // unnamed to named
302  {
303  auto prop1 = OpenMesh::VProp<int>(mesh_);
304  auto prop2 = OpenMesh::VProp<int>(0, mesh_, "ids");
305  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
306 
307  for (auto vh : mesh_.vertices())
308  prop1[vh] = vh.idx()*2-13;
309 
310  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
311 
312  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
313  prop2 = prop1;
314  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
315  TIMING_OUTPUT(std::cout << "copying property temporary to named took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
316 
317  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Temporary property got destroyed";
318 
319  prop1.set_range(mesh_.vertices(), 0);
320 
321  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
322 
323  auto prop3 = OpenMesh::VProp<int>(mesh_, "ids");
324  EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], -13) << "property with name 'ids' was not correctly changed";
325  }
326 
327  // named to unnamed
328  {
329  auto prop1 = OpenMesh::VProp<int>(mesh_, "ids2");
330  auto prop2 = OpenMesh::VProp<int>(mesh_);
331  prop2.set_range(mesh_.vertices(), 0);
332  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
333 
334  for (auto vh : mesh_.vertices())
335  prop1[vh] = vh.idx()*2-13;
336 
337  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
338  prop2 = prop1;
339  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
340  TIMING_OUTPUT(std::cout << "copying property named to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
341 
342  prop1.set_range(mesh_.vertices(), 0);
343 
344  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
345 
346  }
347 
348  // named to named (different names)
349  {
350  auto prop1 = OpenMesh::VProp<int>(mesh_, "ids3");
351  auto prop2 = OpenMesh::VProp<int>(mesh_, "ids4");
352  prop2.set_range(mesh_.vertices(), 0);
353  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
354 
355  for (auto vh : mesh_.vertices())
356  prop1[vh] = vh.idx()*2-13;
357 
358  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
359 
360  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
361  prop2 = prop1;
362  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
363  TIMING_OUTPUT(std::cout << "copying property named to named with different name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
364 
365  prop1.set_range(mesh_.vertices(), 0);
366 
367  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
368  }
369 
370  // named to named (same names)
371  {
372  auto prop1 = OpenMesh::VProp<int>(mesh_, "ids5");
373  auto prop2 = OpenMesh::VProp<int>(mesh_, "ids5");
374 
375  for (auto vh : mesh_.vertices())
376  prop1[vh] = vh.idx()*2-13;
377 
378  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
379  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
380 
381  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
382  prop2 = prop1; // this should be a no op
383  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
384  TIMING_OUTPUT(std::cout << "copying property named to named with same name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
385 
386  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
387  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
388 
389  prop1.set_range(mesh_.vertices(), 42);
390 
391  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
392  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
393 
394  auto prop3 = OpenMesh::VProp<int>(mesh_, "ids5");
395  EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
396  }
397 
398  {
399  auto prop1 = OpenMesh::MProp<int>(mesh_);
400  *prop1 = 43;
401  auto prop2 = prop1;
402 
403  prop2 = prop1;
404 
405  prop2 = std::move(prop1);
406  }
407 }
408 
409 
410 TEST_F(OpenMeshPropertyManager, property_moving_same_mesh) {
411 
412  for (int i = 0; i < N_VERTICES_TIMING; ++i)
413  mesh_.add_vertex(Mesh::Point());
414 
415  // unnamed to unnamed
416  {
417  auto prop1 = OpenMesh::VProp<int>(mesh_);
418  auto prop2 = OpenMesh::VProp<int>(mesh_);
419  prop2.set_range(mesh_.vertices(), 0);
420 
421  for (auto vh : mesh_.vertices())
422  prop1[vh] = vh.idx()*2-13;
423 
424  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
425 
426  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
427  prop2 = std::move(prop1); // this should be cheap
428  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
429  TIMING_OUTPUT(std::cout << "moving property temporary to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
430 
431  EXPECT_FALSE(prop1.isValid()) << "prop1 not invalidated after moving";
432 
433  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
434  }
435 
436  // unnamed to named
437  {
438  auto prop1 = OpenMesh::VProp<int>(mesh_);
439  auto prop2 = OpenMesh::VProp<int>(mesh_, "ids");
440  prop2.set_range(mesh_.vertices(), 0);
441  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
442 
443  for (auto vh : mesh_.vertices())
444  prop1[vh] = vh.idx()*2-13;
445 
446  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
447 
448  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
449  prop2 = std::move(prop1);
450  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
451  TIMING_OUTPUT(std::cout << "moving property temporary to named took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
452 
453  EXPECT_FALSE(prop1.isValid()) << "prop1 not invalidated after moving";
454 
455  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
456 
457  auto prop3 = OpenMesh::VProp<int>(mesh_, "ids");
458  EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], -13) << "property with name 'ids' was not correctly changed";
459  }
460 
461  // named to unnamed
462  {
463  auto prop1 = OpenMesh::VProp<int>(mesh_, "ids2");
464  auto prop2 = OpenMesh::VProp<int>(mesh_);
465  prop2.set_range(mesh_.vertices(), 0);
466  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
467 
468  for (auto vh : mesh_.vertices())
469  prop1[vh] = vh.idx()*2-13;
470 
471  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
472  prop2 = std::move(prop1); // moving named properties will not invalidate the property and will copy the data
473  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
474  TIMING_OUTPUT(std::cout << "moving property named to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
475 
476  EXPECT_TRUE(prop1.isValid()) << "named prop1 should not be invalidated by moving";
477 
478  prop1.set_range(mesh_.vertices(), 0);
479 
480  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
481 
482  }
483 
484  // named to named (different names)
485  {
486  auto prop1 = OpenMesh::VProp<int>(mesh_, "ids3");
487  auto prop2 = OpenMesh::VProp<int>(mesh_, "ids4");
488  prop2.set_range(mesh_.vertices(), 0);
489  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
490 
491  for (auto vh : mesh_.vertices())
492  prop1[vh] = vh.idx()*2-13;
493 
494  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
495 
496  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
497  prop2 = std::move(prop1); // moving named properties will not invalidate the property and will copy the data
498  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
499  TIMING_OUTPUT(std::cout << "moving property named to named with different name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
500 
501  EXPECT_TRUE(prop1.isValid()) << "named prop1 should not be invalidated by moving";
502 
503  prop1.set_range(mesh_.vertices(), 0);
504 
505  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
506  }
507 
508  // named to named (same names)
509  {
510  auto prop1 = OpenMesh::VProp<int>(mesh_, "ids5");
511  auto prop2 = OpenMesh::VProp<int>(mesh_, "ids5");
512 
513  for (auto vh : mesh_.vertices())
514  prop1[vh] = vh.idx()*2-13;
515 
516  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
517  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
518 
519  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
520  prop2 = std::move(prop1); // this should be a no op
521  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
522  TIMING_OUTPUT(std::cout << "moving property named to named with same name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
523 
524  EXPECT_TRUE(prop1.isValid()) << "named prop1 should not be invalidated by moving";
525 
526  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
527  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
528 
529  prop1.set_range(mesh_.vertices(), 0);
530 
531  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 0) << "Property not copied correctly";
532  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not copied correctly";
533 
534  auto prop3 = OpenMesh::VProp<int>(mesh_, "ids5");
535  EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], 0) << "Property not copied correctly";
536  }
537 }
538 
539 
540 
541 TEST_F(OpenMeshPropertyManager, property_copying_different_mesh) {
542 
543  for (int i = 0; i < N_VERTICES_TIMING; ++i)
544  mesh_.add_vertex(Mesh::Point());
545 
546  auto copy = mesh_;
547  for (int i = 0; i < 10; ++i)
548  copy.add_vertex(Mesh::Point());
549 
550  // unnamed to unnamed
551  {
552  auto prop1 = OpenMesh::VProp<int>(3, mesh_);
553  auto prop2 = OpenMesh::VProp<int>(0, copy);
554  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 3) << "Property not initialized correctly";
555  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
556 
557  for (auto vh : mesh_.vertices())
558  prop1[vh] = vh.idx()*2-13;
559 
560  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
561 
562  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
563  prop2 = prop1;
564  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
565  TIMING_OUTPUT(std::cout << "copying property temporary to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
566 
567  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Temporary property got destroyed";
568 
569  prop1.set_range(mesh_.vertices(), 0);
570 
571  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 0) << "Property not copied correctly";
572  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
573  EXPECT_NO_FATAL_FAILURE(prop2[OpenMesh::VertexHandle(static_cast<int>(copy.n_vertices())-1)]) << "Property not correctly resized";
574  }
575 
576  // unnamed to named
577  {
578  auto prop1 = OpenMesh::VProp<int>(mesh_);
579  auto prop2 = OpenMesh::VProp<int>(0, copy, "ids");
580  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
581 
582  for (auto vh : mesh_.vertices())
583  prop1[vh] = vh.idx()*2-13;
584 
585  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
586 
587  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
588  prop2 = prop1;
589  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
590  TIMING_OUTPUT(std::cout << "copying property temporary to named took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
591 
592  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Temporary property got destroyed";
593 
594  prop1.set_range(mesh_.vertices(), 0);
595 
596  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
597 
598  auto prop3 = OpenMesh::VProp<int>(copy, "ids");
599  EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], -13) << "property with name 'ids' was not correctly changed";
600  }
601 
602  // named to unnamed
603  {
604  auto prop1 = OpenMesh::VProp<int>(mesh_, "ids2");
605  auto prop2 = OpenMesh::VProp<int>(copy);
606  prop2.set_range(mesh_.vertices(), 0);
607  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
608 
609  for (auto vh : mesh_.vertices())
610  prop1[vh] = vh.idx()*2-13;
611 
612  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
613  prop2 = prop1;
614  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
615  TIMING_OUTPUT(std::cout << "copying property named to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
616 
617  prop1.set_range(mesh_.vertices(), 0);
618 
619  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
620 
621  }
622 
623  // named to named (different names)
624  {
625  auto prop1 = OpenMesh::VProp<int>(mesh_, "ids3");
626  auto prop2 = OpenMesh::VProp<int>(copy, "ids4");
627  prop2.set_range(mesh_.vertices(), 0);
628  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
629 
630  for (auto vh : mesh_.vertices())
631  prop1[vh] = vh.idx()*2-13;
632 
633  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
634 
635  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
636  prop2 = prop1;
637  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
638  TIMING_OUTPUT(std::cout << "copying property named to named with different name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
639 
640  prop1.set_range(mesh_.vertices(), 0);
641 
642  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
643  }
644 
645  // named to named (same names)
646  {
647  auto prop1 = OpenMesh::VProp<int>(mesh_, "ids5");
648  auto prop2 = OpenMesh::VProp<int>(copy, "ids5");
649 
650  for (auto vh : mesh_.vertices())
651  prop1[vh] = vh.idx()*2-13;
652 
653  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
654 
655  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
656  prop2 = prop1; // this should be a no op
657  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
658  TIMING_OUTPUT(std::cout << "copying property named to named with same name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
659 
660  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
661  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
662 
663  prop1.set_range(mesh_.vertices(), 42);
664 
665  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
666  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
667 
668  auto prop3 = OpenMesh::VProp<int>(mesh_, "ids5");
669  EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
670  auto prop4 = OpenMesh::VProp<int>(copy, "ids5");
671  EXPECT_EQ(prop4[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
672  }
673 }
674 
675 
676 TEST_F(OpenMeshPropertyManager, property_moving_different_mesh) {
677 
678  for (int i = 0; i < N_VERTICES_TIMING; ++i)
679  mesh_.add_vertex(Mesh::Point());
680 
681  auto copy = mesh_;
682  for (int i = 0; i < 10; ++i)
683  copy.add_vertex(Mesh::Point());
684 
685  // unnamed to unnamed
686  {
687  auto prop1 = OpenMesh::VProp<int>(mesh_);
688  auto prop2 = OpenMesh::VProp<int>(copy);
689  prop2.set_range(mesh_.vertices(), 0);
690 
691  for (auto vh : mesh_.vertices())
692  prop1[vh] = vh.idx()*2-13;
693 
694  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
695 
696  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
697  prop2 = std::move(prop1); // this should be cheap
698  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
699  TIMING_OUTPUT(std::cout << "moving property temporary to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
700 
701  EXPECT_FALSE(prop1.isValid()) << "prop1 not invalidated after moving";
702 
703  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
704  EXPECT_NO_FATAL_FAILURE(prop2[OpenMesh::VertexHandle(static_cast<int>(copy.n_vertices())-1)]) << "Property not correctly resized";
705  }
706 
707  // unnamed to named
708  {
709  auto prop1 = OpenMesh::VProp<int>(mesh_);
710  auto prop2 = OpenMesh::VProp<int>(copy, "ids");
711  prop2.set_range(mesh_.vertices(), 0);
712  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
713 
714  for (auto vh : mesh_.vertices())
715  prop1[vh] = vh.idx()*2-13;
716 
717  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
718 
719  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
720  prop2 = std::move(prop1);
721  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
722  TIMING_OUTPUT(std::cout << "moving property temporary to named took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
723 
724  EXPECT_FALSE(prop1.isValid()) << "prop1 not invalidated after moving";
725 
726  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
727 
728  auto prop3 = OpenMesh::VProp<int>(copy, "ids");
729  EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], -13) << "property with name 'ids' was not correctly changed";
730  }
731 
732  // named to unnamed
733  {
734  auto prop1 = OpenMesh::VProp<int>(mesh_, "ids2");
735  auto prop2 = OpenMesh::VProp<int>(copy);
736  prop2.set_range(mesh_.vertices(), 0);
737  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
738 
739  for (auto vh : mesh_.vertices())
740  prop1[vh] = vh.idx()*2-13;
741 
742  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
743  prop2 = std::move(prop1); // moving named properties will not invalidate the property and will copy the data
744  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
745  TIMING_OUTPUT(std::cout << "moving property named to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
746 
747  EXPECT_TRUE(prop1.isValid()) << "named prop1 should not be invalidated by moving";
748 
749  prop1.set_range(mesh_.vertices(), 0);
750 
751  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
752 
753  }
754 
755  // named to named (different names)
756  {
757  auto prop1 = OpenMesh::VProp<int>(mesh_, "ids3");
758  auto prop2 = OpenMesh::VProp<int>(copy, "ids4");
759  prop2.set_range(mesh_.vertices(), 0);
760  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
761 
762  for (auto vh : mesh_.vertices())
763  prop1[vh] = vh.idx()*2-13;
764 
765  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
766 
767  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
768  prop2 = std::move(prop1); // moving named properties will not invalidate the property and will copy the data
769  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
770  TIMING_OUTPUT(std::cout << "moving property named to named with different name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
771 
772  EXPECT_TRUE(prop1.isValid()) << "named prop1 should not be invalidated by moving";
773 
774  prop1.set_range(mesh_.vertices(), 0);
775 
776  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
777  }
778 
779  // named to named (same names)
780  {
781  auto prop1 = OpenMesh::VProp<int>(mesh_, "ids5");
782  auto prop2 = OpenMesh::VProp<int>(copy, "ids5");
783 
785  prop6 = prop1;
786 
787  for (auto vh : mesh_.vertices())
788  prop1[vh] = vh.idx()*2-13;
789 
790  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
791 
792  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
793  prop2 = std::move(prop1); // should copy
794  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
795  TIMING_OUTPUT(std::cout << "moving property named to named with same name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
796 
797  EXPECT_TRUE(prop1.isValid()) << "named prop1 should not be invalidated by moving";
798 
799  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
800  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
801 
802  prop1.set_range(mesh_.vertices(), 42);
803 
804  EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
805  EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
806 
807  auto prop3 = OpenMesh::VProp<int>(mesh_, "ids5");
808  EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
809  auto prop4 = OpenMesh::VProp<int>(copy, "ids5");
810  EXPECT_EQ(prop4[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
811  }
812 }
813 
814 
815 TEST_F(OpenMeshPropertyManager, temporary_property_on_const_mesh) {
816 
817  const auto& const_ref = mesh_;
818 
819  auto cog = OpenMesh::FProp<Mesh::Point>(const_ref);
820  auto points = OpenMesh::getPointsProperty(const_ref);
821 
822  for (auto fh : const_ref.faces())
823  cog(fh) = fh.vertices().avg(points);
824 
825  auto cog_copy = cog;
826 
827  for (auto fh : const_ref.faces())
828  {
829  EXPECT_NE(&cog(fh), &cog_copy(fh)) << "Both properties point to the same memory";
830  EXPECT_EQ(cog(fh), cog_copy(fh)) << "Property not copied correctly";
831  }
832 
833  auto description = OpenMesh::MProp<std::string>(const_ref);
834  description() = "Cool Const Mesh";
835 
836  std::cout << description(OpenMesh::MeshHandle(33)) << std::endl;
837 
838 }
839 
840 
841 OpenMesh::VProp<int> get_id_prop(const OpenMesh::PolyConnectivity& mesh)
842 {
843  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
844 
845  auto id_prop = OpenMesh::VProp<int>(mesh);
846  for (auto vh : mesh.vertices())
847  id_prop(vh) = vh.idx();
848 
849  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
850  TIMING_OUTPUT(std::cout << "Time spend in function: " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
851 
852  return id_prop;
853 }
854 
855 TEST_F(OpenMeshPropertyManager, return_property_from_function) {
856 
857  for (int i = 0; i < N_VERTICES_TIMING; ++i)
858  mesh_.add_vertex(Mesh::Point());
859 
860  TIMING_OUTPUT(auto t_start = std::chrono::high_resolution_clock::now();)
861  auto id_p = get_id_prop(mesh_);
862  TIMING_OUTPUT(auto t_end = std::chrono::high_resolution_clock::now();)
863  TIMING_OUTPUT(std::cout << "Time spend around function " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;)
864 
865  for (auto vh : mesh_.vertices())
866  {
867  EXPECT_EQ(id_p(vh), vh.idx()) << "Property not returned correctly" << std::endl;
868  }
869 
870 }
871 
872 
873 
874 }
Handle type for meshes to simplify some template programming.
Definition: Handles.hh:148
void set_range(HandleTypeIterator begin, HandleTypeIterator end, const PROP_VALUE &value)
Kernel::Point Point
Coordinate type.
Definition: PolyMeshT.hh:112
ConstVertexRangeSkipping vertices() const
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
Connectivity Class for polygonal meshes.
Kernel::VertexHandle VertexHandle
Handle for referencing the corresponding item.
Definition: PolyMeshT.hh:136