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