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 */1516#ifdef __MFC_VER17#pragmawarning( disable : 4786 )18#endif1920#ifdef WIN32 /* windows */21#define _USE_MATH_DEFINES // For M_PI22#endif23#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"3839#ifdef M_PI40#define SKINNER_PI M_PI41#else42#define SKINNER_PI 3.141592653589793238462643#endif4445namespace moab
46 {
4748 Skinner::~Skinner()
49 {
50// delete the adjacency tag51 }
5253ErrorCode Skinner::initialize()
54 {
55// go through and mark all the target dimension entities56// that already exist as not deleteable57// also get the connectivity tags for each type58// also populate adjacency information59 EntityType type;
60 DimensionPair target_ent_types = CN::TypeDimensionMap[mTargetDim];
6162void* null_ptr = NULL;
6364 ErrorCode result = thisMB->tag_get_handle( "skinner adj", sizeof( void* ), MB_TYPE_OPAQUE, mAdjTag,
65 MB_TAG_DENSE | MB_TAG_CREAT, &null_ptr );MB_CHK_ERR( result );
6667if( mDeletableMBTag == 0 )
68 {
69 result =
70 thisMB->tag_get_handle( "skinner deletable", 1, MB_TYPE_BIT, mDeletableMBTag, MB_TAG_BIT | MB_TAG_CREAT );MB_CHK_ERR( result );
71 }
7273 Range entities;
7475// go through each type at this dimension76for( type = target_ent_types.first; type <= target_ent_types.second; ++type )
77 {
78// get the entities of this type in the MB79 thisMB->get_entities_by_type( 0, type, entities );
8081// go through each entity of this type in the MB82// and set its deletable tag to NO83 Range::iterator iter, end_iter;
84 end_iter = entities.end();
85for( iter = entities.begin(); iter != end_iter; ++iter )
86 {
87unsignedchar bit = 0x1;
88 result = thisMB->tag_set_data( mDeletableMBTag, &( *iter ), 1, &bit );
89assert( MB_SUCCESS == result );
90// add adjacency information too91if( TYPE_FROM_HANDLE( *iter ) != MBVERTEX ) add_adjacency( *iter );
92 }
93 }
9495return MB_SUCCESS;
96 }
9798ErrorCode Skinner::deinitialize()
99 {
100 ErrorCode result;
101if( 0 != mDeletableMBTag )
102 {
103 result = thisMB->tag_delete( mDeletableMBTag );
104 mDeletableMBTag = 0;MB_CHK_ERR( result );
105 }
106107// remove the adjacency tag108 std::vector< std::vector< EntityHandle >* > adj_arr;
109 std::vector< std::vector< EntityHandle >* >::iterator i;
110if( 0 != mAdjTag )
111 {
112for( EntityType t = MBVERTEX; t != MBMAXTYPE; ++t )
113 {
114 Range entities;
115 result = thisMB->get_entities_by_type_and_tag( 0, t, &mAdjTag, 0, 1, entities );MB_CHK_ERR( result );
116 adj_arr.resize( entities.size() );
117 result = thisMB->tag_get_data( mAdjTag, entities, &adj_arr[0] );MB_CHK_ERR( result );
118for( i = adj_arr.begin(); i != adj_arr.end(); ++i )
119delete *i;
120 }
121122 result = thisMB->tag_delete( mAdjTag );
123 mAdjTag = 0;MB_CHK_ERR( result );
124 }
125126return MB_SUCCESS;
127 }
128129ErrorCode Skinner::add_adjacency( EntityHandle entity )
130 {
131 std::vector< EntityHandle >* adj = NULL;
132const EntityHandle* nodes;
133int num_nodes;
134 ErrorCode result = thisMB->get_connectivity( entity, nodes, num_nodes, true );MB_CHK_ERR( result );
135const EntityHandle* iter = std::min_element( nodes, nodes + num_nodes );
136137if( iter == nodes + num_nodes ) return MB_SUCCESS;
138139// add this entity to the node140if( 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 it145else146 {
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 }
151152return MB_SUCCESS;
153 }
154155voidSkinner::add_adjacency( EntityHandle entity, const EntityHandle* nodes, constint num_nodes )
156 {
157 std::vector< EntityHandle >* adj = NULL;
158const EntityHandle* iter = std::min_element( nodes, nodes + num_nodes );
159160if( iter == nodes + num_nodes ) return;
161162// should not be setting adjacency lists in ho-nodes163assert( TYPE_FROM_HANDLE( entity ) == MBPOLYGON ||
164 num_nodes == CN::VerticesPerEntity( TYPE_FROM_HANDLE( entity ) ) );
165166// add this entity to the node167if( thisMB->tag_get_data( mAdjTag, iter, 1, &adj ) == MB_SUCCESS && adj != NULL )
168 {
169 adj->push_back( entity );
170 }
171// create a new vector and add it172else173 {
174 adj = new std::vector< EntityHandle >;
175 adj->push_back( entity );
176 thisMB->tag_set_data( mAdjTag, iter, 1, &adj );
177 }
178 }
179180ErrorCode Skinner::find_geometric_skin( const EntityHandle meshset, Range& forward_target_entities )
181 {
182// attempts to find whole model skin, using geom topo sets first then183// normal find_skin function184bool debug = true;
185186// look for geom topo sets187 Tag geom_tag;
188 ErrorCode result =
189 thisMB->tag_get_handle( GEOM_DIMENSION_TAG_NAME, 1, MB_TYPE_INTEGER, geom_tag, MB_TAG_SPARSE | MB_TAG_CREAT );
190191if( MB_SUCCESS != result ) return result;
192193// get face sets (dimension = 2)194 Range face_sets;
195int two = 2;
196constvoid* two_ptr = &two;
197 result = thisMB->get_entities_by_type_and_tag( meshset, MBENTITYSET, &geom_tag, &two_ptr, 1, face_sets );
198199 Range::iterator it;
200if( MB_SUCCESS != result )
201return result;
202elseif( face_sets.empty() )
203return MB_ENTITY_NOT_FOUND;
204205// ok, we have face sets; use those to determine skin206 Range skin_sets;
207if( debug ) std::cout << "Found " << face_sets.size() << " face sets total..." << std::endl;
208209for( it = face_sets.begin(); it != face_sets.end(); ++it )
210 {
211int num_parents;
212 result = thisMB->num_parent_meshsets( *it, &num_parents );
213if( MB_SUCCESS != result )
214return result;
215elseif( num_parents == 1 )
216 skin_sets.insert( *it );
217 }
218219if( debug ) std::cout << "Found " << skin_sets.size() << " 1-parent face sets..." << std::endl;
220221if( skin_sets.empty() ) return MB_FAILURE;
222223// ok, we have the shell; gather up the elements, putting them all in forward for now224for( it = skin_sets.begin(); it != skin_sets.end(); ++it )
225 {
226 result = thisMB->get_entities_by_handle( *it, forward_target_entities, true );
227if( MB_SUCCESS != result ) return result;
228 }
229230return result;
231 }
232233ErrorCode Skinner::find_skin( const EntityHandle meshset,
234const Range& source_entities,
235bool get_vertices,
236 Range& output_handles,
237 Range* output_reverse_handles,
238bool create_vert_elem_adjs,
239bool create_skin_elements,
240bool look_for_scd )
241 {
242if( source_entities.empty() ) return MB_SUCCESS;
243244if( look_for_scd )
245 {
246 ErrorCode rval = find_skin_scd( source_entities, get_vertices, output_handles, create_skin_elements );
247// if it returns success, it's all scd, and we don't need to do anything more248if( MB_SUCCESS == rval ) return rval;
249 }
250251 Core* this_core = dynamic_cast< Core* >( thisMB );
252if( this_core && create_vert_elem_adjs && !this_core->a_entity_factory()->vert_elem_adjacencies() )
253 this_core->a_entity_factory()->create_vert_elem_adjacencies();
254255returnfind_skin_vertices( meshset, source_entities, get_vertices ? &output_handles : 0,
256 get_vertices ? 0 : &output_handles, output_reverse_handles, create_skin_elements );
257 }
258259ErrorCode Skinner::find_skin_scd( const Range& source_entities,
260bool get_vertices,
261 Range& output_handles,
262bool create_skin_elements )
263 {
264// get the scd interface and check if it's been initialized265 ScdInterface* scdi = NULL;
266 ErrorCode rval = thisMB->query_interface( scdi );
267if( !scdi ) return MB_FAILURE;
268269// ok, there's scd mesh; see if the entities passed in are all in a scd box270// a box needs to be wholly included in entities for this to work271 std::vector< ScdBox* > boxes, myboxes;
272 Range myrange;
273 rval = scdi->find_boxes( boxes );
274if( MB_SUCCESS != rval ) return rval;
275for( std::vector< ScdBox* >::iterator bit = boxes.begin(); bit != boxes.end(); ++bit )
276 {
277Range belems( ( *bit )->start_element(), ( *bit )->start_element() + ( *bit )->num_elements() - 1 );
278if( source_entities.contains( belems ) )
279 {
280 myboxes.push_back( *bit );
281 myrange.merge( belems );
282 }
283 }
284if( myboxes.empty() || myrange.size() != source_entities.size() ) return MB_FAILURE;
285286// ok, we're all structured; get the skin for each box287for( std::vector< ScdBox* >::iterator bit = boxes.begin(); bit != boxes.end(); ++bit )
288 {
289 rval = skin_box( *bit, get_vertices, output_handles, create_skin_elements );
290if( MB_SUCCESS != rval ) return rval;
291 }
292293return MB_SUCCESS;
294 }
295296ErrorCode Skinner::skin_box( ScdBox* box, bool get_vertices, Range& output_handles, bool create_skin_elements )
297 {
298 HomCoord bmin = box->box_min(), bmax = box->box_max();
299300// don't support 1d boxes301if( bmin.j() == bmax.j() && bmin.k() == bmax.k() ) return MB_FAILURE;
302303int dim = ( bmin.k() == bmax.k() ? 1 : 2 );
304305 ErrorCode rval;
306 EntityHandle ent;
307308// i=min309for( int k = bmin.k(); k < bmax.k(); k++ )
310 {
311for( int j = bmin.j(); j < bmax.j(); j++ )
312 {
313 ent = 0;
314 rval = box->get_adj_edge_or_face( dim, bmin.i(), j, k, 0, ent, create_skin_elements );
315if( MB_SUCCESS != rval ) return rval;
316if( ent ) output_handles.insert( ent );
317 }
318 }
319// i=max320for( int k = bmin.k(); k < bmax.k(); k++ )
321 {
322for( int j = bmin.j(); j < bmax.j(); j++ )
323 {
324 ent = 0;
325 rval = box->get_adj_edge_or_face( dim, bmax.i(), j, k, 0, ent, create_skin_elements );
326if( MB_SUCCESS != rval ) return rval;
327if( ent ) output_handles.insert( ent );
328 }
329 }
330// j=min331for( int k = bmin.k(); k < bmax.k(); k++ )
332 {
333for( int i = bmin.i(); i < bmax.i(); i++ )
334 {
335 ent = 0;
336 rval = box->get_adj_edge_or_face( dim, i, bmin.j(), k, 1, ent, create_skin_elements );
337if( MB_SUCCESS != rval ) return rval;
338if( ent ) output_handles.insert( ent );
339 }
340 }
341// j=max342for( int k = bmin.k(); k < bmax.k(); k++ )
343 {
344for( int i = bmin.i(); i < bmax.i(); i++ )
345 {
346 ent = 0;
347 rval = box->get_adj_edge_or_face( dim, i, bmax.j(), k, 1, ent, create_skin_elements );
348if( MB_SUCCESS != rval ) return rval;
349if( ent ) output_handles.insert( ent );
350 }
351 }
352// k=min353for( int j = bmin.j(); j < bmax.j(); j++ )
354 {
355for( int i = bmin.i(); i < bmax.i(); i++ )
356 {
357 ent = 0;
358 rval = box->get_adj_edge_or_face( dim, i, j, bmin.k(), 2, ent, create_skin_elements );
359if( MB_SUCCESS != rval ) return rval;
360if( ent ) output_handles.insert( ent );
361 }
362 }
363// k=max364for( int j = bmin.j(); j < bmax.j(); j++ )
365 {
366for( int i = bmin.i(); i < bmax.i(); i++ )
367 {
368 ent = 0;
369 rval = box->get_adj_edge_or_face( dim, i, j, bmax.k(), 2, ent, create_skin_elements );
370if( MB_SUCCESS != rval ) return rval;
371if( ent ) output_handles.insert( ent );
372 }
373 }
374375if( get_vertices )
376 {
377 Range verts;
378 rval = thisMB->get_adjacencies( output_handles, 0, true, verts, Interface::UNION );
379if( MB_SUCCESS != rval ) return rval;
380 output_handles.merge( verts );
381 }
382383return MB_SUCCESS;
384 }
385386ErrorCode Skinner::find_skin_noadj(const Range &source_entities,
387 Range &forward_target_entities,
388 Range &reverse_target_entities/*,
389 bool create_vert_elem_adjs*/)
390 {
391if( source_entities.empty() ) return MB_FAILURE;
392393// get our working dimensions394 EntityType type = thisMB->type_from_handle( *( source_entities.begin() ) );
395constint source_dim = CN::Dimension( type );
396 mTargetDim = source_dim - 1;
397398// make sure we can handle the working dimensions399if( mTargetDim < 0 || source_dim > 3 ) return MB_FAILURE;
400401initialize();
402403 Range::const_iterator iter, end_iter;
404 end_iter = source_entities.end();
405const EntityHandle* conn;
406 EntityHandle match;
407408 direction direct;
409 ErrorCode result;
410// assume we'll never have more than 32 vertices on a facet (checked411// with assert later)412 EntityHandle sub_conn[32];
413 std::vector< EntityHandle > tmp_conn_vec;
414int num_nodes, num_sub_nodes, num_sides;
415int sub_indices[32]; // Also, assume that no polygon has more than 32 nodes416// we could increase that, but we will not display it right in visit moab h5m , anyway417 EntityType sub_type;
418419// for each source entity420for( iter = source_entities.begin(); iter != end_iter; ++iter )
421 {
422// get the connectivity of this entity423int actual_num_nodes_polygon = 0;
424 result = thisMB->get_connectivity( *iter, conn, num_nodes, false, &tmp_conn_vec );
425if( MB_SUCCESS != result ) return result;
426427 type = thisMB->type_from_handle( *iter );
428 Range::iterator seek_iter;
429430// treat separately polygons (also, polyhedra will need special handling)431if( MBPOLYGON == type )
432 {
433// treat padded polygons, if existing; count backwards, see how many of the last nodes434// are repeated assume connectivity is fine, otherwise we could be in trouble435 actual_num_nodes_polygon = num_nodes;
436while( 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 }
443else// get connectivity of each n-1 dimension entity444 num_sides = CN::NumSubEntities( type, mTargetDim );
445for( int i = 0; i < num_sides; i++ )
446 {
447if( MBPOLYGON == type )
448 {
449 sub_conn[0] = conn[i];
450 sub_conn[1] = conn[i + 1];
451if( i + 1 == actual_num_nodes_polygon ) sub_conn[1] = conn[0];
452 }
453else454 {
455 CN::SubEntityNodeIndices( type, num_nodes, mTargetDim, i, sub_type, num_sub_nodes, sub_indices );
456assert( (size_t)num_sub_nodes <= sizeof( sub_indices ) / sizeof( sub_indices[0] ) );
457for( int j = 0; j < num_sub_nodes; j++ )
458 sub_conn[j] = conn[sub_indices[j]];
459 }
460461// see if we can match this connectivity with462// an existing entity463find_match( sub_type, sub_conn, num_sub_nodes, match, direct );
464465// if there is no match, create a new entity466if( match == 0 )
467 {
468 EntityHandle tmphndl = 0;
469int indices[MAX_SUB_ENTITY_VERTICES];
470 EntityType new_type;
471int num_new_nodes;
472if( MBPOLYGON == type )
473 {
474 new_type = MBEDGE;
475 num_new_nodes = 2;
476 }
477else478 {
479 CN::SubEntityNodeIndices( type, num_nodes, mTargetDim, i, new_type, num_new_nodes, indices );
480for( int j = 0; j < num_new_nodes; j++ )
481 sub_conn[j] = conn[indices[j]];
482 }
483 result = thisMB->create_element( new_type, sub_conn, num_new_nodes, tmphndl );
484assert( MB_SUCCESS == result );
485add_adjacency( tmphndl, sub_conn, CN::VerticesPerEntity( new_type ) );
486 forward_target_entities.insert( tmphndl );
487 }
488// if there is a match, delete the matching entity489// if we can.490else491 {
492if( ( seek_iter = forward_target_entities.find( match ) ) != forward_target_entities.end() )
493 {
494 forward_target_entities.erase( seek_iter );
495remove_adjacency( match );
496if( /*!use_adjs &&*/entity_deletable( match ) )
497 {
498 result = thisMB->delete_entities( &match, 1 );
499assert( MB_SUCCESS == result );
500 }
501 }
502elseif( ( seek_iter = reverse_target_entities.find( match ) ) != reverse_target_entities.end() )
503 {
504 reverse_target_entities.erase( seek_iter );
505remove_adjacency( match );
506if( /*!use_adjs &&*/entity_deletable( match ) )
507 {
508 result = thisMB->delete_entities( &match, 1 );
509assert( MB_SUCCESS == result );
510 }
511 }
512else513 {
514if( direct == FORWARD )
515 {
516 forward_target_entities.insert( match );
517 }
518else519 {
520 reverse_target_entities.insert( match );
521 }
522 }
523 }
524 }
525 }
526527deinitialize();
528529return MB_SUCCESS;
530 }
531532voidSkinner::find_match( EntityType type,
533const EntityHandle* conn,
534constint num_nodes,
535 EntityHandle& match,
536 Skinner::direction& direct )
537 {
538 match = 0;
539540if( type == MBVERTEX )
541 {
542 match = *conn;
543 direct = FORWARD;
544return;
545 }
546547const EntityHandle* iter = std::min_element( conn, conn + num_nodes );
548549 std::vector< EntityHandle >* adj = NULL;
550551 ErrorCode result = thisMB->tag_get_data( mAdjTag, iter, 1, &adj );
552if( result == MB_FAILURE || adj == NULL )
553 {
554return;
555 }
556557 std::vector< EntityHandle >::iterator jter, end_jter;
558 end_jter = adj->end();
559560const EntityHandle* tmp;
561int num_verts;
562563for( jter = adj->begin(); jter != end_jter; ++jter )
564 {
565 EntityType tmp_type;
566 tmp_type = thisMB->type_from_handle( *jter );
567568if( type != tmp_type ) continue;
569570 result = thisMB->get_connectivity( *jter, tmp, num_verts, false );
571assert( MB_SUCCESS == result && num_verts >= CN::VerticesPerEntity( type ) );
572// FIXME: connectivity_match appears to work only for linear elements,573// so ignore higher-order nodes.574if( connectivity_match( conn, tmp, CN::VerticesPerEntity( type ), direct ) )
575 {
576 match = *jter;
577break;
578 }
579 }
580 }
581582boolSkinner::connectivity_match( const EntityHandle* conn1,
583const EntityHandle* conn2,
584constint num_verts,
585 Skinner::direction& direct )
586 {
587const EntityHandle* iter = std::find( conn2, conn2 + num_verts, conn1[0] );
588if( iter == conn2 + num_verts ) returnfalse;
589590bool they_match = true;
591592int i;
593unsignedint j = iter - conn2;
594595// first compare forward596for( i = 1; i < num_verts; ++i )
597 {
598if( conn1[i] != conn2[( j + i ) % num_verts] )
599 {
600 they_match = false;
601break;
602 }
603 }
604605if( they_match == true )
606 {
607// need to check for reversed edges here608 direct = ( num_verts == 2 && j ) ? REVERSE : FORWARD;
609returntrue;
610 }
611612 they_match = true;
613614// then compare reverse615 j += num_verts;
616for( i = 1; i < num_verts; )
617 {
618if( conn1[i] != conn2[( j - i ) % num_verts] )
619 {
620 they_match = false;
621break;
622 }
623 ++i;
624 }
625if( they_match )
626 {
627 direct = REVERSE;
628 }
629return they_match;
630 }
631632ErrorCode Skinner::remove_adjacency( EntityHandle entity )
633 {
634 std::vector< EntityHandle > nodes, *adj = NULL;
635 ErrorCode result = thisMB->get_connectivity( &entity, 1, nodes );MB_CHK_ERR( result );
636 std::vector< EntityHandle >::iterator iter = std::min_element( nodes.begin(), nodes.end() );
637638if( iter == nodes.end() ) return MB_FAILURE;
639640// remove this entity from the node641if( thisMB->tag_get_data( mAdjTag, &( *iter ), 1, &adj ) == MB_SUCCESS && adj != NULL )
642 {
643 iter = std::find( adj->begin(), adj->end(), entity );
644if( iter != adj->end() ) adj->erase( iter );
645 }
646647return result;
648 }
649650boolSkinner::entity_deletable( EntityHandle entity )
651 {
652unsignedchar deletable = 0;
653 ErrorCode result = thisMB->tag_get_data( mDeletableMBTag, &entity, 1, &deletable );
654assert( MB_SUCCESS == result );
655if( MB_SUCCESS == result && deletable == 1 ) returnfalse;
656returntrue;
657 }
658659ErrorCode Skinner::classify_2d_boundary( const Range& boundary,
660const Range& bar_elements,
661 EntityHandle boundary_edges,
662 EntityHandle inferred_edges,
663 EntityHandle non_manifold_edges,
664 EntityHandle other_edges,
665int& number_boundary_nodes )
666 {
667 Range bedges, iedges, nmedges, oedges;
668 ErrorCode result =
669classify_2d_boundary( boundary, bar_elements, bedges, iedges, nmedges, oedges, number_boundary_nodes );MB_CHK_ERR( result );
670671// now set the input meshsets to the output ranges672 result = thisMB->clear_meshset( &boundary_edges, 1 );MB_CHK_ERR( result );
673 result = thisMB->add_entities( boundary_edges, bedges );MB_CHK_ERR( result );
674675 result = thisMB->clear_meshset( &inferred_edges, 1 );MB_CHK_ERR( result );
676 result = thisMB->add_entities( inferred_edges, iedges );MB_CHK_ERR( result );
677678 result = thisMB->clear_meshset( &non_manifold_edges, 1 );MB_CHK_ERR( result );
679 result = thisMB->add_entities( non_manifold_edges, nmedges );MB_CHK_ERR( result );
680681 result = thisMB->clear_meshset( &other_edges, 1 );MB_CHK_ERR( result );
682 result = thisMB->add_entities( other_edges, oedges );MB_CHK_ERR( result );
683684return MB_SUCCESS;
685 }
686687ErrorCode Skinner::classify_2d_boundary( const Range& boundary,
688const Range& bar_elements,
689 Range& boundary_edges,
690 Range& inferred_edges,
691 Range& non_manifold_edges,
692 Range& other_edges,
693int& number_boundary_nodes )
694 {
695696// clear out the edge lists697698 boundary_edges.clear();
699 inferred_edges.clear();
700 non_manifold_edges.clear();
701 other_edges.clear();
702703 number_boundary_nodes = 0;
704705// make sure we have something to work with706if( boundary.empty() )
707 {
708return MB_FAILURE;
709 }
710711// get our working dimensions712 EntityType type = thisMB->type_from_handle( *( boundary.begin() ) );
713constint source_dim = CN::Dimension( type );
714715// make sure we can handle the working dimensions716if( source_dim != 2 )
717 {
718return MB_FAILURE;
719 }
720 mTargetDim = source_dim - 1;
721722// initialize723initialize();
724725// additional initialization for this routine726// define a tag for MBEDGE which counts the occurrences of the edge below727// default should be 0 for existing edges, if any728729 Tag count_tag;
730int default_count = 0;
731 ErrorCode result =
732 thisMB->tag_get_handle( 0, 1, MB_TYPE_INTEGER, count_tag, MB_TAG_DENSE | MB_TAG_CREAT, &default_count );MB_CHK_ERR( result );
733734 Range::const_iterator iter, end_iter;
735 end_iter = boundary.end();
736737 std::vector< EntityHandle > conn;
738 EntityHandle sub_conn[2];
739 EntityHandle match;
740741 Range edge_list;
742 Range boundary_nodes;
743 Skinner::direction direct;
744745 EntityType sub_type;
746int num_edge, num_sub_ent_vert;
747constshort* edge_verts;
748749// now, process each entity in the boundary750751for( iter = boundary.begin(); iter != end_iter; ++iter )
752 {
753// get the connectivity of this entity754 conn.clear();
755 result = thisMB->get_connectivity( &( *iter ), 1, conn, false );
756assert( MB_SUCCESS == result );
757758// add node handles to boundary_node range759 std::copy( conn.begin(), conn.begin() + CN::VerticesPerEntity( type ), range_inserter( boundary_nodes ) );
760761 type = thisMB->type_from_handle( *iter );
762763// get connectivity of each n-1 dimension entity (edge in this case)764conststructCN::ConnMap* conn_map = &( CN::mConnectivityMap[type][0] );
765 num_edge = CN::NumSubEntities( type, 1 );
766for( int i = 0; i < num_edge; i++ )
767 {
768 edge_verts = CN::SubEntityVertexIndices( type, 1, i, sub_type, num_sub_ent_vert );
769assert( sub_type == MBEDGE && num_sub_ent_vert == 2 );
770 sub_conn[0] = conn[edge_verts[0]];
771 sub_conn[1] = conn[edge_verts[1]];
772int num_sub_nodes = conn_map->num_corners_per_sub_element[i];
773774// see if we can match this connectivity with775// an existing entity776find_match( MBEDGE, sub_conn, num_sub_nodes, match, direct );
777778// if there is no match, create a new entity779if( match == 0 )
780 {
781 EntityHandle tmphndl = 0;
782int indices[MAX_SUB_ENTITY_VERTICES];
783 EntityType new_type;
784int num_new_nodes;
785 CN::SubEntityNodeIndices( type, conn.size(), 1, i, new_type, num_new_nodes, indices );
786for( int j = 0; j < num_new_nodes; j++ )
787 sub_conn[j] = conn[indices[j]];
788789 result = thisMB->create_element( new_type, sub_conn, num_new_nodes, tmphndl );
790assert( MB_SUCCESS == result );
791add_adjacency( tmphndl, sub_conn, num_sub_nodes );
792// target_entities.insert(tmphndl);793 edge_list.insert( tmphndl );
794int count;
795 result = thisMB->tag_get_data( count_tag, &tmphndl, 1, &count );
796assert( MB_SUCCESS == result );
797 count++;
798 result = thisMB->tag_set_data( count_tag, &tmphndl, 1, &count );
799assert( MB_SUCCESS == result );
800 }
801else802 {
803// We found a match, we must increment the count on the match804int count;
805 result = thisMB->tag_get_data( count_tag, &match, 1, &count );
806assert( MB_SUCCESS == result );
807 count++;
808 result = thisMB->tag_set_data( count_tag, &match, 1, &count );
809assert( MB_SUCCESS == result );
810811// if the entity is not deletable, it was pre-existing in812// the database. We therefore may need to add it to the813// edge_list. Since it will not hurt the range, we add814// whether it was added before or not815if( !entity_deletable( match ) )
816 {
817 edge_list.insert( match );
818 }
819 }
820 }
821 }
822823// Any bar elements in the model should be classified separately824// If the element is in the skin edge_list, then it should be put in825// the non-manifold edge list. Edges not in the edge_list are stand-alone826// bars, and we make them simply boundary elements827828if( !bar_elements.empty() )
829 {
830 Range::iterator bar_iter;
831for( iter = bar_elements.begin(); iter != bar_elements.end(); ++iter )
832 {
833 EntityHandle handle = *iter;
834 bar_iter = edge_list.find( handle );
835if( bar_iter != edge_list.end() )
836 {
837// it is in the list, erase it and put in non-manifold list838 edge_list.erase( bar_iter );
839 non_manifold_edges.insert( handle );
840 }
841else842 {
843// not in the edge list, make it a boundary edge844 boundary_edges.insert( handle );
845 }
846 }
847 }
848849// now all edges should be classified. Go through the edge_list,850// and put all in the appropriate lists851852 Range::iterator edge_iter, edge_end_iter;
853 edge_end_iter = edge_list.end();
854int count;
855for( edge_iter = edge_list.begin(); edge_iter != edge_end_iter; ++edge_iter )
856 {
857// check the count_tag858 result = thisMB->tag_get_data( count_tag, &( *edge_iter ), 1, &count );
859assert( MB_SUCCESS == result );
860if( count == 1 )
861 {
862 boundary_edges.insert( *edge_iter );
863 }
864elseif( count == 2 )
865 {
866 other_edges.insert( *edge_iter );
867 }
868else869 {
870 non_manifold_edges.insert( *edge_iter );
871 }
872 }
873874// find the inferred edges from the other_edge_list875876double min_angle_degrees = 20.0;
877find_inferred_edges( const_cast< Range& >( boundary ), other_edges, inferred_edges, min_angle_degrees );
878879// we now want to remove the inferred_edges from the other_edges880881 Range temp_range;
882883 std::set_difference( other_edges.begin(), other_edges.end(), inferred_edges.begin(), inferred_edges.end(),
884range_inserter( temp_range ), std::less< EntityHandle >() );
885886 other_edges = temp_range;
887888// get rid of count tag and deinitialize889890 result = thisMB->tag_delete( count_tag );
891assert( MB_SUCCESS == result );
892deinitialize();
893894// set the node count895 number_boundary_nodes = boundary_nodes.size();
896897return MB_SUCCESS;
898 }
899900voidSkinner::find_inferred_edges( Range& skin_boundary,
901 Range& candidate_edges,
902 Range& inferred_edges,
903double reference_angle_degrees )
904 {
905906// mark all the entities in the skin boundary907 Tag mark_tag;
908 ErrorCode result = thisMB->tag_get_handle( 0, 1, MB_TYPE_BIT, mark_tag, MB_TAG_CREAT );
909assert( MB_SUCCESS == result );
910unsignedchar bit = true;
911 result = thisMB->tag_clear_data( mark_tag, skin_boundary, &bit );
912assert( MB_SUCCESS == result );
913914// find the cosine of the reference angle915916double reference_cosine = cos( reference_angle_degrees * SKINNER_PI / 180.0 );
917918// check all candidate edges for an angle greater than the minimum919920 Range::iterator iter, end_iter = candidate_edges.end();
921 std::vector< EntityHandle > adjacencies;
922 std::vector< EntityHandle >::iterator adj_iter;
923 EntityHandle face[2];
924925for( iter = candidate_edges.begin(); iter != end_iter; ++iter )
926 {
927928// get the 2D elements connected to this edge929 adjacencies.clear();
930 result = thisMB->get_adjacencies( &( *iter ), 1, 2, false, adjacencies );
931if( MB_SUCCESS != result ) continue;
932933// there should be exactly two, that is why the edge is classified as nonBoundary934// and manifold935936int faces_found = 0;
937for( adj_iter = adjacencies.begin(); adj_iter != adjacencies.end() && faces_found < 2; ++adj_iter )
938 {
939// we need to find two of these which are in the skin940unsignedchar is_marked = 0;
941 result = thisMB->tag_get_data( mark_tag, &( *adj_iter ), 1, &is_marked );
942assert( MB_SUCCESS == result );
943if( is_marked )
944 {
945 face[faces_found] = *adj_iter;
946 faces_found++;
947 }
948 }
949950// assert(faces_found == 2 || faces_found == 0);951if( 2 != faces_found ) continue;
952953// see if the two entities have a sufficient angle954955if( has_larger_angle( face[0], face[1], reference_cosine ) )
956 {
957 inferred_edges.insert( *iter );
958 }
959 }
960961 result = thisMB->tag_delete( mark_tag );
962assert( MB_SUCCESS == result );
963 }
964965boolSkinner::has_larger_angle( EntityHandle& entity1, EntityHandle& entity2, double reference_angle_cosine )
966 {
967// compare normals to get angle. We assume that the surface quads968// which we test here will be approximately planar969970double norm[2][3];
971 Util::normal( thisMB, entity1, norm[0][0], norm[0][1], norm[0][2] );
972 Util::normal( thisMB, entity2, norm[1][0], norm[1][1], norm[1][2] );
973974double cosine = norm[0][0] * norm[1][0] + norm[0][1] * norm[1][1] + norm[0][2] * norm[1][2];
975976if( cosine < reference_angle_cosine )
977 {
978returntrue;
979 }
980981returnfalse;
982 }
983984// get skin entities of prescribed dimension985ErrorCode Skinner::find_skin( const EntityHandle this_set,
986const Range& entities,
987int dim,
988 Range& skin_entities,
989bool create_vert_elem_adjs,
990bool create_skin_elements )
991 {
992 Range tmp_skin;
993 ErrorCode result =
994find_skin( this_set, entities, ( dim == 0 ), tmp_skin, 0, create_vert_elem_adjs, create_skin_elements );
995if( MB_SUCCESS != result || tmp_skin.empty() ) return result;
996997if( tmp_skin.all_of_dimension( dim ) )
998 {
999if( skin_entities.empty() )
1000 skin_entities.swap( tmp_skin );
1001else1002 skin_entities.merge( tmp_skin );
1003 }
1004else1005 {
1006 result = thisMB->get_adjacencies( tmp_skin, dim, create_skin_elements, skin_entities, Interface::UNION );MB_CHK_ERR( result );
1007if( this_set ) result = thisMB->add_entities( this_set, skin_entities );
1008 }
10091010return result;
1011 }
10121013ErrorCode Skinner::find_skin_vertices( const EntityHandle this_set,
1014const Range& entities,
1015 Range* skin_verts,
1016 Range* skin_elems,
1017 Range* skin_rev_elems,
1018bool create_skin_elems,
1019bool corners_only )
1020 {
1021 ErrorCode rval;
1022if( entities.empty() ) return MB_SUCCESS;
10231024constint dim = CN::Dimension( TYPE_FROM_HANDLE( entities.front() ) );
1025if( dim < 1 || dim > 3 || !entities.all_of_dimension( dim ) ) return MB_TYPE_OUT_OF_RANGE;
10261027// are we skinning all entities1028size_t count = entities.size();
1029int num_total;
1030 rval = thisMB->get_number_entities_by_dimension( this_set, dim, num_total );
1031if( MB_SUCCESS != rval ) return rval;
1032bool all = ( count == (size_t)num_total );
10331034// Create a bit tag for fast intersection with input entities range.1035// If we're skinning all the entities in the mesh, we really don't1036// need the tag. To save memory, just create it with a default value1037// of one and don't set it. That way MOAB will return 1 for all1038// entities.1039 Tag tag;
1040char bit = all ? 1 : 0;
1041 rval = thisMB->tag_get_handle( NULL, 1, MB_TYPE_BIT, tag, MB_TAG_CREAT, &bit );
1042if( MB_SUCCESS != rval ) return rval;
10431044// tag all entities in input range1045if( !all )
1046 {
1047std::vector< unsignedchar > vect( count, 1 );
1048 rval = thisMB->tag_set_data( tag, entities, &vect[0] );
1049if( MB_SUCCESS != rval )
1050 {
1051 thisMB->tag_delete( tag );
1052return rval;
1053 }
1054 }
10551056switch( dim )
1057 {
1058case1:
1059if( skin_verts )
1060 rval = find_skin_vertices_1D( tag, entities, *skin_verts );
1061elseif( skin_elems )
1062 rval = find_skin_vertices_1D( tag, entities, *skin_elems );
1063else1064 rval = MB_SUCCESS;
1065break;
1066case2:
1067 rval = find_skin_vertices_2D( this_set, tag, entities, skin_verts, skin_elems, skin_rev_elems,
1068 create_skin_elems, corners_only );
1069break;
1070case3:
1071 rval = find_skin_vertices_3D( this_set, tag, entities, skin_verts, skin_elems, skin_rev_elems,
1072 create_skin_elems, corners_only );
1073break;
1074default:
1075 rval = MB_TYPE_OUT_OF_RANGE;
1076break;
1077 }
10781079 thisMB->tag_delete( tag );
1080return rval;
1081 }
10821083ErrorCode Skinner::find_skin_vertices_1D( Tag tag, const Range& edges, Range& skin_verts )
1084 {
1085// This rather simple algorithm is provided for completeness1086// (not sure how often one really wants the 'skin' of a chain1087// or tangle of edges.)1088//1089// A vertex is on the skin of the edges if it is contained in exactly1090// one of the edges *in the input range*.1091//1092// This function expects the caller to have tagged all edges in the1093// input range with a value of one for the passed bit tag, and all1094// other edges with a value of zero. This allows us to do a faster1095// intersection with the input range and the edges adjacent to a vertex.10961097 ErrorCode rval;
1098 Range::iterator hint = skin_verts.begin();
10991100// All input entities must be edges.1101if( !edges.all_of_dimension( 1 ) ) return MB_TYPE_OUT_OF_RANGE;
11021103// get all the vertices1104 Range verts;
1105 rval = thisMB->get_connectivity( edges, verts, true );
1106if( MB_SUCCESS != rval ) return rval;
11071108// Test how many edges each input vertex is adjacent to.1109 std::vector< char > tag_vals;
1110 std::vector< EntityHandle > adj;
1111int n;
1112for( Range::const_iterator it = verts.begin(); it != verts.end(); ++it )
1113 {
1114// get edges adjacent to vertex1115 adj.clear();
1116 rval = thisMB->get_adjacencies( &*it, 1, 1, false, adj );
1117if( MB_SUCCESS != rval ) return rval;
1118if( adj.empty() ) continue;
11191120// Intersect adjacent edges with the input list of edges1121 tag_vals.resize( adj.size() );
1122 rval = thisMB->tag_get_data( tag, &adj[0], adj.size(), &tag_vals[0] );
1123if( MB_SUCCESS != rval ) return rval;
1124#ifdef MOAB_OLD_STD_COUNT1125 n = 0;
1126 std::count( tag_vals.begin(), tag_vals.end(), '\001' );
1127#else1128 n = std::count( tag_vals.begin(), tag_vals.end(), '\001' );
1129#endif1130// If adjacent to only one input edge, then vertex is on skin1131if( n == 1 )
1132 {
1133 hint = skin_verts.insert( hint, *it );
1134 }
1135 }
11361137return MB_SUCCESS;
1138 }
11391140// A Container for storing a list of element sides adjacent1141// to a vertex. The template parameter is the number of1142// corners for the side.1143template < unsigned CORNERS >
1144classAdjSides1145 {
1146public:
1147/**
1148 * This struct is used to for a reduced representation of element
1149 * "sides" adjacent to a give vertex. As such, it
1150 * a) does not store the initial vertex that all sides are adjacent to
1151 * b) orders the remaining vertices in a specific way for fast comparison.
1152 *
1153 * For edge elements, only the opposite vertex is stored.
1154 * For triangle elements, only the other two vertices are stored,
1155 * and they are stored with the smaller of those two handles first.
1156 * For quad elements, only the other three vertices are stored.
1157 * They are stored such that the vertex opposite the implicit (not
1158 * stored) vertex is always in slot 1. The other two vertices
1159 * (in slots 0 and 2) are ordered such that the handle of the one in
1160 * slot 0 is smaller than the handle in slot 2.
1161 *
1162 * For each side, the adj_elem field is used to store the element that
1163 * it is a side of as long as the element is considered to be on the skin.
1164 * The adj_elem field is cleared (set to zero) to indicate that this
1165 * side is no longer considered to be on the skin (and is the side of
1166 * more than one element.)
1167 */1168structSide1169 {
1170 EntityHandle handles[CORNERS - 1]; //!< side vertices, except for implicit one1171 EntityHandle adj_elem; //!< element that this is a side of, or zero1172boolskin()const
1173 {
1174return0 != adj_elem;
1175 }
11761177/** construct from connectivity of side
1178 *\param array The connectivity of the element side.
1179 *\param idx The index of the implicit vertex (contained
1180 * in all sides in the list.)
1181 *\param adj The element that this is a side of.
1182 */1183Side( const EntityHandle* array, int idx, EntityHandle adj, unsignedshort ) : adj_elem( adj )
1184 {
1185switch( CORNERS )
1186 {
1187case3:
1188 handles[1] = array[( idx + 2 ) % CORNERS];
1189// fall through1190case2:
1191if( 3 == CORNERS ) handles[1] = array[( idx + 2 ) % CORNERS];
1192if( 2 <= CORNERS ) handles[0] = array[( idx + 1 ) % CORNERS];
1193break;
1194default:
1195assert( false );
1196break;
1197 }
1198if( CORNERS == 3 && handles[1] > handles[0] ) std::swap( handles[0], handles[1] );
1199 }
12001201/** construct from connectivity of parent element
1202 *\param array The connectivity of the parent element
1203 *\param idx The index of the implicit vertex (contained
1204 * in all sides in the list.) This is an index
1205 * into 'indices', not 'array'.
1206 *\param adj The element that this is a side of.
1207 *\param indices The indices into 'array' at which the vertices
1208 * representing the side occur.
1209 */1210Side( const EntityHandle* array, int idx, EntityHandle adj, unsignedshort, constshort* indices )
1211 : adj_elem( adj )
1212 {
1213switch( CORNERS )
1214 {
1215case3:
1216 handles[1] = array[indices[( idx + 2 ) % CORNERS]];
1217// fall through1218case2:
1219if( 3 == CORNERS ) handles[1] = array[indices[( idx + 2 ) % CORNERS]];
1220if( 2 <= CORNERS ) handles[0] = array[indices[( idx + 1 ) % CORNERS]];
1221break;
1222default:
1223assert( false );
1224break;
1225 }
1226if( CORNERS == 3 && handles[1] > handles[0] ) std::swap( handles[0], handles[1] );
1227 }
12281229// Compare two side instances. Relies in the ordering of1230// vertex handles as described above.1231booloperator==( const Side& other ) const1232 {
1233switch( CORNERS )
1234 {
1235case3:
1236return handles[0] == other.handles[0] && handles[1] == other.handles[1];
1237case2:
1238return handles[0] == other.handles[0];
1239default:
1240assert( false );
1241returnfalse;
1242 }
1243 }
1244 };
12451246private:
1247 std::vector< Side > data; //!< List of sides1248size_t skin_count; //!< Cached count of sides that are skin12491250public:
1251typedeftypename std::vector< Side >::iterator iterator;
1252typedeftypename std::vector< Side >::const_iterator const_iterator;
1253const_iterator begin()const
1254 {
1255return data.begin();
1256 }
1257const_iterator end()const
1258 {
1259return data.end();
1260 }
12611262voidclear()
1263 {
1264 data.clear();
1265 skin_count = 0;
1266 }
1267boolempty()const
1268 {
1269return data.empty();
1270 }
12711272AdjSides() : skin_count( 0 ) {}
12731274size_tnum_skin()const
1275 {
1276return skin_count;
1277 }
12781279/** \brief insert side, specifying side connectivity
1280 *
1281 * Either insert a new side, or if the side is already in the
1282 * list, mark it as not on the skin.
1283 *
1284 *\param handles The connectivity of the element side.
1285 *\param skip_idx The index of the implicit vertex (contained
1286 * in all sides in the list.)
1287 *\param adj_elem The element that this is a side of.
1288 *\param elem_side Which side of adj_elem are we storing
1289 * (CN side number.)
1290 */1291voidinsert( const EntityHandle* handles, int skip_idx, EntityHandle adj_elem, unsignedshort elem_side )
1292 {
1293Side side( handles, skip_idx, adj_elem, elem_side );
1294 iterator p = std::find( data.begin(), data.end(), side );
1295if( p == data.end() )
1296 {
1297 data.push_back( side );
1298 ++skin_count; // not in list yet, so skin side (so far)1299 }
1300elseif( p->adj_elem )
1301 {
1302 p->adj_elem = 0; // mark as not on skin1303 --skin_count; // decrement cached count of skin elements1304 }
1305 }
13061307/** \brief insert side, specifying list of indices into parent element
1308 * connectivity.
1309 *
1310 * Either insert a new side, or if the side is already in the
1311 * list, mark it as not on the skin.
1312 *
1313 *\param handles The connectivity of the parent element
1314 *\param skip_idx The index of the implicit vertex (contained
1315 * in all sides in the list.) This is an index
1316 * into 'indices', not 'handles'.
1317 *\param adj_elem The element that this is a side of (parent handle).
1318 *\param indices The indices into 'handles' at which the vertices
1319 * representing the side occur.
1320 *\param elem_side Which side of adj_elem are we storing
1321 * (CN side number.)
1322 */1323voidinsert( const EntityHandle* handles,
1324int skip_idx,
1325 EntityHandle adj_elem,
1326unsignedshort elem_side,
1327constshort* indices )
1328 {
1329Side side( handles, skip_idx, adj_elem, elem_side, indices );
1330 iterator p = std::find( data.begin(), data.end(), side );
1331if( p == data.end() )
1332 {
1333 data.push_back( side );
1334 ++skin_count; // not in list yet, so skin side (so far)1335 }
1336elseif( p->adj_elem )
1337 {
1338 p->adj_elem = 0; // mark as not on skin1339 --skin_count; // decrement cached count of skin elements1340 }
1341 }
13421343/**\brief Search list for a given side, and if found, mark as not skin.
1344 *
1345 *\param other Connectivity of side
1346 *\param skip_index Index in 'other' at which implicit vertex occurs.
1347 *\param elem_out If return value is true, the element that the side is a
1348 * side of. If return value is false, not set.
1349 *\return true if found and marked not-skin, false if not found.
1350 *
1351 * Given the connectivity of some existing element, check if it occurs
1352 * in the list. If it does, clear the "is skin" state of the side so
1353 * that we know that we don't need to later create the side element.
1354 */1355boolfind_and_unmark( const EntityHandle* other, int skip_index, EntityHandle& elem_out )
1356 {
1357Side s( other, skip_index, 0, 0 );
1358 iterator p = std::find( data.begin(), data.end(), s );
1359if( p == data.end() || !p->adj_elem )
1360returnfalse;
1361else1362 {
1363 elem_out = p->adj_elem;
1364 p->adj_elem = 0; // clear "is skin" state for side1365 --skin_count; // decrement cached count of skin sides1366returntrue;
1367 }
1368 }
1369 };
13701371/** construct from connectivity of side
1372 *\param array The connectivity of the element side.
1373 *\param idx The index of the implicit vertex (contained
1374 * in all sides in the list.)
1375 *\param adj The element that this is a side of.
1376 */1377template <>
1378 AdjSides< 4 >::Side::Side( const EntityHandle* array, int idx, EntityHandle adj, unsignedshort ) : adj_elem( adj )
1379 {
1380constunsignedint CORNERS = 4;
1381 handles[2] = array[( idx + 3 ) % CORNERS];
1382 handles[1] = array[( idx + 2 ) % CORNERS];
1383 handles[0] = array[( idx + 1 ) % CORNERS];
1384if( handles[2] > handles[0] ) std::swap( handles[0], handles[2] );
1385 }
13861387/** construct from connectivity of parent element
1388 *\param array The connectivity of the parent element
1389 *\param idx The index of the implicit vertex (contained
1390 * in all sides in the list.) This is an index
1391 * into 'indices', not 'array'.
1392 *\param adj The element that this is a side of.
1393 *\param indices The indices into 'array' at which the vertices
1394 * representing the side occur.
1395 */1396template <>
1397 AdjSides< 4 >::Side::Side( const EntityHandle* array, int idx, EntityHandle adj, unsignedshort, constshort* indices )
1398 : adj_elem( adj )
1399 {
1400constunsignedint CORNERS = 4;
1401 handles[2] = array[indices[( idx + 3 ) % CORNERS]];
1402 handles[1] = array[indices[( idx + 2 ) % CORNERS]];
1403 handles[0] = array[indices[( idx + 1 ) % CORNERS]];
1404if( handles[2] > handles[0] ) std::swap( handles[0], handles[2] );
1405 }
14061407// Compare two side instances. Relies in the ordering of1408// vertex handles as described above.1409template <>
1410bool AdjSides< 4 >::Side::operator==( const Side& other ) const1411 {
1412return handles[0] == other.handles[0] && handles[1] == other.handles[1] && handles[2] == other.handles[2];
1413 }
14141415// Utility function used by find_skin_vertices_2D and1416// find_skin_vertices_3D to create elements representing1417// the skin side of a higher-dimension element if one1418// does not already exist.1419//1420// Some arguments may seem redundant, but they are used1421// to create the correct order of element when the input1422// element contains higher-order nodes.1423//1424// This function always creates elements that have a "forward"1425// orientation with respect to the parent element (have1426// nodes ordered the same as CN returns for the "side").1427//1428// elem - The higher-dimension element for which to create1429// a lower-dim element representing the side.1430// side_type - The EntityType of the desired side.1431// side_conn - The connectivity of the new side.1432ErrorCode Skinner::create_side( const EntityHandle this_set,
1433 EntityHandle elem,
1434 EntityType side_type,
1435const EntityHandle* side_conn,
1436 EntityHandle& side_elem )
1437 {
1438constint max_side = 9;
1439const EntityHandle* conn;
1440int len, side_len, side, sense, offset, indices[max_side];
1441 ErrorCode rval;
1442 EntityType type = TYPE_FROM_HANDLE( elem ), tmp_type;
1443constint ncorner = CN::VerticesPerEntity( side_type );
1444constint d = CN::Dimension( side_type );
1445 std::vector< EntityHandle > storage;
14461447// Get the connectivity of the parent element1448 rval = thisMB->get_connectivity( elem, conn, len, false, &storage );
1449if( MB_SUCCESS != rval ) return rval;
14501451// treat separately MBPOLYGON; we want to create the edge in the1452// forward sense always ; so figure out the sense first, then get out1453if( MBPOLYGON == type && 1 == d && MBEDGE == side_type )
1454 {
1455// first find the first vertex in the conn list1456int i = 0;
1457for( i = 0; i < len; i++ )
1458 {
1459if( conn[i] == side_conn[0] ) break;
1460 }
1461if( len == i ) return MB_FAILURE; // not found, big error1462// now, what if the polygon is padded?1463// the previous index is fine always. but the next one could be trouble :(1464int prevIndex = ( i + len - 1 ) % len;
1465int nextIndex = ( i + 1 ) % len;
1466// if the next index actually point to the same node, as current, it means it is padded1467if( conn[nextIndex] == conn[i] )
1468 {
1469// it really means we are at the end of proper nodes, the last nodes are repeated, so it1470// should be the first node1471 nextIndex = 0; // this is the first node!1472 }
1473 EntityHandle conn2[2] = { side_conn[0], side_conn[1] };
1474if( conn[prevIndex] == side_conn[1] )
1475 {
1476// reverse, so the edge will be forward1477 conn2[0] = side_conn[1];
1478 conn2[1] = side_conn[0];
1479 }
1480elseif( conn[nextIndex] != side_conn[1] )
1481return MB_FAILURE; // it is not adjacent to the polygon14821483 rval = thisMB->create_element( MBEDGE, conn2, 2, side_elem );MB_CHK_ERR( rval );
1484if( this_set )
1485 {
1486 rval = thisMB->add_entities( this_set, &side_elem, 1 );MB_CHK_ERR( rval );
1487 }
1488return MB_SUCCESS;
1489 }
1490// Find which side we are creating and get indices of all nodes1491// (including higher-order, if any.)1492 CN::SideNumber( type, conn, side_conn, ncorner, d, side, sense, offset );
1493 CN::SubEntityNodeIndices( type, len, d, side, tmp_type, side_len, indices );
1494assert( side_len <= max_side );
1495assert( side_type == tmp_type );
14961497// NOTE: re-create conn array even when no higher-order nodes1498// because we want it to always be forward with respect1499// to the side ordering.1500 EntityHandle side_conn_full[max_side];
1501for( int i = 0; i < side_len; ++i )
1502 side_conn_full[i] = conn[indices[i]];
15031504 rval = thisMB->create_element( side_type, side_conn_full, side_len, side_elem );MB_CHK_ERR( rval );
1505if( this_set )
1506 {
1507 rval = thisMB->add_entities( this_set, &side_elem, 1 );MB_CHK_ERR( rval );
1508 }
1509return MB_SUCCESS;
1510 ;
1511 }
15121513// Test if an edge is reversed with respect CN's ordering1514// for the "side" of a face.1515boolSkinner::edge_reversed( EntityHandle face, const EntityHandle* edge_ends )
1516 {
1517const EntityHandle* conn;
1518int len, idx;
1519 ErrorCode rval = thisMB->get_connectivity( face, conn, len, true );
1520if( MB_SUCCESS != rval )
1521 {
1522assert( false );
1523returnfalse;
1524 }
1525 idx = std::find( conn, conn + len, edge_ends[0] ) - conn;
1526if( idx == len )
1527 {
1528assert( false );
1529returnfalse;
1530 }
1531return ( edge_ends[1] == conn[( idx + len - 1 ) % len] );
1532 }
15331534// Test if a 2D element representing the side or face of a1535// volume element is reversed with respect to the CN node1536// ordering for the corresponding region element side.1537boolSkinner::face_reversed( EntityHandle region, const EntityHandle* face_corners, EntityType face_type )
1538 {
1539const EntityHandle* conn;
1540int len, side, sense, offset;
1541 ErrorCode rval = thisMB->get_connectivity( region, conn, len, true );
1542if( MB_SUCCESS != rval )
1543 {
1544assert( false );
1545returnfalse;
1546 }
1547short r = CN::SideNumber( TYPE_FROM_HANDLE( region ), conn, face_corners, CN::VerticesPerEntity( face_type ),
1548 CN::Dimension( face_type ), side, sense, offset );
1549assert( 0 == r );
1550return ( !r && sense == -1 );
1551 }
15521553ErrorCode Skinner::find_skin_vertices_2D( const EntityHandle this_set,
1554 Tag tag,
1555const Range& faces,
1556 Range* skin_verts,
1557 Range* skin_edges,
1558 Range* reversed_edges,
1559bool create_edges,
1560bool corners_only )
1561 {
1562// This function iterates over all the vertices contained in the1563// input face list. For each such vertex, it then iterates over1564// all of the sides of the face elements adjacent to the vertex.1565// If an adjacent side is the side of only one of the input1566// faces, then that side is on the skin.1567//1568// This algorithm will visit each skin vertex exactly once. It1569// will visit each skin side once for each vertex in the side.1570//1571// This function expects the caller to have created the passed bit1572// tag and set it to one only for the faces in the passed range. This1573// tag is used to do a fast intersection of the faces adjacent to a1574// vertex with the faces in the input range (discard any for which the1575// tag is not set to one.)15761577 ErrorCode rval;
1578 std::vector< EntityHandle >::iterator i, j;
1579 Range::iterator hint;
1580if( skin_verts ) hint = skin_verts->begin();
1581 std::vector< EntityHandle > storage;
1582const EntityHandle* conn;
1583int len;
1584bool find_edges = skin_edges || create_edges;
1585bool printed_nonconformal_ho_warning = false;
1586 EntityHandle face;
15871588if( !faces.all_of_dimension( 2 ) ) return MB_TYPE_OUT_OF_RANGE;
15891590// get all the vertices1591 Range verts;
1592 rval = thisMB->get_connectivity( faces, verts, true );
1593if( MB_SUCCESS != rval ) return rval;
15941595 std::vector< char > tag_vals;
1596 std::vector< EntityHandle > adj;
1597 AdjSides< 2 > adj_edges;
1598for( Range::const_iterator it = verts.begin(); it != verts.end(); ++it )
1599 {
1600bool higher_order = false;
16011602// get all adjacent faces1603 adj.clear();
1604 rval = thisMB->get_adjacencies( &*it, 1, 2, false, adj );
1605if( MB_SUCCESS != rval ) return rval;
1606if( adj.empty() ) continue;
16071608// remove those not in the input list (intersect with input list)1609 i = j = adj.begin();
1610 tag_vals.resize( adj.size() );
1611 rval = thisMB->tag_get_data( tag, &adj[0], adj.size(), &tag_vals[0] );
1612if( MB_SUCCESS != rval ) return rval;
1613// remove non-tagged entries1614 i = j = adj.begin();
1615for( ; i != adj.end(); ++i )
1616if( tag_vals[i - adj.begin()] ) *( j++ ) = *i;
1617 adj.erase( j, adj.end() );
16181619// For each adjacent face, check the edges adjacent to the current vertex1620 adj_edges.clear(); // other vertex for adjacent edges1621for( i = adj.begin(); i != adj.end(); ++i )
1622 {
1623 rval = thisMB->get_connectivity( *i, conn, len, false, &storage );
1624if( MB_SUCCESS != rval ) return rval;
16251626// For a single face element adjacent to this vertex, there1627// will be exactly two sides (edges) adjacent to the vertex.1628// Find the other vertex for each of the two edges.16291630 EntityHandle prev, next; // vertices of two adjacent edge-sides1631constint idx = std::find( conn, conn + len, *it ) - conn;
1632assert( idx != len );
16331634if( TYPE_FROM_HANDLE( *i ) == MBTRI && len > 3 )
1635 {
1636 len = 3;
1637 higher_order = true;
1638if( idx > 2 )
1639 { // skip higher-order nodes for now1640if( !printed_nonconformal_ho_warning )
1641 {
1642 printed_nonconformal_ho_warning = true;
1643 std::cerr << "Non-conformal higher-order mesh detected in skinner: "1644 << "vertex " << ID_FROM_HANDLE( *it ) << " is a corner in "1645 << "some elements and a higher-order node in others" << std::endl;
1646 }
1647continue;
1648 }
1649 }
1650elseif( TYPE_FROM_HANDLE( *i ) == MBQUAD && len > 4 )
1651 {
1652 len = 4;
1653 higher_order = true;
1654if( idx > 3 )
1655 { // skip higher-order nodes for now1656if( !printed_nonconformal_ho_warning )
1657 {
1658 printed_nonconformal_ho_warning = true;
1659 std::cerr << "Non-conformal higher-order mesh detected in skinner: "1660 << "vertex " << ID_FROM_HANDLE( *it ) << " is a corner in "1661 << "some elements and a higher-order node in others" << std::endl;
1662 }
1663continue;
1664 }
1665 }
16661667// so it must be a MBPOLYGON1668constint prev_idx = ( idx + len - 1 ) % len; // this should be fine, always, even for padded case1669 prev = conn[prev_idx];
1670 next = conn[( idx + 1 ) % len];
1671if( next == conn[idx] ) // it must be the padded case, so roll to the beginning1672 next = conn[0];
16731674// Insert sides (edges) in our list of candidate skin sides1675 adj_edges.insert( &prev, 1, *i, prev_idx );
1676 adj_edges.insert( &next, 1, *i, idx );
1677 }
16781679// If vertex is not on skin, advance to next vertex.1680// adj_edges handled checking for duplicates on insertion.1681// If every candidate skin edge occurred more than once (was1682// not in fact on the skin), then we're done with this vertex.1683if( 0 == adj_edges.num_skin() ) continue;
16841685// If user requested Range of *vertices* on the skin...1686if( skin_verts )
1687 {
1688// Put skin vertex in output list1689 hint = skin_verts->insert( hint, *it );
16901691// Add mid edge nodes to vertex list1692if( !corners_only && higher_order )
1693 {
1694for( AdjSides< 2 >::const_iterator p = adj_edges.begin(); p != adj_edges.end(); ++p )
1695 {
1696if( p->skin() )
1697 {
1698 face = p->adj_elem;
1699 EntityType type = TYPE_FROM_HANDLE( face );
17001701 rval = thisMB->get_connectivity( face, conn, len, false );
1702if( MB_SUCCESS != rval ) return rval;
1703if( !CN::HasMidEdgeNodes( type, len ) ) continue;
17041705 EntityHandle ec[2] = { *it, p->handles[0] };
1706int side, sense, offset;
1707 CN::SideNumber( type, conn, ec, 2, 1, side, sense, offset );
1708 offset = CN::HONodeIndex( type, len, 1, side );
1709assert( offset >= 0 && offset < len );
1710 skin_verts->insert( conn[offset] );
1711 }
1712 }
1713 }
1714 }
17151716// If user requested Range of *edges* on the skin...1717if( find_edges )
1718 {
1719// Search list of existing adjacent edges for any that are on the skin1720 adj.clear();
1721 rval = thisMB->get_adjacencies( &*it, 1, 1, false, adj );
1722if( MB_SUCCESS != rval ) return rval;
1723for( i = adj.begin(); i != adj.end(); ++i )
1724 {
1725 rval = thisMB->get_connectivity( *i, conn, len, true );
1726if( MB_SUCCESS != rval ) return rval;
17271728// bool equality expression within find_and_unmark call1729// will be evaluate to the index of *it in the conn array.1730//1731// Note that the order of the terms in the if statement is important.1732// We want to unmark any existing skin edges even if we aren't1733// returning them. Otherwise we'll end up creating duplicates1734// if create_edges is true and skin_edges is not.1735if( adj_edges.find_and_unmark( conn, ( conn[1] == *it ), face ) && skin_edges )
1736 {
1737if( reversed_edges && edge_reversed( face, conn ) )
1738 reversed_edges->insert( *i );
1739else1740 skin_edges->insert( *i );
1741 }
1742 }
1743 }
17441745// If the user requested that we create new edges for sides1746// on the skin for which there is no existing edge, and there1747// are still skin sides for which no corresponding edge was found...1748if( create_edges && adj_edges.num_skin() )
1749 {
1750// Create any skin edges that don't exist1751for( AdjSides< 2 >::const_iterator p = adj_edges.begin(); p != adj_edges.end(); ++p )
1752 {
1753if( p->skin() )
1754 {
1755 EntityHandle edge, ec[] = { *it, p->handles[0] };
1756 rval = create_side( this_set, p->adj_elem, MBEDGE, ec, edge );
1757if( MB_SUCCESS != rval ) return rval;
1758if( skin_edges ) skin_edges->insert( edge );
1759 }
1760 }
1761 }
17621763 } // end for each vertex17641765return MB_SUCCESS;
1766 }
17671768ErrorCode Skinner::find_skin_vertices_3D( const EntityHandle this_set,
1769 Tag tag,
1770const Range& entities,
1771 Range* skin_verts,
1772 Range* skin_faces,
1773 Range* reversed_faces,
1774bool create_faces,
1775bool corners_only )
1776 {
1777// This function iterates over all the vertices contained in the1778// input vol elem list. For each such vertex, it then iterates over1779// all of the sides of the vol elements adjacent to the vertex.1780// If an adjacent side is the side of only one of the input1781// elements, then that side is on the skin.1782//1783// This algorithm will visit each skin vertex exactly once. It1784// will visit each skin side once for each vertex in the side.1785//1786// This function expects the caller to have created the passed bit1787// tag and set it to one only for the elements in the passed range. This1788// tag is used to do a fast intersection of the elements adjacent to a1789// vertex with the elements in the input range (discard any for which the1790// tag is not set to one.)1791//1792// For each vertex, iterate over each adjacent element. Construct1793// lists of the sides of each adjacent element that contain the vertex.1794//1795// A list of three-vertex sides is kept for all triangular faces,1796// included three-vertex faces of type MBPOLYGON. Putting polygons1797// in the same list ensures that we find polyhedron and non-polyhedron1798// elements that are adjacent.1799//1800// A list of four-vertex sides is kept for all quadrilateral faces,1801// including four-vertex faces of type MBPOLYGON.1802//1803// Sides with more than four vertices must have an explicit MBPOLYGON1804// element representing them because MBPOLYHEDRON connectivity is a1805// list of faces rather than vertices. So the third list (vertices>=5),1806// need contain only the handle of the face rather than the vertex handles.18071808 ErrorCode rval;
1809 std::vector< EntityHandle >::iterator i, j;
1810 Range::iterator hint;
1811if( skin_verts ) hint = skin_verts->begin();
1812 std::vector< EntityHandle > storage, storage2; // temp storage for conn lists1813const EntityHandle *conn, *conn2;
1814int len, len2;
1815bool find_faces = skin_faces || create_faces;
1816int clen, side, sense, offset, indices[9];
1817 EntityType face_type;
1818 EntityHandle elem;
1819bool printed_nonconformal_ho_warning = false;
18201821if( !entities.all_of_dimension( 3 ) ) return MB_TYPE_OUT_OF_RANGE;
18221823// get all the vertices1824 Range verts;
1825 rval = thisMB->get_connectivity( entities, verts, true );
1826if( MB_SUCCESS != rval ) return rval;
1827// if there are polyhedra in the input list, need to make another1828// call to get vertices from faces1829if( !verts.all_of_dimension( 0 ) )
1830 {
1831 Range::iterator it = verts.upper_bound( MBVERTEX );
1832 Range pfaces;
1833 pfaces.merge( it, verts.end() );
1834 verts.erase( it, verts.end() );
1835 rval = thisMB->get_connectivity( pfaces, verts, true );
1836if( MB_SUCCESS != rval ) return rval;
1837assert( verts.all_of_dimension( 0 ) );
1838 }
18391840 AdjSides< 4 > adj_quads; // 4-node sides adjacent to a vertex1841 AdjSides< 3 > adj_tris; // 3-node sides adjacent to a vertex1842 AdjSides< 2 > adj_poly; // n-node sides (n>5) adjacent to vertex1843// (must have an explicit polygon, so store1844// polygon handle rather than vertices.)1845 std::vector< char > tag_vals;
1846 std::vector< EntityHandle > adj;
1847for( Range::const_iterator it = verts.begin(); it != verts.end(); ++it )
1848 {
1849bool higher_order = false;
18501851// get all adjacent elements1852 adj.clear();
1853 rval = thisMB->get_adjacencies( &*it, 1, 3, false, adj );
1854if( MB_SUCCESS != rval ) return rval;
1855if( adj.empty() ) continue;
18561857// remove those not tagged (intersect with input range)1858 i = j = adj.begin();
1859 tag_vals.resize( adj.size() );
1860 rval = thisMB->tag_get_data( tag, &adj[0], adj.size(), &tag_vals[0] );
1861if( MB_SUCCESS != rval ) return rval;
1862for( ; i != adj.end(); ++i )
1863if( tag_vals[i - adj.begin()] ) *( j++ ) = *i;
1864 adj.erase( j, adj.end() );
18651866// Build lists of sides of 3D element adjacent to the current vertex1867 adj_quads.clear(); // store three other vertices for each adjacent quad face1868 adj_tris.clear(); // store two other vertices for each adjacent tri face1869 adj_poly.clear(); // store handle of each adjacent polygonal face1870int idx;
1871for( i = adj.begin(); i != adj.end(); ++i )
1872 {
1873const EntityType type = TYPE_FROM_HANDLE( *i );
18741875// Special case for POLYHEDRA1876if( type == MBPOLYHEDRON )
1877 {
1878 rval = thisMB->get_connectivity( *i, conn, len );
1879if( MB_SUCCESS != rval ) return rval;
1880for( int k = 0; k < len; ++k )
1881 {
1882 rval = thisMB->get_connectivity( conn[k], conn2, len2, true, &storage2 );
1883if( MB_SUCCESS != rval ) return rval;
1884 idx = std::find( conn2, conn2 + len2, *it ) - conn2;
1885if( idx == len2 ) // vertex not in this face1886continue;
18871888// Treat 3- and 4-vertex faces specially, so that1889// if the mesh contains both elements and polyhedra,1890// we don't miss one type adjacent to the other.1891switch( len2 )
1892 {
1893case3:
1894 adj_tris.insert( conn2, idx, *i, k );
1895break;
1896case4:
1897 adj_quads.insert( conn2, idx, *i, k );
1898break;
1899default:
1900 adj_poly.insert( conn + k, 1, *i, k );
1901break;
1902 }
1903 }
1904 }
1905else1906 {
1907 rval = thisMB->get_connectivity( *i, conn, len, false, &storage );
1908if( MB_SUCCESS != rval ) return rval;
19091910 idx = std::find( conn, conn + len, *it ) - conn;
1911assert( idx != len );
19121913if( len > CN::VerticesPerEntity( type ) )
1914 {
1915 higher_order = true;
1916// skip higher-order nodes for now1917if( idx >= CN::VerticesPerEntity( type ) )
1918 {
1919if( !printed_nonconformal_ho_warning )
1920 {
1921 printed_nonconformal_ho_warning = true;
1922 std::cerr << "Non-conformal higher-order mesh detected in skinner: "1923 << "vertex " << ID_FROM_HANDLE( *it ) << " is a corner in "1924 << "some elements and a higher-order node in others" << std::endl;
1925 }
1926continue;
1927 }
1928 }
19291930// For each side of the element...1931constint num_faces = CN::NumSubEntities( type, 2 );
1932for( int f = 0; f < num_faces; ++f )
1933 {
1934int num_vtx;
1935constshort* face_indices = CN::SubEntityVertexIndices( type, 2, f, face_type, num_vtx );
1936constshort face_idx = std::find( face_indices, face_indices + num_vtx, (short)idx ) - face_indices;
1937// skip sides that do not contain vertex from outer loop1938if( face_idx == num_vtx ) continue; // current vertex not in this face19391940assert( num_vtx <= 4 ); // polyhedra handled above1941switch( face_type )
1942 {
1943case MBTRI:
1944 adj_tris.insert( conn, face_idx, *i, f, face_indices );
1945break;
1946case MBQUAD:
1947 adj_quads.insert( conn, face_idx, *i, f, face_indices );
1948break;
1949default:
1950return MB_TYPE_OUT_OF_RANGE;
1951 }
1952 }
1953 }
1954 } // end for (adj[3])19551956// If vertex is not on skin, advance to next vertex1957if( 0 == ( adj_tris.num_skin() + adj_quads.num_skin() + adj_poly.num_skin() ) ) continue;
19581959// If user requested that skin *vertices* be passed back...1960if( skin_verts )
1961 {
1962// Put skin vertex in output list1963 hint = skin_verts->insert( hint, *it );
19641965// Add mid-edge and mid-face nodes to vertex list1966if( !corners_only && higher_order )
1967 {
1968for( AdjSides< 3 >::const_iterator t = adj_tris.begin(); t != adj_tris.end(); ++t )
1969 {
1970if( t->skin() )
1971 {
1972 elem = t->adj_elem;
1973 EntityType type = TYPE_FROM_HANDLE( elem );
19741975 rval = thisMB->get_connectivity( elem, conn, len, false );
1976if( MB_SUCCESS != rval ) return rval;
1977if( !CN::HasMidNodes( type, len ) ) continue;
19781979 EntityHandle ec[3] = { *it, t->handles[0], t->handles[1] };
1980 CN::SideNumber( type, conn, ec, 3, 2, side, sense, offset );
1981 CN::SubEntityNodeIndices( type, len, 2, side, face_type, clen, indices );
1982assert( MBTRI == face_type );
1983for( int k = 3; k < clen; ++k )
1984 skin_verts->insert( conn[indices[k]] );
1985 }
1986 }
1987for( AdjSides< 4 >::const_iterator q = adj_quads.begin(); q != adj_quads.end(); ++q )
1988 {
1989if( q->skin() )
1990 {
1991 elem = q->adj_elem;
1992 EntityType type = TYPE_FROM_HANDLE( elem );
19931994 rval = thisMB->get_connectivity( elem, conn, len, false );
1995if( MB_SUCCESS != rval ) return rval;
1996if( !CN::HasMidNodes( type, len ) ) continue;
19971998 EntityHandle ec[4] = { *it, q->handles[0], q->handles[1], q->handles[2] };
1999 CN::SideNumber( type, conn, ec, 4, 2, side, sense, offset );
2000 CN::SubEntityNodeIndices( type, len, 2, side, face_type, clen, indices );
2001assert( MBQUAD == face_type );
2002for( int k = 4; k < clen; ++k )
2003 skin_verts->insert( conn[indices[k]] );
2004 }
2005 }
2006 }
2007 }
20082009// If user requested that we pass back the list of 2D elements2010// representing the skin of the mesh...2011if( find_faces )
2012 {
2013// Search list of adjacent faces for any that are on the skin2014 adj.clear();
2015 rval = thisMB->get_adjacencies( &*it, 1, 2, false, adj );
2016if( MB_SUCCESS != rval ) return rval;
20172018for( i = adj.begin(); i != adj.end(); ++i )
2019 {
2020 rval = thisMB->get_connectivity( *i, conn, len, true );
2021if( MB_SUCCESS != rval ) return rval;
2022constint idx2 = std::find( conn, conn + len, *it ) - conn;
2023if( idx2 >= len )
2024 {
2025assert( printed_nonconformal_ho_warning );
2026continue;
2027 }
20282029// Note that the order of the terms in the if statements below2030// is important. We want to unmark any existing skin faces even2031// if we aren't returning them. Otherwise we'll end up creating2032// duplicates if create_faces is true.2033if( 3 == len )
2034 {
2035if( adj_tris.find_and_unmark( conn, idx2, elem ) && skin_faces )
2036 {
2037if( reversed_faces && face_reversed( elem, conn, MBTRI ) )
2038 reversed_faces->insert( *i );
2039else2040 skin_faces->insert( *i );
2041 }
2042 }
2043elseif( 4 == len )
2044 {
2045if( adj_quads.find_and_unmark( conn, idx2, elem ) && skin_faces )
2046 {
2047if( reversed_faces && face_reversed( elem, conn, MBQUAD ) )
2048 reversed_faces->insert( *i );
2049else2050 skin_faces->insert( *i );
2051 }
2052 }
2053else2054 {
2055if( adj_poly.find_and_unmark( &*i, 1, elem ) && skin_faces ) skin_faces->insert( *i );
2056 }
2057 }
2058 }
20592060// If user does not want use to create new faces representing2061// sides for which there is currently no explicit element,2062// skip the remaining code and advance the outer loop to the2063// next vertex.2064if( !create_faces ) continue;
20652066// Polyhedra always have explicitly defined faces, so2067// there is no way we could need to create such a face.2068assert( 0 == adj_poly.num_skin() );
20692070// Create any skin tris that don't exist2071if( adj_tris.num_skin() )
2072 {
2073for( AdjSides< 3 >::const_iterator t = adj_tris.begin(); t != adj_tris.end(); ++t )
2074 {
2075if( t->skin() )
2076 {
2077 EntityHandle tri, c[3] = { *it, t->handles[0], t->handles[1] };
2078 rval = create_side( this_set, t->adj_elem, MBTRI, c, tri );
2079if( MB_SUCCESS != rval ) return rval;
2080if( skin_faces ) skin_faces->insert( tri );
2081 }
2082 }
2083 }
20842085// Create any skin quads that don't exist2086if( adj_quads.num_skin() )
2087 {
2088for( AdjSides< 4 >::const_iterator q = adj_quads.begin(); q != adj_quads.end(); ++q )
2089 {
2090if( q->skin() )
2091 {
2092 EntityHandle quad, c[4] = { *it, q->handles[0], q->handles[1], q->handles[2] };
2093 rval = create_side( this_set, q->adj_elem, MBQUAD, c, quad );
2094if( MB_SUCCESS != rval ) return rval;
2095if( skin_faces ) skin_faces->insert( quad );
2096 }
2097 }
2098 }
2099 } // end for each vertex21002101return MB_SUCCESS;
2102 }
21032104 } // namespace moab