Mesh Oriented datABase  (version 5.6.0)
An array-based unstructured mesh library
Skinner.cpp
Go to the documentation of this file.
1 /**
2  * MOAB, a Mesh-Oriented datABase, is a software component for creating,
3  * storing and accessing finite element mesh data.
4  *
5  * Copyright 2004 Sandia Corporation. Under the terms of Contract
6  * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
7  * retains certain rights in this software.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  */
15 
16 #ifdef __MFC_VER
17 #pragma warning( disable : 4786 )
18 #endif
19 
20 #ifdef WIN32 /* windows */
21 #define _USE_MATH_DEFINES // For M_PI
22 #endif
23 #include "moab/Skinner.hpp"
24 #include "moab/Range.hpp"
25 #include "moab/CN.hpp"
26 #include <vector>
27 #include <set>
28 #include <algorithm>
29 #include <cmath>
30 #include <cassert>
31 #include <iostream>
32 #include "moab/Util.hpp"
33 #include "Internals.hpp"
34 #include "MBTagConventions.hpp"
35 #include "moab/Core.hpp"
36 #include "AEntityFactory.hpp"
37 #include "moab/ScdInterface.hpp"
38 
39 #ifdef M_PI
40 #define SKINNER_PI M_PI
41 #else
42 #define SKINNER_PI 3.1415926535897932384626
43 #endif
44 
45 namespace moab
46 {
47 
49 {
50  // delete the adjacency tag
51 }
52 
54 {
55  // go through and mark all the target dimension entities
56  // that already exist as not deleteable
57  // also get the connectivity tags for each type
58  // also populate adjacency information
59  EntityType type;
60  DimensionPair target_ent_types = CN::TypeDimensionMap[mTargetDim];
61 
62  void* null_ptr = NULL;
63 
64  ErrorCode result = thisMB->tag_get_handle( "skinner adj", sizeof( void* ), MB_TYPE_OPAQUE, mAdjTag,
65  MB_TAG_DENSE | MB_TAG_CREAT, &null_ptr );
66  MB_CHK_ERR( result );
67 
68  if( mDeletableMBTag == 0 )
69  {
70  result =
72  MB_CHK_ERR( result );
73  }
74 
75  Range entities;
76 
77  // go through each type at this dimension
78  for( type = target_ent_types.first; type <= target_ent_types.second; ++type )
79  {
80  // get the entities of this type in the MB
81  thisMB->get_entities_by_type( 0, type, entities );
82 
83  // go through each entity of this type in the MB
84  // and set its deletable tag to NO
85  Range::iterator iter, end_iter;
86  end_iter = entities.end();
87  for( iter = entities.begin(); iter != end_iter; ++iter )
88  {
89  unsigned char bit = 0x1;
90  result = thisMB->tag_set_data( mDeletableMBTag, &( *iter ), 1, &bit );
91  assert( MB_SUCCESS == result );
92  // add adjacency information too
93  if( TYPE_FROM_HANDLE( *iter ) != MBVERTEX ) add_adjacency( *iter );
94  }
95  }
96 
97  return MB_SUCCESS;
98 }
99 
101 {
102  ErrorCode result;
103  if( 0 != mDeletableMBTag )
104  {
105  result = thisMB->tag_delete( mDeletableMBTag );
106  mDeletableMBTag = 0;
107  MB_CHK_ERR( result );
108  }
109 
110  // remove the adjacency tag
111  std::vector< std::vector< EntityHandle >* > adj_arr;
112  std::vector< std::vector< EntityHandle >* >::iterator i;
113  if( 0 != mAdjTag )
114  {
115  for( EntityType t = MBVERTEX; t != MBMAXTYPE; ++t )
116  {
117  Range entities;
118  result = thisMB->get_entities_by_type_and_tag( 0, t, &mAdjTag, 0, 1, entities );
119  MB_CHK_ERR( result );
120  adj_arr.resize( entities.size() );
121  result = thisMB->tag_get_data( mAdjTag, entities, &adj_arr[0] );
122  MB_CHK_ERR( result );
123  for( i = adj_arr.begin(); i != adj_arr.end(); ++i )
124  delete *i;
125  }
126 
127  result = thisMB->tag_delete( mAdjTag );
128  mAdjTag = 0;
129  MB_CHK_ERR( result );
130  }
131 
132  return MB_SUCCESS;
133 }
134 
136 {
137  std::vector< EntityHandle >* adj = NULL;
138  const EntityHandle* nodes;
139  int num_nodes;
140  ErrorCode result = thisMB->get_connectivity( entity, nodes, num_nodes, true );
141  MB_CHK_ERR( result );
142  const EntityHandle* iter = std::min_element( nodes, nodes + num_nodes );
143 
144  if( iter == nodes + num_nodes ) return MB_SUCCESS;
145 
146  // add this entity to the node
147  if( thisMB->tag_get_data( mAdjTag, iter, 1, &adj ) == MB_SUCCESS && adj != NULL )
148  {
149  adj->push_back( entity );
150  }
151  // create a new vector and add it
152  else
153  {
154  adj = new std::vector< EntityHandle >;
155  adj->push_back( entity );
156  result = thisMB->tag_set_data( mAdjTag, iter, 1, &adj );
157  MB_CHK_ERR( result );
158  }
159 
160  return MB_SUCCESS;
161 }
162 
163 void Skinner::add_adjacency( EntityHandle entity, const EntityHandle* nodes, const int num_nodes )
164 {
165  std::vector< EntityHandle >* adj = NULL;
166  const EntityHandle* iter = std::min_element( nodes, nodes + num_nodes );
167 
168  if( iter == nodes + num_nodes ) return;
169 
170  // should not be setting adjacency lists in ho-nodes
171  assert( TYPE_FROM_HANDLE( entity ) == MBPOLYGON ||
172  num_nodes == CN::VerticesPerEntity( TYPE_FROM_HANDLE( entity ) ) );
173 
174  // add this entity to the node
175  if( thisMB->tag_get_data( mAdjTag, iter, 1, &adj ) == MB_SUCCESS && adj != NULL )
176  {
177  adj->push_back( entity );
178  }
179  // create a new vector and add it
180  else
181  {
182  adj = new std::vector< EntityHandle >;
183  adj->push_back( entity );
184  thisMB->tag_set_data( mAdjTag, iter, 1, &adj );
185  }
186 }
187 
188 ErrorCode Skinner::find_geometric_skin( const EntityHandle meshset, Range& forward_target_entities )
189 {
190  // attempts to find whole model skin, using geom topo sets first then
191  // normal find_skin function
192  bool debug = true;
193 
194  // look for geom topo sets
195  Tag geom_tag;
196  ErrorCode result =
198 
199  if( MB_SUCCESS != result ) return result;
200 
201  // get face sets (dimension = 2)
202  Range face_sets;
203  int two = 2;
204  const void* two_ptr = &two;
205  result = thisMB->get_entities_by_type_and_tag( meshset, MBENTITYSET, &geom_tag, &two_ptr, 1, face_sets );
206 
207  Range::iterator it;
208  if( MB_SUCCESS != result )
209  return result;
210  else if( face_sets.empty() )
211  return MB_ENTITY_NOT_FOUND;
212 
213  // ok, we have face sets; use those to determine skin
214  Range skin_sets;
215  if( debug ) std::cout << "Found " << face_sets.size() << " face sets total..." << std::endl;
216 
217  for( it = face_sets.begin(); it != face_sets.end(); ++it )
218  {
219  int num_parents;
220  result = thisMB->num_parent_meshsets( *it, &num_parents );
221  if( MB_SUCCESS != result )
222  return result;
223  else if( num_parents == 1 )
224  skin_sets.insert( *it );
225  }
226 
227  if( debug ) std::cout << "Found " << skin_sets.size() << " 1-parent face sets..." << std::endl;
228 
229  if( skin_sets.empty() ) return MB_FAILURE;
230 
231  // ok, we have the shell; gather up the elements, putting them all in forward for now
232  for( it = skin_sets.begin(); it != skin_sets.end(); ++it )
233  {
234  result = thisMB->get_entities_by_handle( *it, forward_target_entities, true );
235  if( MB_SUCCESS != result ) return result;
236  }
237 
238  return result;
239 }
240 
242  const Range& source_entities,
243  bool get_vertices,
244  Range& output_handles,
245  Range* output_reverse_handles,
246  bool create_vert_elem_adjs,
247  bool create_skin_elements,
248  bool look_for_scd )
249 {
250  if( source_entities.empty() ) return MB_SUCCESS;
251 
252  if( look_for_scd )
253  {
254  ErrorCode rval = find_skin_scd( source_entities, get_vertices, output_handles, create_skin_elements );
255  // if it returns success, it's all scd, and we don't need to do anything more
256  if( MB_SUCCESS == rval ) return rval;
257  }
258 
259  Core* this_core = dynamic_cast< Core* >( thisMB );
260  if( this_core && create_vert_elem_adjs && !this_core->a_entity_factory()->vert_elem_adjacencies() )
262 
263  return find_skin_vertices( meshset, source_entities, get_vertices ? &output_handles : 0,
264  get_vertices ? 0 : &output_handles, output_reverse_handles, create_skin_elements );
265 }
266 
267 ErrorCode Skinner::find_skin_scd( const Range& source_entities,
268  bool get_vertices,
269  Range& output_handles,
270  bool create_skin_elements )
271 {
272  // get the scd interface and check if it's been initialized
273  ScdInterface* scdi = NULL;
274  ErrorCode rval = thisMB->query_interface( scdi );
275  if( !scdi ) return MB_FAILURE;
276 
277  // ok, there's scd mesh; see if the entities passed in are all in a scd box
278  // a box needs to be wholly included in entities for this to work
279  std::vector< ScdBox* > boxes, myboxes;
280  Range myrange;
281  rval = scdi->find_boxes( boxes );
282  if( MB_SUCCESS != rval ) return rval;
283  for( std::vector< ScdBox* >::iterator bit = boxes.begin(); bit != boxes.end(); ++bit )
284  {
285  Range belems( ( *bit )->start_element(), ( *bit )->start_element() + ( *bit )->num_elements() - 1 );
286  if( source_entities.contains( belems ) )
287  {
288  myboxes.push_back( *bit );
289  myrange.merge( belems );
290  }
291  }
292  if( myboxes.empty() || myrange.size() != source_entities.size() ) return MB_FAILURE;
293 
294  // ok, we're all structured; get the skin for each box
295  for( std::vector< ScdBox* >::iterator bit = boxes.begin(); bit != boxes.end(); ++bit )
296  {
297  rval = skin_box( *bit, get_vertices, output_handles, create_skin_elements );
298  if( MB_SUCCESS != rval ) return rval;
299  }
300 
301  return MB_SUCCESS;
302 }
303 
304 ErrorCode Skinner::skin_box( ScdBox* box, bool get_vertices, Range& output_handles, bool create_skin_elements )
305 {
306  HomCoord bmin = box->box_min(), bmax = box->box_max();
307 
308  // don't support 1d boxes
309  if( bmin.j() == bmax.j() && bmin.k() == bmax.k() ) return MB_FAILURE;
310 
311  int dim = ( bmin.k() == bmax.k() ? 1 : 2 );
312 
313  ErrorCode rval;
314  EntityHandle ent;
315 
316  // i=min
317  for( int k = bmin.k(); k < bmax.k(); k++ )
318  {
319  for( int j = bmin.j(); j < bmax.j(); j++ )
320  {
321  ent = 0;
322  rval = box->get_adj_edge_or_face( dim, bmin.i(), j, k, 0, ent, create_skin_elements );
323  if( MB_SUCCESS != rval ) return rval;
324  if( ent ) output_handles.insert( ent );
325  }
326  }
327  // i=max
328  for( int k = bmin.k(); k < bmax.k(); k++ )
329  {
330  for( int j = bmin.j(); j < bmax.j(); j++ )
331  {
332  ent = 0;
333  rval = box->get_adj_edge_or_face( dim, bmax.i(), j, k, 0, ent, create_skin_elements );
334  if( MB_SUCCESS != rval ) return rval;
335  if( ent ) output_handles.insert( ent );
336  }
337  }
338  // j=min
339  for( int k = bmin.k(); k < bmax.k(); k++ )
340  {
341  for( int i = bmin.i(); i < bmax.i(); i++ )
342  {
343  ent = 0;
344  rval = box->get_adj_edge_or_face( dim, i, bmin.j(), k, 1, ent, create_skin_elements );
345  if( MB_SUCCESS != rval ) return rval;
346  if( ent ) output_handles.insert( ent );
347  }
348  }
349  // j=max
350  for( int k = bmin.k(); k < bmax.k(); k++ )
351  {
352  for( int i = bmin.i(); i < bmax.i(); i++ )
353  {
354  ent = 0;
355  rval = box->get_adj_edge_or_face( dim, i, bmax.j(), k, 1, ent, create_skin_elements );
356  if( MB_SUCCESS != rval ) return rval;
357  if( ent ) output_handles.insert( ent );
358  }
359  }
360  // k=min
361  for( int j = bmin.j(); j < bmax.j(); j++ )
362  {
363  for( int i = bmin.i(); i < bmax.i(); i++ )
364  {
365  ent = 0;
366  rval = box->get_adj_edge_or_face( dim, i, j, bmin.k(), 2, ent, create_skin_elements );
367  if( MB_SUCCESS != rval ) return rval;
368  if( ent ) output_handles.insert( ent );
369  }
370  }
371  // k=max
372  for( int j = bmin.j(); j < bmax.j(); j++ )
373  {
374  for( int i = bmin.i(); i < bmax.i(); i++ )
375  {
376  ent = 0;
377  rval = box->get_adj_edge_or_face( dim, i, j, bmax.k(), 2, ent, create_skin_elements );
378  if( MB_SUCCESS != rval ) return rval;
379  if( ent ) output_handles.insert( ent );
380  }
381  }
382 
383  if( get_vertices )
384  {
385  Range verts;
386  rval = thisMB->get_adjacencies( output_handles, 0, true, verts, Interface::UNION );
387  if( MB_SUCCESS != rval ) return rval;
388  output_handles.merge( verts );
389  }
390 
391  return MB_SUCCESS;
392 }
393 
394 ErrorCode Skinner::find_skin_noadj(const Range &source_entities,
395  Range &forward_target_entities,
396  Range &reverse_target_entities/*,
397  bool create_vert_elem_adjs*/)
398 {
399  if( source_entities.empty() ) return MB_FAILURE;
400 
401  // get our working dimensions
402  EntityType type = thisMB->type_from_handle( *( source_entities.begin() ) );
403  const int source_dim = CN::Dimension( type );
404  mTargetDim = source_dim - 1;
405 
406  // make sure we can handle the working dimensions
407  if( mTargetDim < 0 || source_dim > 3 ) return MB_FAILURE;
408 
409  initialize();
410 
411  Range::const_iterator iter, end_iter;
412  end_iter = source_entities.end();
413  const EntityHandle* conn;
414  EntityHandle match;
415 
416  direction direct;
417  ErrorCode result;
418  // assume we'll never have more than 32 vertices on a facet (checked
419  // with assert later)
420  EntityHandle sub_conn[32];
421  std::vector< EntityHandle > tmp_conn_vec;
422  int num_nodes, num_sub_nodes, num_sides;
423  int sub_indices[32]; // Also, assume that no polygon has more than 32 nodes
424  // we could increase that, but we will not display it right in visit moab h5m , anyway
425  EntityType sub_type;
426 
427  // for each source entity
428  for( iter = source_entities.begin(); iter != end_iter; ++iter )
429  {
430  // get the connectivity of this entity
431  int actual_num_nodes_polygon = 0;
432  result = thisMB->get_connectivity( *iter, conn, num_nodes, false, &tmp_conn_vec );
433  if( MB_SUCCESS != result ) return result;
434 
435  type = thisMB->type_from_handle( *iter );
436  Range::iterator seek_iter;
437 
438  // treat separately polygons (also, polyhedra will need special handling)
439  if( MBPOLYGON == type )
440  {
441  // treat padded polygons, if existing; count backwards, see how many of the last nodes
442  // are repeated assume connectivity is fine, otherwise we could be in trouble
443  actual_num_nodes_polygon = num_nodes;
444  while( actual_num_nodes_polygon >= 3 &&
445  conn[actual_num_nodes_polygon - 1] == conn[actual_num_nodes_polygon - 2] )
446  actual_num_nodes_polygon--;
447  num_sides = actual_num_nodes_polygon;
448  sub_type = MBEDGE;
449  num_sub_nodes = 2;
450  }
451  else // get connectivity of each n-1 dimension entity
452  num_sides = CN::NumSubEntities( type, mTargetDim );
453  for( int i = 0; i < num_sides; i++ )
454  {
455  if( MBPOLYGON == type )
456  {
457  sub_conn[0] = conn[i];
458  sub_conn[1] = conn[i + 1];
459  if( i + 1 == actual_num_nodes_polygon ) sub_conn[1] = conn[0];
460  }
461  else
462  {
463  CN::SubEntityNodeIndices( type, num_nodes, mTargetDim, i, sub_type, num_sub_nodes, sub_indices );
464  assert( (size_t)num_sub_nodes <= sizeof( sub_indices ) / sizeof( sub_indices[0] ) );
465  for( int j = 0; j < num_sub_nodes; j++ )
466  sub_conn[j] = conn[sub_indices[j]];
467  }
468 
469  // see if we can match this connectivity with
470  // an existing entity
471  find_match( sub_type, sub_conn, num_sub_nodes, match, direct );
472 
473  // if there is no match, create a new entity
474  if( match == 0 )
475  {
476  EntityHandle tmphndl = 0;
477  int indices[MAX_SUB_ENTITY_VERTICES];
478  EntityType new_type;
479  int num_new_nodes;
480  if( MBPOLYGON == type )
481  {
482  new_type = MBEDGE;
483  num_new_nodes = 2;
484  }
485  else
486  {
487  CN::SubEntityNodeIndices( type, num_nodes, mTargetDim, i, new_type, num_new_nodes, indices );
488  for( int j = 0; j < num_new_nodes; j++ )
489  sub_conn[j] = conn[indices[j]];
490  }
491  result = thisMB->create_element( new_type, sub_conn, num_new_nodes, tmphndl );
492  assert( MB_SUCCESS == result );
493  add_adjacency( tmphndl, sub_conn, CN::VerticesPerEntity( new_type ) );
494  forward_target_entities.insert( tmphndl );
495  }
496  // if there is a match, delete the matching entity
497  // if we can.
498  else
499  {
500  if( ( seek_iter = forward_target_entities.find( match ) ) != forward_target_entities.end() )
501  {
502  forward_target_entities.erase( seek_iter );
503  remove_adjacency( match );
504  if( /*!use_adjs &&*/ entity_deletable( match ) )
505  {
506  result = thisMB->delete_entities( &match, 1 );
507  assert( MB_SUCCESS == result );
508  }
509  }
510  else if( ( seek_iter = reverse_target_entities.find( match ) ) != reverse_target_entities.end() )
511  {
512  reverse_target_entities.erase( seek_iter );
513  remove_adjacency( match );
514  if( /*!use_adjs &&*/ entity_deletable( match ) )
515  {
516  result = thisMB->delete_entities( &match, 1 );
517  assert( MB_SUCCESS == result );
518  }
519  }
520  else
521  {
522  if( direct == FORWARD )
523  {
524  forward_target_entities.insert( match );
525  }
526  else
527  {
528  reverse_target_entities.insert( match );
529  }
530  }
531  }
532  }
533  }
534 
535  deinitialize();
536 
537  return MB_SUCCESS;
538 }
539 
540 void Skinner::find_match( EntityType type,
541  const EntityHandle* conn,
542  const int num_nodes,
543  EntityHandle& match,
544  Skinner::direction& direct )
545 {
546  match = 0;
547 
548  if( type == MBVERTEX )
549  {
550  match = *conn;
551  direct = FORWARD;
552  return;
553  }
554 
555  const EntityHandle* iter = std::min_element( conn, conn + num_nodes );
556 
557  std::vector< EntityHandle >* adj = NULL;
558 
559  ErrorCode result = thisMB->tag_get_data( mAdjTag, iter, 1, &adj );
560  if( result == MB_FAILURE || adj == NULL )
561  {
562  return;
563  }
564 
565  std::vector< EntityHandle >::iterator jter, end_jter;
566  end_jter = adj->end();
567 
568  const EntityHandle* tmp;
569  int num_verts;
570 
571  for( jter = adj->begin(); jter != end_jter; ++jter )
572  {
573  EntityType tmp_type;
574  tmp_type = thisMB->type_from_handle( *jter );
575 
576  if( type != tmp_type ) continue;
577 
578  result = thisMB->get_connectivity( *jter, tmp, num_verts, false );
579  assert( MB_SUCCESS == result && num_verts >= CN::VerticesPerEntity( type ) );
580  // FIXME: connectivity_match appears to work only for linear elements,
581  // so ignore higher-order nodes.
582  if( connectivity_match( conn, tmp, CN::VerticesPerEntity( type ), direct ) )
583  {
584  match = *jter;
585  break;
586  }
587  }
588 }
589 
591  const EntityHandle* conn2,
592  const int num_verts,
593  Skinner::direction& direct )
594 {
595  const EntityHandle* iter = std::find( conn2, conn2 + num_verts, conn1[0] );
596  if( iter == conn2 + num_verts ) return false;
597 
598  bool they_match = true;
599 
600  int i;
601  unsigned int j = iter - conn2;
602 
603  // first compare forward
604  for( i = 1; i < num_verts; ++i )
605  {
606  if( conn1[i] != conn2[( j + i ) % num_verts] )
607  {
608  they_match = false;
609  break;
610  }
611  }
612 
613  if( they_match == true )
614  {
615  // need to check for reversed edges here
616  direct = ( num_verts == 2 && j ) ? REVERSE : FORWARD;
617  return true;
618  }
619 
620  they_match = true;
621 
622  // then compare reverse
623  j += num_verts;
624  for( i = 1; i < num_verts; )
625  {
626  if( conn1[i] != conn2[( j - i ) % num_verts] )
627  {
628  they_match = false;
629  break;
630  }
631  ++i;
632  }
633  if( they_match )
634  {
635  direct = REVERSE;
636  }
637  return they_match;
638 }
639 
641 {
642  std::vector< EntityHandle > nodes, *adj = NULL;
643  ErrorCode result = thisMB->get_connectivity( &entity, 1, nodes );
644  MB_CHK_ERR( result );
645  std::vector< EntityHandle >::iterator iter = std::min_element( nodes.begin(), nodes.end() );
646 
647  if( iter == nodes.end() ) return MB_FAILURE;
648 
649  // remove this entity from the node
650  if( thisMB->tag_get_data( mAdjTag, &( *iter ), 1, &adj ) == MB_SUCCESS && adj != NULL )
651  {
652  iter = std::find( adj->begin(), adj->end(), entity );
653  if( iter != adj->end() ) adj->erase( iter );
654  }
655 
656  return result;
657 }
658 
660 {
661  unsigned char deletable = 0;
662  ErrorCode result = thisMB->tag_get_data( mDeletableMBTag, &entity, 1, &deletable );
663  assert( MB_SUCCESS == result );
664  if( MB_SUCCESS == result && deletable == 1 ) return false;
665  return true;
666 }
667 
669  const Range& bar_elements,
670  EntityHandle boundary_edges,
671  EntityHandle inferred_edges,
672  EntityHandle non_manifold_edges,
673  EntityHandle other_edges,
674  int& number_boundary_nodes )
675 {
676  Range bedges, iedges, nmedges, oedges;
677  ErrorCode result =
678  classify_2d_boundary( boundary, bar_elements, bedges, iedges, nmedges, oedges, number_boundary_nodes );
679  MB_CHK_ERR( result );
680 
681  // now set the input meshsets to the output ranges
682  result = thisMB->clear_meshset( &boundary_edges, 1 );
683  MB_CHK_ERR( result );
684  result = thisMB->add_entities( boundary_edges, bedges );
685  MB_CHK_ERR( result );
686 
687  result = thisMB->clear_meshset( &inferred_edges, 1 );
688  MB_CHK_ERR( result );
689  result = thisMB->add_entities( inferred_edges, iedges );
690  MB_CHK_ERR( result );
691 
692  result = thisMB->clear_meshset( &non_manifold_edges, 1 );
693  MB_CHK_ERR( result );
694  result = thisMB->add_entities( non_manifold_edges, nmedges );
695  MB_CHK_ERR( result );
696 
697  result = thisMB->clear_meshset( &other_edges, 1 );
698  MB_CHK_ERR( result );
699  result = thisMB->add_entities( other_edges, oedges );
700  MB_CHK_ERR( result );
701 
702  return MB_SUCCESS;
703 }
704 
706  const Range& bar_elements,
707  Range& boundary_edges,
708  Range& inferred_edges,
709  Range& non_manifold_edges,
710  Range& other_edges,
711  int& number_boundary_nodes )
712 {
713 
714  // clear out the edge lists
715 
716  boundary_edges.clear();
717  inferred_edges.clear();
718  non_manifold_edges.clear();
719  other_edges.clear();
720 
721  number_boundary_nodes = 0;
722 
723  // make sure we have something to work with
724  if( boundary.empty() )
725  {
726  return MB_FAILURE;
727  }
728 
729  // get our working dimensions
730  EntityType type = thisMB->type_from_handle( *( boundary.begin() ) );
731  const int source_dim = CN::Dimension( type );
732 
733  // make sure we can handle the working dimensions
734  if( source_dim != 2 )
735  {
736  return MB_FAILURE;
737  }
738  mTargetDim = source_dim - 1;
739 
740  // initialize
741  initialize();
742 
743  // additional initialization for this routine
744  // define a tag for MBEDGE which counts the occurrences of the edge below
745  // default should be 0 for existing edges, if any
746 
747  Tag count_tag;
748  int default_count = 0;
749  ErrorCode result =
750  thisMB->tag_get_handle( 0, 1, MB_TYPE_INTEGER, count_tag, MB_TAG_DENSE | MB_TAG_CREAT, &default_count );
751  MB_CHK_ERR( result );
752 
753  Range::const_iterator iter, end_iter;
754  end_iter = boundary.end();
755 
756  std::vector< EntityHandle > conn;
757  EntityHandle sub_conn[2];
758  EntityHandle match;
759 
760  Range edge_list;
761  Range boundary_nodes;
762  Skinner::direction direct;
763 
764  EntityType sub_type;
765  int num_edge, num_sub_ent_vert;
766  const short* edge_verts;
767 
768  // now, process each entity in the boundary
769 
770  for( iter = boundary.begin(); iter != end_iter; ++iter )
771  {
772  // get the connectivity of this entity
773  conn.clear();
774  result = thisMB->get_connectivity( &( *iter ), 1, conn, false );
775  assert( MB_SUCCESS == result );
776 
777  // add node handles to boundary_node range
778  std::copy( conn.begin(), conn.begin() + CN::VerticesPerEntity( type ), range_inserter( boundary_nodes ) );
779 
780  type = thisMB->type_from_handle( *iter );
781 
782  // get connectivity of each n-1 dimension entity (edge in this case)
783  const struct CN::ConnMap* conn_map = &( CN::mConnectivityMap[type][0] );
784  num_edge = CN::NumSubEntities( type, 1 );
785  for( int i = 0; i < num_edge; i++ )
786  {
787  edge_verts = CN::SubEntityVertexIndices( type, 1, i, sub_type, num_sub_ent_vert );
788  assert( sub_type == MBEDGE && num_sub_ent_vert == 2 );
789  sub_conn[0] = conn[edge_verts[0]];
790  sub_conn[1] = conn[edge_verts[1]];
791  int num_sub_nodes = conn_map->num_corners_per_sub_element[i];
792 
793  // see if we can match this connectivity with
794  // an existing entity
795  find_match( MBEDGE, sub_conn, num_sub_nodes, match, direct );
796 
797  // if there is no match, create a new entity
798  if( match == 0 )
799  {
800  EntityHandle tmphndl = 0;
801  int indices[MAX_SUB_ENTITY_VERTICES];
802  EntityType new_type;
803  int num_new_nodes;
804  CN::SubEntityNodeIndices( type, conn.size(), 1, i, new_type, num_new_nodes, indices );
805  for( int j = 0; j < num_new_nodes; j++ )
806  sub_conn[j] = conn[indices[j]];
807 
808  result = thisMB->create_element( new_type, sub_conn, num_new_nodes, tmphndl );
809  assert( MB_SUCCESS == result );
810  add_adjacency( tmphndl, sub_conn, num_sub_nodes );
811  // target_entities.insert(tmphndl);
812  edge_list.insert( tmphndl );
813  int count;
814  result = thisMB->tag_get_data( count_tag, &tmphndl, 1, &count );
815  assert( MB_SUCCESS == result );
816  count++;
817  result = thisMB->tag_set_data( count_tag, &tmphndl, 1, &count );
818  assert( MB_SUCCESS == result );
819  }
820  else
821  {
822  // We found a match, we must increment the count on the match
823  int count;
824  result = thisMB->tag_get_data( count_tag, &match, 1, &count );
825  assert( MB_SUCCESS == result );
826  count++;
827  result = thisMB->tag_set_data( count_tag, &match, 1, &count );
828  assert( MB_SUCCESS == result );
829 
830  // if the entity is not deletable, it was pre-existing in
831  // the database. We therefore may need to add it to the
832  // edge_list. Since it will not hurt the range, we add
833  // whether it was added before or not
834  if( !entity_deletable( match ) )
835  {
836  edge_list.insert( match );
837  }
838  }
839  }
840  }
841 
842  // Any bar elements in the model should be classified separately
843  // If the element is in the skin edge_list, then it should be put in
844  // the non-manifold edge list. Edges not in the edge_list are stand-alone
845  // bars, and we make them simply boundary elements
846 
847  if( !bar_elements.empty() )
848  {
849  Range::iterator bar_iter;
850  for( iter = bar_elements.begin(); iter != bar_elements.end(); ++iter )
851  {
852  EntityHandle handle = *iter;
853  bar_iter = edge_list.find( handle );
854  if( bar_iter != edge_list.end() )
855  {
856  // it is in the list, erase it and put in non-manifold list
857  edge_list.erase( bar_iter );
858  non_manifold_edges.insert( handle );
859  }
860  else
861  {
862  // not in the edge list, make it a boundary edge
863  boundary_edges.insert( handle );
864  }
865  }
866  }
867 
868  // now all edges should be classified. Go through the edge_list,
869  // and put all in the appropriate lists
870 
871  Range::iterator edge_iter, edge_end_iter;
872  edge_end_iter = edge_list.end();
873  int count;
874  for( edge_iter = edge_list.begin(); edge_iter != edge_end_iter; ++edge_iter )
875  {
876  // check the count_tag
877  result = thisMB->tag_get_data( count_tag, &( *edge_iter ), 1, &count );
878  assert( MB_SUCCESS == result );
879  if( count == 1 )
880  {
881  boundary_edges.insert( *edge_iter );
882  }
883  else if( count == 2 )
884  {
885  other_edges.insert( *edge_iter );
886  }
887  else
888  {
889  non_manifold_edges.insert( *edge_iter );
890  }
891  }
892 
893  // find the inferred edges from the other_edge_list
894 
895  double min_angle_degrees = 20.0;
896  find_inferred_edges( const_cast< Range& >( boundary ), other_edges, inferred_edges, min_angle_degrees );
897 
898  // we now want to remove the inferred_edges from the other_edges
899 
900  Range temp_range;
901 
902  std::set_difference( other_edges.begin(), other_edges.end(), inferred_edges.begin(), inferred_edges.end(),
903  range_inserter( temp_range ), std::less< EntityHandle >() );
904 
905  other_edges = temp_range;
906 
907  // get rid of count tag and deinitialize
908 
909  result = thisMB->tag_delete( count_tag );
910  assert( MB_SUCCESS == result );
911  deinitialize();
912 
913  // set the node count
914  number_boundary_nodes = boundary_nodes.size();
915 
916  return MB_SUCCESS;
917 }
918 
919 void Skinner::find_inferred_edges( Range& skin_boundary,
920  Range& candidate_edges,
921  Range& inferred_edges,
922  double reference_angle_degrees )
923 {
924 
925  // mark all the entities in the skin boundary
926  Tag mark_tag;
927  ErrorCode result = thisMB->tag_get_handle( 0, 1, MB_TYPE_BIT, mark_tag, MB_TAG_CREAT );
928  assert( MB_SUCCESS == result );
929  unsigned char bit = true;
930  result = thisMB->tag_clear_data( mark_tag, skin_boundary, &bit );
931  assert( MB_SUCCESS == result );
932 
933  // find the cosine of the reference angle
934 
935  double reference_cosine = cos( reference_angle_degrees * SKINNER_PI / 180.0 );
936 
937  // check all candidate edges for an angle greater than the minimum
938 
939  Range::iterator iter, end_iter = candidate_edges.end();
940  std::vector< EntityHandle > adjacencies;
941  std::vector< EntityHandle >::iterator adj_iter;
942  EntityHandle face[2];
943 
944  for( iter = candidate_edges.begin(); iter != end_iter; ++iter )
945  {
946 
947  // get the 2D elements connected to this edge
948  adjacencies.clear();
949  result = thisMB->get_adjacencies( &( *iter ), 1, 2, false, adjacencies );
950  if( MB_SUCCESS != result ) continue;
951 
952  // there should be exactly two, that is why the edge is classified as nonBoundary
953  // and manifold
954 
955  int faces_found = 0;
956  for( adj_iter = adjacencies.begin(); adj_iter != adjacencies.end() && faces_found < 2; ++adj_iter )
957  {
958  // we need to find two of these which are in the skin
959  unsigned char is_marked = 0;
960  result = thisMB->tag_get_data( mark_tag, &( *adj_iter ), 1, &is_marked );
961  assert( MB_SUCCESS == result );
962  if( is_marked )
963  {
964  face[faces_found] = *adj_iter;
965  faces_found++;
966  }
967  }
968 
969  // assert(faces_found == 2 || faces_found == 0);
970  if( 2 != faces_found ) continue;
971 
972  // see if the two entities have a sufficient angle
973 
974  if( has_larger_angle( face[0], face[1], reference_cosine ) )
975  {
976  inferred_edges.insert( *iter );
977  }
978  }
979 
980  result = thisMB->tag_delete( mark_tag );
981  assert( MB_SUCCESS == result );
982 }
983 
984 bool Skinner::has_larger_angle( EntityHandle& entity1, EntityHandle& entity2, double reference_angle_cosine )
985 {
986  // compare normals to get angle. We assume that the surface quads
987  // which we test here will be approximately planar
988 
989  double norm[2][3];
990  Util::normal( thisMB, entity1, norm[0][0], norm[0][1], norm[0][2] );
991  Util::normal( thisMB, entity2, norm[1][0], norm[1][1], norm[1][2] );
992 
993  double cosine = norm[0][0] * norm[1][0] + norm[0][1] * norm[1][1] + norm[0][2] * norm[1][2];
994 
995  if( cosine < reference_angle_cosine )
996  {
997  return true;
998  }
999 
1000  return false;
1001 }
1002 
1003 // get skin entities of prescribed dimension
1005  const Range& entities,
1006  int dim,
1007  Range& skin_entities,
1008  bool create_vert_elem_adjs,
1009  bool create_skin_elements )
1010 {
1011  Range tmp_skin;
1012  ErrorCode result =
1013  find_skin( this_set, entities, ( dim == 0 ), tmp_skin, 0, create_vert_elem_adjs, create_skin_elements );
1014  if( MB_SUCCESS != result || tmp_skin.empty() ) return result;
1015 
1016  if( tmp_skin.all_of_dimension( dim ) )
1017  {
1018  if( skin_entities.empty() )
1019  skin_entities.swap( tmp_skin );
1020  else
1021  skin_entities.merge( tmp_skin );
1022  }
1023  else
1024  {
1025  result = thisMB->get_adjacencies( tmp_skin, dim, create_skin_elements, skin_entities, Interface::UNION );
1026  MB_CHK_ERR( result );
1027  if( this_set ) result = thisMB->add_entities( this_set, skin_entities );
1028  }
1029 
1030  return result;
1031 }
1032 
1034  const Range& entities,
1035  Range* skin_verts,
1036  Range* skin_elems,
1037  Range* skin_rev_elems,
1038  bool create_skin_elems,
1039  bool corners_only )
1040 {
1041  ErrorCode rval;
1042  if( entities.empty() ) return MB_SUCCESS;
1043 
1044  const int dim = CN::Dimension( TYPE_FROM_HANDLE( entities.front() ) );
1045  if( dim < 1 || dim > 3 || !entities.all_of_dimension( dim ) ) return MB_TYPE_OUT_OF_RANGE;
1046 
1047  // are we skinning all entities
1048  size_t count = entities.size();
1049  int num_total;
1050  rval = thisMB->get_number_entities_by_dimension( this_set, dim, num_total );
1051  if( MB_SUCCESS != rval ) return rval;
1052  bool all = ( count == (size_t)num_total );
1053 
1054  // Create a bit tag for fast intersection with input entities range.
1055  // If we're skinning all the entities in the mesh, we really don't
1056  // need the tag. To save memory, just create it with a default value
1057  // of one and don't set it. That way MOAB will return 1 for all
1058  // entities.
1059  Tag tag;
1060  char bit = all ? 1 : 0;
1061  rval = thisMB->tag_get_handle( NULL, 1, MB_TYPE_BIT, tag, MB_TAG_CREAT, &bit );
1062  if( MB_SUCCESS != rval ) return rval;
1063 
1064  // tag all entities in input range
1065  if( !all )
1066  {
1067  std::vector< unsigned char > vect( count, 1 );
1068  rval = thisMB->tag_set_data( tag, entities, &vect[0] );
1069  if( MB_SUCCESS != rval )
1070  {
1071  thisMB->tag_delete( tag );
1072  return rval;
1073  }
1074  }
1075 
1076  switch( dim )
1077  {
1078  case 1:
1079  if( skin_verts )
1080  rval = find_skin_vertices_1D( tag, entities, *skin_verts );
1081  else if( skin_elems )
1082  rval = find_skin_vertices_1D( tag, entities, *skin_elems );
1083  else
1084  rval = MB_SUCCESS;
1085  break;
1086  case 2:
1087  rval = find_skin_vertices_2D( this_set, tag, entities, skin_verts, skin_elems, skin_rev_elems,
1088  create_skin_elems, corners_only );
1089  break;
1090  case 3:
1091  rval = find_skin_vertices_3D( this_set, tag, entities, skin_verts, skin_elems, skin_rev_elems,
1092  create_skin_elems, corners_only );
1093  break;
1094  default:
1095  rval = MB_TYPE_OUT_OF_RANGE;
1096  break;
1097  }
1098 
1099  thisMB->tag_delete( tag );
1100  return rval;
1101 }
1102 
1103 ErrorCode Skinner::find_skin_vertices_1D( Tag tag, const Range& edges, Range& skin_verts )
1104 {
1105  // This rather simple algorithm is provided for completeness
1106  // (not sure how often one really wants the 'skin' of a chain
1107  // or tangle of edges.)
1108  //
1109  // A vertex is on the skin of the edges if it is contained in exactly
1110  // one of the edges *in the input range*.
1111  //
1112  // This function expects the caller to have tagged all edges in the
1113  // input range with a value of one for the passed bit tag, and all
1114  // other edges with a value of zero. This allows us to do a faster
1115  // intersection with the input range and the edges adjacent to a vertex.
1116 
1117  ErrorCode rval;
1118  Range::iterator hint = skin_verts.begin();
1119 
1120  // All input entities must be edges.
1121  if( !edges.all_of_dimension( 1 ) ) return MB_TYPE_OUT_OF_RANGE;
1122 
1123  // get all the vertices
1124  Range verts;
1125  rval = thisMB->get_connectivity( edges, verts, true );
1126  if( MB_SUCCESS != rval ) return rval;
1127 
1128  // Test how many edges each input vertex is adjacent to.
1129  std::vector< char > tag_vals;
1130  std::vector< EntityHandle > adj;
1131  int n;
1132  for( Range::const_iterator it = verts.begin(); it != verts.end(); ++it )
1133  {
1134  // get edges adjacent to vertex
1135  adj.clear();
1136  rval = thisMB->get_adjacencies( &*it, 1, 1, false, adj );
1137  if( MB_SUCCESS != rval ) return rval;
1138  if( adj.empty() ) continue;
1139 
1140  // Intersect adjacent edges with the input list of edges
1141  tag_vals.resize( adj.size() );
1142  rval = thisMB->tag_get_data( tag, &adj[0], adj.size(), &tag_vals[0] );
1143  if( MB_SUCCESS != rval ) return rval;
1144 #ifdef MOAB_OLD_STD_COUNT
1145  n = 0;
1146  std::count( tag_vals.begin(), tag_vals.end(), '\001' );
1147 #else
1148  n = std::count( tag_vals.begin(), tag_vals.end(), '\001' );
1149 #endif
1150  // If adjacent to only one input edge, then vertex is on skin
1151  if( n == 1 )
1152  {
1153  hint = skin_verts.insert( hint, *it );
1154  }
1155  }
1156 
1157  return MB_SUCCESS;
1158 }
1159 
1160 // A Container for storing a list of element sides adjacent
1161 // to a vertex. The template parameter is the number of
1162 // corners for the side.
1163 template < unsigned CORNERS >
1165 {
1166  public:
1167  /**
1168  * This struct is used to for a reduced representation of element
1169  * "sides" adjacent to a give vertex. As such, it
1170  * a) does not store the initial vertex that all sides are adjacent to
1171  * b) orders the remaining vertices in a specific way for fast comparison.
1172  *
1173  * For edge elements, only the opposite vertex is stored.
1174  * For triangle elements, only the other two vertices are stored,
1175  * and they are stored with the smaller of those two handles first.
1176  * For quad elements, only the other three vertices are stored.
1177  * They are stored such that the vertex opposite the implicit (not
1178  * stored) vertex is always in slot 1. The other two vertices
1179  * (in slots 0 and 2) are ordered such that the handle of the one in
1180  * slot 0 is smaller than the handle in slot 2.
1181  *
1182  * For each side, the adj_elem field is used to store the element that
1183  * it is a side of as long as the element is considered to be on the skin.
1184  * The adj_elem field is cleared (set to zero) to indicate that this
1185  * side is no longer considered to be on the skin (and is the side of
1186  * more than one element.)
1187  */
1188  struct Side
1189  {
1190  EntityHandle handles[CORNERS - 1]; //!< side vertices, except for implicit one
1191  EntityHandle adj_elem; //!< element that this is a side of, or zero
1192  bool skin() const
1193  {
1194  return 0 != adj_elem;
1195  }
1196 
1197  /** construct from connectivity of side
1198  *\param array The connectivity of the element side.
1199  *\param idx The index of the implicit vertex (contained
1200  * in all sides in the list.)
1201  *\param adj The element that this is a side of.
1202  */
1203  Side( const EntityHandle* array, int idx, EntityHandle adj, unsigned short ) : adj_elem( adj )
1204  {
1205  switch( CORNERS )
1206  {
1207  case 3:
1208  handles[1] = array[( idx + 2 ) % CORNERS];
1209  // fall through
1210  case 2:
1211  if( 3 == CORNERS ) handles[1] = array[( idx + 2 ) % CORNERS];
1212  if( 2 <= CORNERS ) handles[0] = array[( idx + 1 ) % CORNERS];
1213  break;
1214  default:
1215  assert( false );
1216  break;
1217  }
1218  if( CORNERS == 3 && handles[1] > handles[0] ) std::swap( handles[0], handles[1] );
1219  }
1220 
1221  /** construct from connectivity of parent element
1222  *\param array The connectivity of the parent element
1223  *\param idx The index of the implicit vertex (contained
1224  * in all sides in the list.) This is an index
1225  * into 'indices', not 'array'.
1226  *\param adj The element that this is a side of.
1227  *\param indices The indices into 'array' at which the vertices
1228  * representing the side occur.
1229  */
1230  Side( const EntityHandle* array, int idx, EntityHandle adj, unsigned short, const short* indices )
1231  : adj_elem( adj )
1232  {
1233  switch( CORNERS )
1234  {
1235  case 3:
1236  handles[1] = array[indices[( idx + 2 ) % CORNERS]];
1237  // fall through
1238  case 2:
1239  if( 3 == CORNERS ) handles[1] = array[indices[( idx + 2 ) % CORNERS]];
1240  if( 2 <= CORNERS ) handles[0] = array[indices[( idx + 1 ) % CORNERS]];
1241  break;
1242  default:
1243  assert( false );
1244  break;
1245  }
1246  if( CORNERS == 3 && handles[1] > handles[0] ) std::swap( handles[0], handles[1] );
1247  }
1248 
1249  // Compare two side instances. Relies in the ordering of
1250  // vertex handles as described above.
1251  bool operator==( const Side& other ) const
1252  {
1253  switch( CORNERS )
1254  {
1255  case 3:
1256  return handles[0] == other.handles[0] && handles[1] == other.handles[1];
1257  case 2:
1258  return handles[0] == other.handles[0];
1259  default:
1260  assert( false );
1261  return false;
1262  }
1263  }
1264  };
1265 
1266  private:
1267  std::vector< Side > data; //!< List of sides
1268  size_t skin_count; //!< Cached count of sides that are skin
1269 
1270  public:
1271  typedef typename std::vector< Side >::iterator iterator;
1272  typedef typename std::vector< Side >::const_iterator const_iterator;
1274  {
1275  return data.begin();
1276  }
1278  {
1279  return data.end();
1280  }
1281 
1282  void clear()
1283  {
1284  data.clear();
1285  skin_count = 0;
1286  }
1287  bool empty() const
1288  {
1289  return data.empty();
1290  }
1291 
1292  AdjSides() : skin_count( 0 ) {}
1293 
1294  size_t num_skin() const
1295  {
1296  return skin_count;
1297  }
1298 
1299  /** \brief insert side, specifying side connectivity
1300  *
1301  * Either insert a new side, or if the side is already in the
1302  * list, mark it as not on the skin.
1303  *
1304  *\param handles The connectivity of the element side.
1305  *\param skip_idx The index of the implicit vertex (contained
1306  * in all sides in the list.)
1307  *\param adj_elem The element that this is a side of.
1308  *\param elem_side Which side of adj_elem are we storing
1309  * (CN side number.)
1310  */
1311  void insert( const EntityHandle* handles, int skip_idx, EntityHandle adj_elem, unsigned short elem_side )
1312  {
1313  Side side( handles, skip_idx, adj_elem, elem_side );
1314  iterator p = std::find( data.begin(), data.end(), side );
1315  if( p == data.end() )
1316  {
1317  data.push_back( side );
1318  ++skin_count; // not in list yet, so skin side (so far)
1319  }
1320  else if( p->adj_elem )
1321  {
1322  p->adj_elem = 0; // mark as not on skin
1323  --skin_count; // decrement cached count of skin elements
1324  }
1325  }
1326 
1327  /** \brief insert side, specifying list of indices into parent element
1328  * connectivity.
1329  *
1330  * Either insert a new side, or if the side is already in the
1331  * list, mark it as not on the skin.
1332  *
1333  *\param handles The connectivity of the parent element
1334  *\param skip_idx The index of the implicit vertex (contained
1335  * in all sides in the list.) This is an index
1336  * into 'indices', not 'handles'.
1337  *\param adj_elem The element that this is a side of (parent handle).
1338  *\param indices The indices into 'handles' at which the vertices
1339  * representing the side occur.
1340  *\param elem_side Which side of adj_elem are we storing
1341  * (CN side number.)
1342  */
1343  void insert( const EntityHandle* handles,
1344  int skip_idx,
1345  EntityHandle adj_elem,
1346  unsigned short elem_side,
1347  const short* indices )
1348  {
1349  Side side( handles, skip_idx, adj_elem, elem_side, indices );
1350  iterator p = std::find( data.begin(), data.end(), side );
1351  if( p == data.end() )
1352  {
1353  data.push_back( side );
1354  ++skin_count; // not in list yet, so skin side (so far)
1355  }
1356  else if( p->adj_elem )
1357  {
1358  p->adj_elem = 0; // mark as not on skin
1359  --skin_count; // decrement cached count of skin elements
1360  }
1361  }
1362 
1363  /**\brief Search list for a given side, and if found, mark as not skin.
1364  *
1365  *\param other Connectivity of side
1366  *\param skip_index Index in 'other' at which implicit vertex occurs.
1367  *\param elem_out If return value is true, the element that the side is a
1368  * side of. If return value is false, not set.
1369  *\return true if found and marked not-skin, false if not found.
1370  *
1371  * Given the connectivity of some existing element, check if it occurs
1372  * in the list. If it does, clear the "is skin" state of the side so
1373  * that we know that we don't need to later create the side element.
1374  */
1375  bool find_and_unmark( const EntityHandle* other, int skip_index, EntityHandle& elem_out )
1376  {
1377  Side s( other, skip_index, 0, 0 );
1378  iterator p = std::find( data.begin(), data.end(), s );
1379  if( p == data.end() || !p->adj_elem )
1380  return false;
1381  else
1382  {
1383  elem_out = p->adj_elem;
1384  p->adj_elem = 0; // clear "is skin" state for side
1385  --skin_count; // decrement cached count of skin sides
1386  return true;
1387  }
1388  }
1389 };
1390 
1391 /** construct from connectivity of side
1392  *\param array The connectivity of the element side.
1393  *\param idx The index of the implicit vertex (contained
1394  * in all sides in the list.)
1395  *\param adj The element that this is a side of.
1396  */
1397 template <>
1398 AdjSides< 4 >::Side::Side( const EntityHandle* array, int idx, EntityHandle adj, unsigned short ) : adj_elem( adj )
1399 {
1400  const unsigned int CORNERS = 4;
1401  handles[2] = array[( idx + 3 ) % CORNERS];
1402  handles[1] = array[( idx + 2 ) % CORNERS];
1403  handles[0] = array[( idx + 1 ) % CORNERS];
1404  if( handles[2] > handles[0] ) std::swap( handles[0], handles[2] );
1405 }
1406 
1407 /** construct from connectivity of parent element
1408  *\param array The connectivity of the parent element
1409  *\param idx The index of the implicit vertex (contained
1410  * in all sides in the list.) This is an index
1411  * into 'indices', not 'array'.
1412  *\param adj The element that this is a side of.
1413  *\param indices The indices into 'array' at which the vertices
1414  * representing the side occur.
1415  */
1416 template <>
1417 AdjSides< 4 >::Side::Side( const EntityHandle* array, int idx, EntityHandle adj, unsigned short, const short* indices )
1418  : adj_elem( adj )
1419 {
1420  const unsigned int CORNERS = 4;
1421  handles[2] = array[indices[( idx + 3 ) % CORNERS]];
1422  handles[1] = array[indices[( idx + 2 ) % CORNERS]];
1423  handles[0] = array[indices[( idx + 1 ) % CORNERS]];
1424  if( handles[2] > handles[0] ) std::swap( handles[0], handles[2] );
1425 }
1426 
1427 // Compare two side instances. Relies in the ordering of
1428 // vertex handles as described above.
1429 template <>
1430 bool AdjSides< 4 >::Side::operator==( const Side& other ) const
1431 {
1432  return handles[0] == other.handles[0] && handles[1] == other.handles[1] && handles[2] == other.handles[2];
1433 }
1434 
1435 // Utility function used by find_skin_vertices_2D and
1436 // find_skin_vertices_3D to create elements representing
1437 // the skin side of a higher-dimension element if one
1438 // does not already exist.
1439 //
1440 // Some arguments may seem redundant, but they are used
1441 // to create the correct order of element when the input
1442 // element contains higher-order nodes.
1443 //
1444 // This function always creates elements that have a "forward"
1445 // orientation with respect to the parent element (have
1446 // nodes ordered the same as CN returns for the "side").
1447 //
1448 // elem - The higher-dimension element for which to create
1449 // a lower-dim element representing the side.
1450 // side_type - The EntityType of the desired side.
1451 // side_conn - The connectivity of the new side.
1453  EntityHandle elem,
1454  EntityType side_type,
1455  const EntityHandle* side_conn,
1456  EntityHandle& side_elem )
1457 {
1458  const int max_side = 9;
1459  const EntityHandle* conn;
1460  int len, side_len, side, sense, offset, indices[max_side];
1461  ErrorCode rval;
1462  EntityType type = TYPE_FROM_HANDLE( elem ), tmp_type;
1463  const int ncorner = CN::VerticesPerEntity( side_type );
1464  const int d = CN::Dimension( side_type );
1465  std::vector< EntityHandle > storage;
1466 
1467  // Get the connectivity of the parent element
1468  rval = thisMB->get_connectivity( elem, conn, len, false, &storage );
1469  if( MB_SUCCESS != rval ) return rval;
1470 
1471  // treat separately MBPOLYGON; we want to create the edge in the
1472  // forward sense always ; so figure out the sense first, then get out
1473  if( MBPOLYGON == type && 1 == d && MBEDGE == side_type )
1474  {
1475  // first find the first vertex in the conn list
1476  int i = 0;
1477  for( i = 0; i < len; i++ )
1478  {
1479  if( conn[i] == side_conn[0] ) break;
1480  }
1481  if( len == i ) return MB_FAILURE; // not found, big error
1482  // now, what if the polygon is padded?
1483  // the previous index is fine always. but the next one could be trouble :(
1484  int prevIndex = ( i + len - 1 ) % len;
1485  int nextIndex = ( i + 1 ) % len;
1486  // if the next index actually point to the same node, as current, it means it is padded
1487  if( conn[nextIndex] == conn[i] )
1488  {
1489  // it really means we are at the end of proper nodes, the last nodes are repeated, so it
1490  // should be the first node
1491  nextIndex = 0; // this is the first node!
1492  }
1493  EntityHandle conn2[2] = { side_conn[0], side_conn[1] };
1494  if( conn[prevIndex] == side_conn[1] )
1495  {
1496  // reverse, so the edge will be forward
1497  conn2[0] = side_conn[1];
1498  conn2[1] = side_conn[0];
1499  }
1500  else if( conn[nextIndex] != side_conn[1] )
1501  return MB_FAILURE; // it is not adjacent to the polygon
1502 
1503  MB_CHK_ERR( thisMB->create_element( MBEDGE, conn2, 2, side_elem ) );
1504  if( this_set )
1505  {
1506  MB_CHK_ERR( thisMB->add_entities( this_set, &side_elem, 1 ) );
1507  }
1508  return MB_SUCCESS;
1509  }
1510  // Find which side we are creating and get indices of all nodes
1511  // (including higher-order, if any.)
1512  CN::SideNumber( type, conn, side_conn, ncorner, d, side, sense, offset );
1513  CN::SubEntityNodeIndices( type, len, d, side, tmp_type, side_len, indices );
1514  assert( side_len <= max_side );
1515  assert( side_type == tmp_type );
1516 
1517  // NOTE: re-create conn array even when no higher-order nodes
1518  // because we want it to always be forward with respect
1519  // to the side ordering.
1520  EntityHandle side_conn_full[max_side];
1521  for( int i = 0; i < side_len; ++i )
1522  side_conn_full[i] = conn[indices[i]];
1523 
1524  MB_CHK_ERR( thisMB->create_element( side_type, side_conn_full, side_len, side_elem ) );
1525  if( this_set )
1526  {
1527  MB_CHK_ERR( thisMB->add_entities( this_set, &side_elem, 1 ) );
1528  }
1529  return MB_SUCCESS;
1530  ;
1531 }
1532 
1533 // Test if an edge is reversed with respect CN's ordering
1534 // for the "side" of a face.
1536 {
1537  const EntityHandle* conn;
1538  int len, idx;
1539  ErrorCode rval = thisMB->get_connectivity( face, conn, len, true );
1540  if( MB_SUCCESS != rval )
1541  {
1542  assert( false );
1543  return false;
1544  }
1545  idx = std::find( conn, conn + len, edge_ends[0] ) - conn;
1546  if( idx == len )
1547  {
1548  assert( false );
1549  return false;
1550  }
1551  return ( edge_ends[1] == conn[( idx + len - 1 ) % len] );
1552 }
1553 
1554 // Test if a 2D element representing the side or face of a
1555 // volume element is reversed with respect to the CN node
1556 // ordering for the corresponding region element side.
1557 bool Skinner::face_reversed( EntityHandle region, const EntityHandle* face_corners, EntityType face_type )
1558 {
1559  const EntityHandle* conn;
1560  int len, side, sense, offset;
1561  ErrorCode rval = thisMB->get_connectivity( region, conn, len, true );
1562  if( MB_SUCCESS != rval )
1563  {
1564  assert( false );
1565  return false;
1566  }
1567  short r = CN::SideNumber( TYPE_FROM_HANDLE( region ), conn, face_corners, CN::VerticesPerEntity( face_type ),
1568  CN::Dimension( face_type ), side, sense, offset );
1569  assert( 0 == r );
1570  return ( !r && sense == -1 );
1571 }
1572 
1574  Tag tag,
1575  const Range& faces,
1576  Range* skin_verts,
1577  Range* skin_edges,
1578  Range* reversed_edges,
1579  bool create_edges,
1580  bool corners_only )
1581 {
1582  // This function iterates over all the vertices contained in the
1583  // input face list. For each such vertex, it then iterates over
1584  // all of the sides of the face elements adjacent to the vertex.
1585  // If an adjacent side is the side of only one of the input
1586  // faces, then that side is on the skin.
1587  //
1588  // This algorithm will visit each skin vertex exactly once. It
1589  // will visit each skin side once for each vertex in the side.
1590  //
1591  // This function expects the caller to have created the passed bit
1592  // tag and set it to one only for the faces in the passed range. This
1593  // tag is used to do a fast intersection of the faces adjacent to a
1594  // vertex with the faces in the input range (discard any for which the
1595  // tag is not set to one.)
1596 
1597  ErrorCode rval;
1598  std::vector< EntityHandle >::iterator i, j;
1599  Range::iterator hint;
1600  if( skin_verts ) hint = skin_verts->begin();
1601  std::vector< EntityHandle > storage;
1602  const EntityHandle* conn;
1603  int len;
1604  bool find_edges = skin_edges || create_edges;
1605  bool printed_nonconformal_ho_warning = false;
1607 
1608  if( !faces.all_of_dimension( 2 ) ) return MB_TYPE_OUT_OF_RANGE;
1609 
1610  // get all the vertices
1611  Range verts;
1612  rval = thisMB->get_connectivity( faces, verts, true );
1613  if( MB_SUCCESS != rval ) return rval;
1614 
1615  std::vector< char > tag_vals;
1616  std::vector< EntityHandle > adj;
1617  AdjSides< 2 > adj_edges;
1618  for( Range::const_iterator it = verts.begin(); it != verts.end(); ++it )
1619  {
1620  bool higher_order = false;
1621 
1622  // get all adjacent faces
1623  adj.clear();
1624  rval = thisMB->get_adjacencies( &*it, 1, 2, false, adj );
1625  if( MB_SUCCESS != rval ) return rval;
1626  if( adj.empty() ) continue;
1627 
1628  // remove those not in the input list (intersect with input list)
1629  i = j = adj.begin();
1630  tag_vals.resize( adj.size() );
1631  rval = thisMB->tag_get_data( tag, &adj[0], adj.size(), &tag_vals[0] );
1632  if( MB_SUCCESS != rval ) return rval;
1633  // remove non-tagged entries
1634  i = j = adj.begin();
1635  for( ; i != adj.end(); ++i )
1636  if( tag_vals[i - adj.begin()] ) *( j++ ) = *i;
1637  adj.erase( j, adj.end() );
1638 
1639  // For each adjacent face, check the edges adjacent to the current vertex
1640  adj_edges.clear(); // other vertex for adjacent edges
1641  for( i = adj.begin(); i != adj.end(); ++i )
1642  {
1643  rval = thisMB->get_connectivity( *i, conn, len, false, &storage );
1644  if( MB_SUCCESS != rval ) return rval;
1645 
1646  // For a single face element adjacent to this vertex, there
1647  // will be exactly two sides (edges) adjacent to the vertex.
1648  // Find the other vertex for each of the two edges.
1649 
1650  EntityHandle prev, next; // vertices of two adjacent edge-sides
1651  const int idx = std::find( conn, conn + len, *it ) - conn;
1652  assert( idx != len );
1653 
1654  if( TYPE_FROM_HANDLE( *i ) == MBTRI && len > 3 )
1655  {
1656  len = 3;
1657  higher_order = true;
1658  if( idx > 2 )
1659  { // skip higher-order nodes for now
1660  if( !printed_nonconformal_ho_warning )
1661  {
1662  printed_nonconformal_ho_warning = true;
1663  std::cerr << "Non-conformal higher-order mesh detected in skinner: "
1664  << "vertex " << ID_FROM_HANDLE( *it ) << " is a corner in "
1665  << "some elements and a higher-order node in others" << std::endl;
1666  }
1667  continue;
1668  }
1669  }
1670  else if( TYPE_FROM_HANDLE( *i ) == MBQUAD && len > 4 )
1671  {
1672  len = 4;
1673  higher_order = true;
1674  if( idx > 3 )
1675  { // skip higher-order nodes for now
1676  if( !printed_nonconformal_ho_warning )
1677  {
1678  printed_nonconformal_ho_warning = true;
1679  std::cerr << "Non-conformal higher-order mesh detected in skinner: "
1680  << "vertex " << ID_FROM_HANDLE( *it ) << " is a corner in "
1681  << "some elements and a higher-order node in others" << std::endl;
1682  }
1683  continue;
1684  }
1685  }
1686 
1687  // so it must be a MBPOLYGON
1688  const int prev_idx = ( idx + len - 1 ) % len; // this should be fine, always, even for padded case
1689  prev = conn[prev_idx];
1690  next = conn[( idx + 1 ) % len];
1691  if( next == conn[idx] ) // it must be the padded case, so roll to the beginning
1692  next = conn[0];
1693 
1694  // Insert sides (edges) in our list of candidate skin sides
1695  adj_edges.insert( &prev, 1, *i, prev_idx );
1696  adj_edges.insert( &next, 1, *i, idx );
1697  }
1698 
1699  // If vertex is not on skin, advance to next vertex.
1700  // adj_edges handled checking for duplicates on insertion.
1701  // If every candidate skin edge occurred more than once (was
1702  // not in fact on the skin), then we're done with this vertex.
1703  if( 0 == adj_edges.num_skin() ) continue;
1704 
1705  // If user requested Range of *vertices* on the skin...
1706  if( skin_verts )
1707  {
1708  // Put skin vertex in output list
1709  hint = skin_verts->insert( hint, *it );
1710 
1711  // Add mid edge nodes to vertex list
1712  if( !corners_only && higher_order )
1713  {
1714  for( AdjSides< 2 >::const_iterator p = adj_edges.begin(); p != adj_edges.end(); ++p )
1715  {
1716  if( p->skin() )
1717  {
1718  face = p->adj_elem;
1719  EntityType type = TYPE_FROM_HANDLE( face );
1720 
1721  rval = thisMB->get_connectivity( face, conn, len, false );
1722  if( MB_SUCCESS != rval ) return rval;
1723  if( !CN::HasMidEdgeNodes( type, len ) ) continue;
1724 
1725  EntityHandle ec[2] = { *it, p->handles[0] };
1726  int side, sense, offset;
1727  CN::SideNumber( type, conn, ec, 2, 1, side, sense, offset );
1728  offset = CN::HONodeIndex( type, len, 1, side );
1729  assert( offset >= 0 && offset < len );
1730  skin_verts->insert( conn[offset] );
1731  }
1732  }
1733  }
1734  }
1735 
1736  // If user requested Range of *edges* on the skin...
1737  if( find_edges )
1738  {
1739  // Search list of existing adjacent edges for any that are on the skin
1740  adj.clear();
1741  rval = thisMB->get_adjacencies( &*it, 1, 1, false, adj );
1742  if( MB_SUCCESS != rval ) return rval;
1743  for( i = adj.begin(); i != adj.end(); ++i )
1744  {
1745  rval = thisMB->get_connectivity( *i, conn, len, true );
1746  if( MB_SUCCESS != rval ) return rval;
1747 
1748  // bool equality expression within find_and_unmark call
1749  // will be evaluate to the index of *it in the conn array.
1750  //
1751  // Note that the order of the terms in the if statement is important.
1752  // We want to unmark any existing skin edges even if we aren't
1753  // returning them. Otherwise we'll end up creating duplicates
1754  // if create_edges is true and skin_edges is not.
1755  if( adj_edges.find_and_unmark( conn, ( conn[1] == *it ), face ) && skin_edges )
1756  {
1757  if( reversed_edges && edge_reversed( face, conn ) )
1758  reversed_edges->insert( *i );
1759  else
1760  skin_edges->insert( *i );
1761  }
1762  }
1763  }
1764 
1765  // If the user requested that we create new edges for sides
1766  // on the skin for which there is no existing edge, and there
1767  // are still skin sides for which no corresponding edge was found...
1768  if( create_edges && adj_edges.num_skin() )
1769  {
1770  // Create any skin edges that don't exist
1771  for( AdjSides< 2 >::const_iterator p = adj_edges.begin(); p != adj_edges.end(); ++p )
1772  {
1773  if( p->skin() )
1774  {
1775  EntityHandle edge, ec[] = { *it, p->handles[0] };
1776  rval = create_side( this_set, p->adj_elem, MBEDGE, ec, edge );
1777  if( MB_SUCCESS != rval ) return rval;
1778  if( skin_edges ) skin_edges->insert( edge );
1779  }
1780  }
1781  }
1782 
1783  } // end for each vertex
1784 
1785  return MB_SUCCESS;
1786 }
1787 
1789  Tag tag,
1790  const Range& entities,
1791  Range* skin_verts,
1792  Range* skin_faces,
1793  Range* reversed_faces,
1794  bool create_faces,
1795  bool corners_only )
1796 {
1797  // This function iterates over all the vertices contained in the
1798  // input vol elem list. For each such vertex, it then iterates over
1799  // all of the sides of the vol elements adjacent to the vertex.
1800  // If an adjacent side is the side of only one of the input
1801  // elements, then that side is on the skin.
1802  //
1803  // This algorithm will visit each skin vertex exactly once. It
1804  // will visit each skin side once for each vertex in the side.
1805  //
1806  // This function expects the caller to have created the passed bit
1807  // tag and set it to one only for the elements in the passed range. This
1808  // tag is used to do a fast intersection of the elements adjacent to a
1809  // vertex with the elements in the input range (discard any for which the
1810  // tag is not set to one.)
1811  //
1812  // For each vertex, iterate over each adjacent element. Construct
1813  // lists of the sides of each adjacent element that contain the vertex.
1814  //
1815  // A list of three-vertex sides is kept for all triangular faces,
1816  // included three-vertex faces of type MBPOLYGON. Putting polygons
1817  // in the same list ensures that we find polyhedron and non-polyhedron
1818  // elements that are adjacent.
1819  //
1820  // A list of four-vertex sides is kept for all quadrilateral faces,
1821  // including four-vertex faces of type MBPOLYGON.
1822  //
1823  // Sides with more than four vertices must have an explicit MBPOLYGON
1824  // element representing them because MBPOLYHEDRON connectivity is a
1825  // list of faces rather than vertices. So the third list (vertices>=5),
1826  // need contain only the handle of the face rather than the vertex handles.
1827 
1828  ErrorCode rval;
1829  std::vector< EntityHandle >::iterator i, j;
1830  Range::iterator hint;
1831  if( skin_verts ) hint = skin_verts->begin();
1832  std::vector< EntityHandle > storage, storage2; // temp storage for conn lists
1833  const EntityHandle *conn, *conn2;
1834  int len, len2;
1835  bool find_faces = skin_faces || create_faces;
1836  int clen, side, sense, offset, indices[9];
1837  EntityType face_type;
1838  EntityHandle elem;
1839  bool printed_nonconformal_ho_warning = false;
1840 
1841  if( !entities.all_of_dimension( 3 ) ) return MB_TYPE_OUT_OF_RANGE;
1842 
1843  // get all the vertices
1844  Range verts;
1845  rval = thisMB->get_connectivity( entities, verts, true );
1846  if( MB_SUCCESS != rval ) return rval;
1847  // if there are polyhedra in the input list, need to make another
1848  // call to get vertices from faces
1849  if( !verts.all_of_dimension( 0 ) )
1850  {
1851  Range::iterator it = verts.upper_bound( MBVERTEX );
1852  Range pfaces;
1853  pfaces.merge( it, verts.end() );
1854  verts.erase( it, verts.end() );
1855  rval = thisMB->get_connectivity( pfaces, verts, true );
1856  if( MB_SUCCESS != rval ) return rval;
1857  assert( verts.all_of_dimension( 0 ) );
1858  }
1859 
1860  AdjSides< 4 > adj_quads; // 4-node sides adjacent to a vertex
1861  AdjSides< 3 > adj_tris; // 3-node sides adjacent to a vertex
1862  AdjSides< 2 > adj_poly; // n-node sides (n>5) adjacent to vertex
1863  // (must have an explicit polygon, so store
1864  // polygon handle rather than vertices.)
1865  std::vector< char > tag_vals;
1866  std::vector< EntityHandle > adj;
1867  for( Range::const_iterator it = verts.begin(); it != verts.end(); ++it )
1868  {
1869  bool higher_order = false;
1870 
1871  // get all adjacent elements
1872  adj.clear();
1873  rval = thisMB->get_adjacencies( &*it, 1, 3, false, adj );
1874  if( MB_SUCCESS != rval ) return rval;
1875  if( adj.empty() ) continue;
1876 
1877  // remove those not tagged (intersect with input range)
1878  i = j = adj.begin();
1879  tag_vals.resize( adj.size() );
1880  rval = thisMB->tag_get_data( tag, &adj[0], adj.size(), &tag_vals[0] );
1881  if( MB_SUCCESS != rval ) return rval;
1882  for( ; i != adj.end(); ++i )
1883  if( tag_vals[i - adj.begin()] ) *( j++ ) = *i;
1884  adj.erase( j, adj.end() );
1885 
1886  // Build lists of sides of 3D element adjacent to the current vertex
1887  adj_quads.clear(); // store three other vertices for each adjacent quad face
1888  adj_tris.clear(); // store two other vertices for each adjacent tri face
1889  adj_poly.clear(); // store handle of each adjacent polygonal face
1890  int idx;
1891  for( i = adj.begin(); i != adj.end(); ++i )
1892  {
1893  const EntityType type = TYPE_FROM_HANDLE( *i );
1894 
1895  // Special case for POLYHEDRA
1896  if( type == MBPOLYHEDRON )
1897  {
1898  rval = thisMB->get_connectivity( *i, conn, len );
1899  if( MB_SUCCESS != rval ) return rval;
1900  for( int k = 0; k < len; ++k )
1901  {
1902  rval = thisMB->get_connectivity( conn[k], conn2, len2, true, &storage2 );
1903  if( MB_SUCCESS != rval ) return rval;
1904  idx = std::find( conn2, conn2 + len2, *it ) - conn2;
1905  if( idx == len2 ) // vertex not in this face
1906  continue;
1907 
1908  // Treat 3- and 4-vertex faces specially, so that
1909  // if the mesh contains both elements and polyhedra,
1910  // we don't miss one type adjacent to the other.
1911  switch( len2 )
1912  {
1913  case 3:
1914  adj_tris.insert( conn2, idx, *i, k );
1915  break;
1916  case 4:
1917  adj_quads.insert( conn2, idx, *i, k );
1918  break;
1919  default:
1920  adj_poly.insert( conn + k, 1, *i, k );
1921  break;
1922  }
1923  }
1924  }
1925  else
1926  {
1927  rval = thisMB->get_connectivity( *i, conn, len, false, &storage );
1928  if( MB_SUCCESS != rval ) return rval;
1929 
1930  idx = std::find( conn, conn + len, *it ) - conn;
1931  assert( idx != len );
1932 
1933  if( len > CN::VerticesPerEntity( type ) )
1934  {
1935  higher_order = true;
1936  // skip higher-order nodes for now
1937  if( idx >= CN::VerticesPerEntity( type ) )
1938  {
1939  if( !printed_nonconformal_ho_warning )
1940  {
1941  printed_nonconformal_ho_warning = true;
1942  std::cerr << "Non-conformal higher-order mesh detected in skinner: "
1943  << "vertex " << ID_FROM_HANDLE( *it ) << " is a corner in "
1944  << "some elements and a higher-order node in others" << std::endl;
1945  }
1946  continue;
1947  }
1948  }
1949 
1950  // For each side of the element...
1951  const int num_faces = CN::NumSubEntities( type, 2 );
1952  for( int f = 0; f < num_faces; ++f )
1953  {
1954  int num_vtx;
1955  const short* face_indices = CN::SubEntityVertexIndices( type, 2, f, face_type, num_vtx );
1956  const short face_idx = std::find( face_indices, face_indices + num_vtx, (short)idx ) - face_indices;
1957  // skip sides that do not contain vertex from outer loop
1958  if( face_idx == num_vtx ) continue; // current vertex not in this face
1959 
1960  assert( num_vtx <= 4 ); // polyhedra handled above
1961  switch( face_type )
1962  {
1963  case MBTRI:
1964  adj_tris.insert( conn, face_idx, *i, f, face_indices );
1965  break;
1966  case MBQUAD:
1967  adj_quads.insert( conn, face_idx, *i, f, face_indices );
1968  break;
1969  default:
1970  return MB_TYPE_OUT_OF_RANGE;
1971  }
1972  }
1973  }
1974  } // end for (adj[3])
1975 
1976  // If vertex is not on skin, advance to next vertex
1977  if( 0 == ( adj_tris.num_skin() + adj_quads.num_skin() + adj_poly.num_skin() ) ) continue;
1978 
1979  // If user requested that skin *vertices* be passed back...
1980  if( skin_verts )
1981  {
1982  // Put skin vertex in output list
1983  hint = skin_verts->insert( hint, *it );
1984 
1985  // Add mid-edge and mid-face nodes to vertex list
1986  if( !corners_only && higher_order )
1987  {
1988  for( AdjSides< 3 >::const_iterator t = adj_tris.begin(); t != adj_tris.end(); ++t )
1989  {
1990  if( t->skin() )
1991  {
1992  elem = t->adj_elem;
1993  EntityType type = TYPE_FROM_HANDLE( elem );
1994 
1995  rval = thisMB->get_connectivity( elem, conn, len, false );
1996  if( MB_SUCCESS != rval ) return rval;
1997  if( !CN::HasMidNodes( type, len ) ) continue;
1998 
1999  EntityHandle ec[3] = { *it, t->handles[0], t->handles[1] };
2000  CN::SideNumber( type, conn, ec, 3, 2, side, sense, offset );
2001  CN::SubEntityNodeIndices( type, len, 2, side, face_type, clen, indices );
2002  assert( MBTRI == face_type );
2003  for( int k = 3; k < clen; ++k )
2004  skin_verts->insert( conn[indices[k]] );
2005  }
2006  }
2007  for( AdjSides< 4 >::const_iterator q = adj_quads.begin(); q != adj_quads.end(); ++q )
2008  {
2009  if( q->skin() )
2010  {
2011  elem = q->adj_elem;
2012  EntityType type = TYPE_FROM_HANDLE( elem );
2013 
2014  rval = thisMB->get_connectivity( elem, conn, len, false );
2015  if( MB_SUCCESS != rval ) return rval;
2016  if( !CN::HasMidNodes( type, len ) ) continue;
2017 
2018  EntityHandle ec[4] = { *it, q->handles[0], q->handles[1], q->handles[2] };
2019  CN::SideNumber( type, conn, ec, 4, 2, side, sense, offset );
2020  CN::SubEntityNodeIndices( type, len, 2, side, face_type, clen, indices );
2021  assert( MBQUAD == face_type );
2022  for( int k = 4; k < clen; ++k )
2023  skin_verts->insert( conn[indices[k]] );
2024  }
2025  }
2026  }
2027  }
2028 
2029  // If user requested that we pass back the list of 2D elements
2030  // representing the skin of the mesh...
2031  if( find_faces )
2032  {
2033  // Search list of adjacent faces for any that are on the skin
2034  adj.clear();
2035  rval = thisMB->get_adjacencies( &*it, 1, 2, false, adj );
2036  if( MB_SUCCESS != rval ) return rval;
2037 
2038  for( i = adj.begin(); i != adj.end(); ++i )
2039  {
2040  rval = thisMB->get_connectivity( *i, conn, len, true );
2041  if( MB_SUCCESS != rval ) return rval;
2042  const int idx2 = std::find( conn, conn + len, *it ) - conn;
2043  if( idx2 >= len )
2044  {
2045  assert( printed_nonconformal_ho_warning );
2046  continue;
2047  }
2048 
2049  // Note that the order of the terms in the if statements below
2050  // is important. We want to unmark any existing skin faces even
2051  // if we aren't returning them. Otherwise we'll end up creating
2052  // duplicates if create_faces is true.
2053  if( 3 == len )
2054  {
2055  if( adj_tris.find_and_unmark( conn, idx2, elem ) && skin_faces )
2056  {
2057  if( reversed_faces && face_reversed( elem, conn, MBTRI ) )
2058  reversed_faces->insert( *i );
2059  else
2060  skin_faces->insert( *i );
2061  }
2062  }
2063  else if( 4 == len )
2064  {
2065  if( adj_quads.find_and_unmark( conn, idx2, elem ) && skin_faces )
2066  {
2067  if( reversed_faces && face_reversed( elem, conn, MBQUAD ) )
2068  reversed_faces->insert( *i );
2069  else
2070  skin_faces->insert( *i );
2071  }
2072  }
2073  else
2074  {
2075  if( adj_poly.find_and_unmark( &*i, 1, elem ) && skin_faces ) skin_faces->insert( *i );
2076  }
2077  }
2078  }
2079 
2080  // If user does not want use to create new faces representing
2081  // sides for which there is currently no explicit element,
2082  // skip the remaining code and advance the outer loop to the
2083  // next vertex.
2084  if( !create_faces ) continue;
2085 
2086  // Polyhedra always have explicitly defined faces, so
2087  // there is no way we could need to create such a face.
2088  assert( 0 == adj_poly.num_skin() );
2089 
2090  // Create any skin tris that don't exist
2091  if( adj_tris.num_skin() )
2092  {
2093  for( AdjSides< 3 >::const_iterator t = adj_tris.begin(); t != adj_tris.end(); ++t )
2094  {
2095  if( t->skin() )
2096  {
2097  EntityHandle tri, c[3] = { *it, t->handles[0], t->handles[1] };
2098  rval = create_side( this_set, t->adj_elem, MBTRI, c, tri );
2099  if( MB_SUCCESS != rval ) return rval;
2100  if( skin_faces ) skin_faces->insert( tri );
2101  }
2102  }
2103  }
2104 
2105  // Create any skin quads that don't exist
2106  if( adj_quads.num_skin() )
2107  {
2108  for( AdjSides< 4 >::const_iterator q = adj_quads.begin(); q != adj_quads.end(); ++q )
2109  {
2110  if( q->skin() )
2111  {
2112  EntityHandle quad, c[4] = { *it, q->handles[0], q->handles[1], q->handles[2] };
2113  rval = create_side( this_set, q->adj_elem, MBQUAD, c, quad );
2114  if( MB_SUCCESS != rval ) return rval;
2115  if( skin_faces ) skin_faces->insert( quad );
2116  }
2117  }
2118  }
2119  } // end for each vertex
2120 
2121  return MB_SUCCESS;
2122 }
2123 
2124 } // namespace moab