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