MOAB: Mesh Oriented datABase  (version 5.5.0)
mbfacet_test.cpp
Go to the documentation of this file.
1 /**
2  * This library is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU Lesser General Public
4  * License as published by the Free Software Foundation; either
5  * version 2.1 of the License, or (at your option) any later version.
6  *
7  */
8 /**
9  * \file mbfacet_test.cpp
10  *
11  * \brief test mbfacet, a unit test for the FBEngine class, which is providing iGeom like methods to
12  * a MOAB db
13  *
14  */
15 #include "moab/Core.hpp"
16 #include <iostream>
17 #include <fstream>
18 #include <set>
19 #include <algorithm>
20 #include <vector>
21 #include <iterator>
22 #include <algorithm>
23 #include <iomanip>
24 #include <cassert>
25 #include <cstring>
26 #include <cmath>
27 #include "moab/FBEngine.hpp"
28 #include "moab/GeomTopoTool.hpp"
29 #include "TestUtil.hpp"
30 
31 std::string filename;
32 std::string filename_out;
33 std::string polygon_file_name;
34 
35 std::string quads_file;
36 
37 double min_dot = 0.8; // default
38 
42 
43 using namespace moab;
44 
49 ErrorCode ray_test( FBEngine* pFacet );
52 
54 
55 void handle_error_code( ErrorCode rv, int& number_failed, int& number_successful )
56 {
57  if( rv == MB_SUCCESS )
58  {
59  std::cout << "Success";
60  number_successful++;
61  }
62  else
63  {
64  std::cout << "Failure";
65  number_failed++;
66  }
67 }
68 
69 int main( int argc, char* argv[] )
70 {
71  filename = TestDir + "unittest/PB.h5m";
72  polygon_file_name = TestDir + "unittest/polyPB.txt";
73  filename_out = "PB_facet.h5m";
74  quads_file = TestDir + "unittest/quads.h5m";
75 
76  min_dot = 0.8;
77 
78  keep_output = false;
79  bool only_cropping = false;
80  if( argc == 1 )
81  {
82  std::cout << "Using default input files: " << filename << " " << polygon_file_name << " " << filename_out << " "
83  << min_dot << " " << quads_file << std::endl;
84  std::cout << " default output file: " << filename_out << " will be deleted \n";
85  }
86  else if( argc == 5 )
87  {
88  only_cropping = true;
89  filename = argv[1];
90  polygon_file_name = argv[2];
91  filename_out = argv[3];
92  min_dot = atof( argv[4] );
93  }
94  else if( argc >= 6 )
95  {
96  filename = argv[1];
97  polygon_file_name = argv[2];
98  filename_out = argv[3];
99  min_dot = atof( argv[4] );
100  quads_file = argv[5];
101  keep_output = true;
102  std::cout << "Using input files: " << filename << " " << polygon_file_name << " " << std::endl;
103  std::cout << " default output file: " << filename_out << " will be saved \n";
104  std::cout << " min_dot: " << min_dot << " quads file:" << quads_file << "\n";
105  }
106  else
107  {
108  std::cerr << "Usage: " << argv[0] << " [geom_filename] [polygon_file] [output_file] [min_dot] [quads_file]"
109  << std::endl;
110  return 1;
111  }
112 
113  Core mbcore;
114  Interface* mb = &mbcore;
115 
116  ErrorCode rval = mb->load_file( filename.c_str() );MB_CHK_SET_ERR( rval, "failed to load input file" );
117 
118  FBEngine* pFacet = new FBEngine( mb, NULL, true ); // smooth facetting, no OBB tree passed
119 
120  if( !pFacet ) return 1; // error
121 
122  // should the init be part of constructor or not?
123  // this is where the obb tree is constructed, and smooth faceting initialized, too.
124  rval = pFacet->Init();MB_CHK_SET_ERR( rval, "failed to initialize smoothing" );
125 
126  std::cout << "root set test: ";
127  rval = root_set_test( pFacet );
129  std::cout << "\n";
130 
131  std::cout << "gentity set test: ";
132  rval = gentityset_test( pFacet );
134  std::cout << "\n";
135 
136  std::cout << "geometry evals test: ";
137  rval = geometry_evaluation_test( pFacet );
139  std::cout << "\n";
140 
141  std::cout << "normal evals test: ";
142  rval = normals_test( pFacet );
144  std::cout << "\n";
145 
146  std::cout << "ray test: ";
147  rval = ray_test( pFacet );
149  std::cout << "\n";
150 
151  std::cout << "split with loop test ";
152  rval = split_test( mb, pFacet );
154  std::cout << "\n";
155 
156  // pFacet has been deleted in split_test(), so we should mark it as NULL
157  // Setting pFacet (value parameter) to NULL in split_test() does not work
158  pFacet = NULL;
159 
160  std::cout << " check split: ";
161  rval = check_split( mb );
163  std::cout << "\n";
164 
165  if( only_cropping ) return number_tests_failed; // we do not remove the output file, either
166 
167  std::cout << " split quads test: ";
168  rval = split_quads_test();
170  std::cout << "\n";
171 
172  // when we are done, remove modified file if we want to
173  if( !keep_output )
174  {
175  remove( filename_out.c_str() );
176  }
177  return number_tests_failed;
178 }
179 
181 {
183  ErrorCode rval = pFacet->getRootSet( &root_set );MB_CHK_SET_ERR( rval, "ERROR : getRootSet failed!" );
184 
185  return MB_SUCCESS;
186 }
187 
189 {
190  int num_type = 4;
191  EntityHandle ges_array[4];
192  int number_array[4];
193  // int num_all_gentities_super = 0;
194  int ent_type = 0; // iBase_VERTEX;
195 
197  ErrorCode rval = pFacet->getRootSet( &root_set );MB_CHK_SET_ERR( rval, "ERROR : getRootSet failed!" );
198 
199  // get the number of sets in the whole model
200  int all_sets = 0;
201  rval = pFacet->getNumEntSets( root_set, 0, &all_sets );MB_CHK_SET_ERR( rval, "Problem getting the number of all gentity sets in whole model." ); // why do we
202  // count all sets
203 
204  // add gentities to entitysets by type
205  for( ; ent_type < num_type; ent_type++ )
206  {
207  // initialize the entityset
208  rval = pFacet->createEntSet( 1, &ges_array[ent_type] ); // 1 means isList, ordered
209  MB_CHK_SET_ERR( rval, "Problem creating entityset." );
210 
211  // get entities by type in total "mesh"
212  Range gentities;
213  rval = pFacet->getEntities( root_set, ent_type, gentities );MB_CHK_SET_ERR( rval, "Failed to get gentities by type in gentityset_test." );
214 
215  // add gentities into gentity set
216  rval = pFacet->addEntArrToSet( gentities, ges_array[ent_type] );MB_CHK_SET_ERR( rval, "Failed to add gentities in entityset_test." );
217 
218  // Check to make sure entity set really has correct number of entities in it
219  rval = pFacet->getNumOfType( ges_array[ent_type], ent_type, &number_array[ent_type] );MB_CHK_SET_ERR( rval, "Failed to get number of gentities by type in entityset_test." );
220 
221  // compare the number of entities by type
222  int num_type_gentity = gentities.size();
223 
224  if( number_array[ent_type] != num_type_gentity )
225  {
226  std::cerr << "Number of gentities by type is not correct" << std::endl;
227  return MB_FAILURE;
228  }
229 
230  // add to number of all entities in super set
231  // num_all_gentities_super += num_type_gentity;
232  }
233 
234  // make a super set having all entitysets
235  EntityHandle super_set;
236  rval = pFacet->createEntSet( 1, &super_set ); // 1 is list
237  MB_CHK_SET_ERR( rval, "Failed to create a super set in gentityset_test." );
238 
239  for( int i = 0; i < num_type; i++ )
240  {
241  rval = pFacet->addEntSet( ges_array[i], super_set );MB_CHK_SET_ERR( rval, "Failed to create a super set in gentityset_test." );
242  }
243 
244  //----------TEST BOOLEAN OPERATIONS----------------//
245 
246  EntityHandle temp_ges1;
247  rval = pFacet->createEntSet( 1, &temp_ges1 );MB_CHK_SET_ERR( rval, "Failed to create a super set in gentityset_test." );
248 
249  // Subtract
250  // add all EDGEs and FACEs to temp_es1
251  // get all EDGE entities
252  Range gedges, gfaces, temp_gentities1;
253  rval = pFacet->getEntities( ges_array[1], /* iBase_EDGE*/ 1, gedges );MB_CHK_SET_ERR( rval, "Failed to get gedge gentities in gentityset_test." );
254 
255  // add EDGEs to ges1
256  rval = pFacet->addEntArrToSet( gedges, temp_ges1 );MB_CHK_SET_ERR( rval, "Failed to add gedge gentities in gentityset_test." );
257 
258  // get all FACE gentities
259  rval = pFacet->getEntities( ges_array[2], /*iBase_FACE*/ 2, gfaces );MB_CHK_SET_ERR( rval, "Failed to get gface gentities in gentityset_test." );
260 
261  // add FACEs to es1
262  rval = pFacet->addEntArrToSet( gfaces, temp_ges1 );MB_CHK_SET_ERR( rval, "Failed to add gface gentities in gentityset_test." );
263 
264  // subtract EDGEs
265  rval = pFacet->gsubtract( temp_ges1, ges_array[1], temp_ges1 );MB_CHK_SET_ERR( rval, "Failed to subtract gentitysets in gentityset_test." );
266 
267  rval = pFacet->getEntities( temp_ges1, 2, temp_gentities1 );MB_CHK_SET_ERR( rval, "Failed to get gface gentities in gentityset_test." );
268 
269  if( gfaces.size() != temp_gentities1.size() )
270  {
271  std::cerr << "Number of entitysets after subtraction not correct \
272  in gentityset_test."
273  << std::endl;
274  return MB_FAILURE;
275  }
276 
277  // check there's nothing but gfaces in temp_ges1
278  int num_gents;
279  rval = pFacet->getNumOfType( temp_ges1, 1, &num_gents );MB_CHK_SET_ERR( rval, "Failed to get dimensions of gentities in gentityset_test." );
280  if( 0 != num_gents )
281  {
282  std::cerr << "Subtraction failed to remove all edges" << std::endl;
283  return MB_FAILURE;
284  }
285 
286  return MB_SUCCESS;
287 }
288 
290 {
291  int i;
293  ErrorCode rval = pFacet->getRootSet( &root_set );MB_CHK_SET_ERR( rval, "ERROR : getRootSet failed!" );
294 
295  int top = 0; // iBase_VERTEX;
296  int num_test_top = 4; // iBase_ALL_TYPES;
297  std::vector< std::vector< EntityHandle > > gentity_vectors( num_test_top );
298 
299  // fill the vectors of each topology entities
300  //
301  for( i = top; i < num_test_top; i++ )
302  {
303  Range gentities;
304  rval = pFacet->getEntities( root_set, i, gentities );MB_CHK_SET_ERR( rval, "Failed to get gentities in eval tests." );
305 
306  gentity_vectors[i].resize( gentities.size() );
307  std::copy( gentities.begin(), gentities.end(), gentity_vectors[i].begin() );
308  }
309 
310  // check geometries in both directions
311  double min[3], max[3], on[3];
312  double near[3] = { .0, .0, .0 };
313  std::vector< EntityHandle >::iterator vit;
314  /*for (i = iBase_REGION; i >= iBase_VERTEX; i--) {
315  if (i != iBase_EDGE) {*/
316  for( i = 3; i >= 0; i-- )
317  {
318  if( i != 1 )
319  {
320  for( vit = gentity_vectors[i].begin(); vit != gentity_vectors[i].end(); ++vit )
321  {
322  EntityHandle this_gent = *vit;
323 
324  rval = pFacet->getEntBoundBox( this_gent, &min[0], &min[1], &min[2], &max[0], &max[1], &max[2] );MB_CHK_SET_ERR( rval, "Failed to get bounding box of entity." );
325 
326  for( int j = 0; j < 3; j++ )
327  near[j] = ( min[j] + max[j] ) / 2.;
328  rval = pFacet->getEntClosestPt( this_gent, near[0], near[1], near[2], &on[0], &on[1], &on[2] );MB_CHK_SET_ERR( rval, "Failed to get closest point on entity." );
329  }
330  }
331  else
332  {
333  // for edges, provide a little better help
334  for( vit = gentity_vectors[i].begin(); vit != gentity_vectors[i].end(); ++vit )
335  {
336  EntityHandle this_gent = *vit;
337 
338  // we know that the edge is parametric, with par between 0 and 1
339  // some random parameter
340  rval = pFacet->getEntUtoXYZ( this_gent, 0.33, near[0], near[1], near[2] );MB_CHK_SET_ERR( rval, "Failed to get a new point" );
341 
342  std::cout << " entity of type " << i << " position:\n " << near[0] << " " << near[1] << " " << near[2]
343  << "\n";
344  rval = pFacet->getEntClosestPt( this_gent, near[0], near[1], near[2], &on[0], &on[1], &on[2] );MB_CHK_SET_ERR( rval, "Failed to get closest point on entity." );
345 
346  std::cout << " close by: " << on[0] << " " << on[1] << " " << on[2] << "\n";
347  }
348  }
349  }
350 
351  return MB_SUCCESS;
352 }
353 
354 // test normals evaluations on the surface only
356 {
357  int i;
359  ErrorCode rval = pFacet->getRootSet( &root_set );MB_CHK_SET_ERR( rval, "ERROR : getRootSet failed!" );
360 
361  int top = 0; // iBase_VERTEX;
362  int num_test_top = 4; // iBase_ALL_TYPES;
363  std::vector< std::vector< EntityHandle > > gentity_vectors( num_test_top );
364 
365  // fill the vectors of each topology entities
366  //
367  for( i = top; i < num_test_top; i++ )
368  {
369  Range gentities;
370  rval = pFacet->getEntities( root_set, i, gentities );MB_CHK_SET_ERR( rval, "Failed to get gentities in eval tests." );
371 
372  gentity_vectors[i].resize( gentities.size() );
373  std::copy( gentities.begin(), gentities.end(), gentity_vectors[i].begin() );
374  }
375 
376  // check adjacencies in both directions
377  double min[3], max[3];
378  double normal[3] = { .0, .0, .0 };
379  std::vector< EntityHandle >::iterator vit;
380  for( i = 3 /*iBase_REGION*/; i > 1 /*iBase_EDGE*/; i-- )
381  {
382  for( vit = gentity_vectors[i].begin(); vit != gentity_vectors[i].end(); ++vit )
383  {
384  EntityHandle this_gent = *vit;
385  rval = pFacet->getEntBoundBox( this_gent, &min[0], &min[1], &min[2], &max[0], &max[1], &max[2] );MB_CHK_SET_ERR( rval, "Failed to get bounding box of entity." );
386 
387  rval = pFacet->getEntNrmlXYZ( this_gent, ( max[0] + min[0] ) / 2, ( max[1] + min[1] ) / 2,
388  ( max[2] + min[2] ) / 2, &normal[0], &normal[1], &normal[2] );MB_CHK_SET_ERR( rval, "Failed to get normal to the closest point." );
389 
390  std::cout << " entity of type " << i << " closest normal to center:\n " << normal[0] << " " << normal[1]
391  << " " << normal[2] << "\n";
392  }
393  }
394 
395  return MB_SUCCESS;
396 }
397 
398 // ray test
400 {
401 
403  ErrorCode rval = pFacet->getRootSet( &root_set );MB_CHK_SET_ERR( rval, "ERROR : getRootSet failed!" );
404 
405  int top = 2; // iBase_FACE;
406 
407  Range faces;
408  rval = pFacet->getEntities( root_set, top, faces );MB_CHK_SET_ERR( rval, "Failed to get faces in ray_test." );
409 
410  // check only the first face
411 
412  // check adjacencies in both directions
413  double min[3], max[3];
414 
415  EntityHandle first_face = faces[0];
416 
417  rval = pFacet->getEntBoundBox( first_face, &min[0], &min[1], &min[2], &max[0], &max[1], &max[2] );MB_CHK_SET_ERR( rval, "Failed to get bounding box of entity." );
418 
419  // assume that the ray shot from the bottom of the box (middle) is a pretty good candidate
420  // in z direction
421  double x = ( min[0] + max[0] ) / 2, y = ( min[1] + max[1] ) / 2, z = min[2];
422  std::vector< EntityHandle > intersect_entity_handles;
423  std::vector< double > intersect_coords;
424  std::vector< double > param_coords;
425 
426  rval = pFacet->getPntRayIntsct( x, y, z, // shot from
427  0., 0., 1., // direction
428  intersect_entity_handles,
429  /*iBase_INTERLEAVED,*/
430  intersect_coords, param_coords );MB_CHK_SET_ERR( rval, "Failed to find ray intersections points " );
431 
432  for( unsigned int i = 0; i < intersect_entity_handles.size(); i++ )
433  {
434 
435  std::cout << " entity of type " << pFacet->moab_instance()->type_from_handle( intersect_entity_handles[i] )
436  << " id from handle: " << pFacet->moab_instance()->id_from_handle( intersect_entity_handles[i] )
437  << "\n"
438  << intersect_coords[3 * i] << " " << intersect_coords[3 * i + 1] << " " << intersect_coords[3 * i + 2]
439  << "\n"
440  << " distance: " << param_coords[i] << "\n";
441  }
442 
443  return MB_SUCCESS;
444 }
445 
446 // this test is for creating 2 surfaces given a polyline and a direction for piercing.
447 // this test is for cropping a surface with a closed loop, no intersection
448 // with initial boundary
450 {
451 
453  ErrorCode rval = pFacet->getRootSet( &root_set );MB_CHK_SET_ERR( rval, "ERROR : getRootSet failed!" );
454 
455  int top = 2; // iBase_FACE;
456 
457  Range faces;
458  rval = pFacet->getEntities( root_set, top, faces );MB_CHK_SET_ERR( rval, "Failed to get faces in split_test." );
459 
460  // check only the first face
461 
462  EntityHandle first_face = faces[0];
463  // use the polyPB.txt file to get the trimming polygon
464  ;
465  // read the file with the polygon user data
466 
467  std::ifstream datafile( polygon_file_name.c_str(), std::ifstream::in );
468  if( !datafile )
469  {
470  std::cout << "can't read file\n";
471  return MB_FAILURE;
472  }
473  //
474  char temp[100];
475  double direction[3]; // normalized
476  double gridSize;
477  datafile.getline( temp, 100 ); // first line
478 
479  // get direction and mesh size along polygon segments, from file
480  sscanf( temp, " %lf %lf %lf %lf ", direction, direction + 1, direction + 2, &gridSize );
481  // NORMALIZE(direction);// just to be sure
482 
483  std::vector< double > xyz;
484  while( !datafile.eof() )
485  {
486  datafile.getline( temp, 100 );
487  // int id = 0;
488  double x, y, z;
489  int nr = sscanf( temp, "%lf %lf %lf", &x, &y, &z );
490  if( nr == 3 )
491  {
492  xyz.push_back( x );
493  xyz.push_back( y );
494  xyz.push_back( z );
495  }
496  }
497  int sizePolygon = (int)xyz.size() / 3;
498  if( sizePolygon < 3 )
499  {
500  std::cerr << " Not enough points in the polygon" << std::endl;
501  return MB_FAILURE;
502  }
503 
504  EntityHandle newFace; // first test is with closed surface
505  rval = pFacet->split_surface_with_direction( first_face, xyz, direction, /*closed*/ 1, min_dot, newFace );MB_CHK_SET_ERR( rval, "ERROR : splitting surface failed!" );
506 
507  // save a new database
508  pFacet->delete_smooth_tags();
509  // get a new gtt tool
510 
511  // duplicate -- extract a new geom topo tool
512  GeomTopoTool* gtt = pFacet->get_gtt();
513  GeomTopoTool* duplicate = NULL;
514  std::vector< EntityHandle > gents;
515  gents.push_back( newFace );
516  rval = gtt->duplicate_model( duplicate, &gents );MB_CHK_SET_ERR( rval, "Failed to extract surface." );
517 
518  EntityHandle newRootSet = duplicate->get_root_model_set();
519  delete pFacet;
520  pFacet = NULL; // try not to write the obb tree
521  rval = mb->write_file( filename_out.c_str(), NULL, NULL, &newRootSet, 1 );MB_CHK_SET_ERR( rval, "ERROR : writing mesh failed!" );
522 
523  delete duplicate;
524  return rval;
525 }
526 
528 {
529  // check loading the file in an empty db
530  // delete pFacet;// should clean up the FBEngine
531  ErrorCode rval = mb->delete_mesh();MB_CHK_SET_ERR( rval, "ERROR : delete mesh failed!" );
532 
533  rval = mb->load_file( filename_out.c_str() );MB_CHK_SET_ERR( rval, "ERROR : can't load modified file!" );
534 
535  FBEngine* pFacet = new FBEngine( mb, NULL, true ); // smooth facetting, no OBB tree passed
536 
537  // repeat tests on modified file
538 
539  // should the init be part of constructor or not?
540  // this is where the obb tree is constructed, and smooth faceting initialized, too.
541  rval = pFacet->Init();MB_CHK_SET_ERR( rval, "failed to initialize smoothing" );
542 
543  std::cout << "root set test: ";
544  rval = root_set_test( pFacet );
546  std::cout << "\n";
547 
548  std::cout << "gentity set test: ";
549  rval = gentityset_test( pFacet );
551  std::cout << "\n";
552 
553  std::cout << "geometry evals test: ";
554  rval = geometry_evaluation_test( pFacet );
556  std::cout << "\n";
557 
558  std::cout << "normal evals test: ";
559  rval = normals_test( pFacet );
561  std::cout << "\n";
562 
563  std::cout << "ray test: ";
564  rval = ray_test( pFacet );
566  std::cout << "\n";
567 
568  delete pFacet;
569  pFacet = NULL;
570  if( number_tests_failed > 0 ) return MB_FAILURE;
571 
572  return MB_SUCCESS;
573 }
574 
576 {
577  Core mbcore;
578  Interface* mb = &mbcore;
579 
580  ErrorCode rval = mb->load_file( quads_file.c_str() );MB_CHK_SET_ERR( rval, "failed to load quads file" );
581 
582  FBEngine* pFacet = new FBEngine( mb, NULL, true );
583 
584  rval = pFacet->Init();MB_CHK_SET_ERR( rval, "failed to initialize smoothing" );
585 
587  rval = pFacet->getRootSet( &root_set );MB_CHK_SET_ERR( rval, "ERROR : getRootSet failed!" );
588  int top = 2; // iBase_FACE;
589 
590  Range faces;
591  rval = pFacet->getEntities( root_set, top, faces );MB_CHK_SET_ERR( rval, "Failed to get faces in split_test." );
592 
593  if( faces.size() != 2 )
594  {
595  std::cout << "num faces after loading quads model:" << faces.size() << "\n";
596  return MB_FAILURE; //
597  }
598 
599  // multiple tests
600  std::cout << "gentity set test: ";
601  rval = gentityset_test( pFacet );
603  std::cout << "\n";
604 
605  std::cout << "geometry evals test: ";
606  rval = geometry_evaluation_test( pFacet );
608  std::cout << "\n";
609 
610  std::cout << "normal evals test: ";
611  rval = normals_test( pFacet );
613  std::cout << "\n";
614 
615  std::cout << "ray test: ";
616  rval = ray_test( pFacet );
618  std::cout << "\n";
619 
620  // export split triangles model, after deleting the smooth tags
621  pFacet->delete_smooth_tags();
622 
623  delete pFacet;
624  pFacet = NULL;
625  std::string spl_file = "q.split.h5m";
626  rval = mb->write_file( spl_file.c_str(), 0, 0, &root_set, 1 );MB_CHK_SET_ERR( rval, "can't write result file" );
627 
628  if( !keep_output )
629  {
630  remove( spl_file.c_str() );
631  }
632 
633  if( number_tests_failed > 0 ) return MB_FAILURE;
634 
635  return MB_SUCCESS;
636 }