Mesh Oriented datABase  (version 5.5.0)
An array-based unstructured mesh library
DualTool.cpp
Go to the documentation of this file.
1 /**
2  * MOAB, a Mesh-Oriented datABase, is a software component for creating,
3  * storing and accessing finite element mesh data.
4  *
5  * Copyright 2004 Sandia Corporation. Under the terms of Contract
6  * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
7  * retains certain rights in this software.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  */
15 
16 #include "moab/DualTool.hpp"
17 #include "moab/Range.hpp"
18 // using Core for call to check_adjacencies
19 #include "moab/Core.hpp"
20 #include "Internals.hpp"
21 #include "MBTagConventions.hpp"
22 #include "moab/Skinner.hpp"
23 #include "moab/Core.hpp"
24 #include "moab/MeshTopoUtil.hpp"
25 #include "AEntityFactory.hpp"
26 #include "moab/CN.hpp"
27 #include <string>
28 #include <algorithm>
29 #include <iostream>
30 #include <sstream>
31 #include <cassert>
32 
33 #define RR \
34  if( MB_SUCCESS != result ) return result
35 #define SWAP( a, b ) \
36  { \
37  EntityHandle tmp_ent = a; \
38  ( a ) = b; \
39  ( b ) = tmp_ent; \
40  }
41 
42 namespace moab
43 {
44 
45 bool debug = false;
46 bool debug_ap = false;
47 
48 //! tag name for dual surfaces
49 const char* DualTool::DUAL_SURFACE_TAG_NAME = "DUAL_SURFACE";
50 
51 //! tag name for dual curves
52 const char* DualTool::DUAL_CURVE_TAG_NAME = "DUAL_CURVE";
53 
54 //! tag name for dual cells
55 const char* DualTool::IS_DUAL_CELL_TAG_NAME = "__IS_DUAL_CELL";
56 
57 //! tag name for dual entities
58 const char* DualTool::DUAL_ENTITY_TAG_NAME = "__DUAL_ENTITY";
59 
60 //! tag name for extra dual entities
61 const char* DualTool::EXTRA_DUAL_ENTITY_TAG_NAME = "__EXTRA_DUAL_ENTITY";
62 
63 //! tag name for graphics point
64 const char* DualTool::DUAL_GRAPHICS_POINT_TAG_NAME = "__DUAL_GRAPHICS_POINT";
65 
66 // const int DualTool::GP_SIZE = 20;
67 
68 DualTool::DualTool( Interface* impl ) : mbImpl( impl )
69 {
70  EntityHandle dum_handle = 0;
71  ErrorCode result;
72 
74  MB_TAG_SPARSE | MB_TAG_CREAT, &dum_handle );
75  assert( MB_SUCCESS == result );
76 
78  &dum_handle );
79  assert( MB_SUCCESS == result );
80 
81  unsigned int dummy = 0;
83  MB_TAG_SPARSE | MB_TAG_CREAT, &dummy );
84  assert( MB_SUCCESS == result );
85 
87  MB_TAG_DENSE | MB_TAG_CREAT, &dum_handle );
88  assert( MB_SUCCESS == result );
89 
91  MB_TAG_SPARSE | MB_TAG_CREAT, &dum_handle );
92  assert( MB_SUCCESS == result );
93 
94  static const char dum_name[CATEGORY_TAG_SIZE] = { 0 };
96  MB_TAG_SPARSE | MB_TAG_CREAT, dum_name );
97  assert( MB_SUCCESS == result );
98 
99  DualTool::GraphicsPoint dum_pt( 0.0, 0.0, 0.0, -1 );
102  assert( MB_SUCCESS == result );
103 
105 
106  if( MB_SUCCESS == result )
107  {
108  } // empty statement to get rid of warning.
109 
110  maxHexId = -1;
111 }
112 
114 
115 //! construct the dual entities for the entire mesh
117 {
118  // allocate a dual entity for each primal entity in the mesh, starting
119  // with highest dimension and working downward; do each dimension in a separate code
120  // block, since they're all handled slightly differently
121 
122  Range regions, faces, edges, vertices;
123  ErrorCode result;
124 
125  if( NULL == entities || 0 == num_entities )
126  {
127 
128  // first, construct all the aentities, since they're currently needed to
129  // compute the dual
130  result = mbImpl->get_entities_by_dimension( 0, 0, vertices );
131  if( MB_SUCCESS != result ) return result;
132 
133  result = MeshTopoUtil( mbImpl ).construct_aentities( vertices );
134  if( MB_SUCCESS != result ) return result;
135 
136  // get all edges, faces and regions now, so we don't need to filter out dual
137  // entities later
138 
139  result = mbImpl->get_entities_by_dimension( 0, 1, edges );
140  if( MB_SUCCESS != result ) return result;
141  result = mbImpl->get_entities_by_dimension( 0, 2, faces );
142  if( MB_SUCCESS != result ) return result;
143  result = mbImpl->get_entities_by_dimension( 0, 3, regions );
144  if( MB_SUCCESS != result ) return result;
145 
146  // get the max global id for hexes, we'll need for modification ops
147  std::vector< int > gid_vec( regions.size() );
148  result = mbImpl->tag_get_data( globalId_tag(), regions, &gid_vec[0] );
149  if( MB_SUCCESS != result ) return result;
150  maxHexId = -1;
151  Range::iterator rit;
152  unsigned int i;
153  for( rit = regions.begin(), i = 0; rit != regions.end(); ++rit, i++ )
154  {
155  if( gid_vec[i] > maxHexId && mbImpl->type_from_handle( *rit ) == MBHEX ) maxHexId = gid_vec[i];
156  }
157  }
158  else
159  {
160  // get entities of various dimensions adjacent to these
161  result = mbImpl->get_adjacencies( entities, num_entities, 0, true, vertices, Interface::UNION );
162  if( MB_SUCCESS != result ) return result;
163  result = mbImpl->get_adjacencies( entities, num_entities, 1, true, edges, Interface::UNION );
164  if( MB_SUCCESS != result ) return result;
165  result = mbImpl->get_adjacencies( entities, num_entities, 2, true, faces, Interface::UNION );
166  if( MB_SUCCESS != result ) return result;
167  result = mbImpl->get_adjacencies( entities, num_entities, 3, true, regions, Interface::UNION );
168  if( MB_SUCCESS != result ) return result;
169  }
170 
171  Range dual_verts;
172  result = construct_dual_vertices( regions, dual_verts );
173  if( MB_SUCCESS != result || dual_verts.size() != regions.size() ) return result;
174  if( debug ) std::cout << "Constructed " << dual_verts.size() << " dual vertices." << std::endl;
175 
176  // don't really need dual edges, but construct 'em anyway
177  Range dual_edges;
178  result = construct_dual_edges( faces, dual_edges );
179  if( MB_SUCCESS != result || dual_edges.size() != faces.size() ) return result;
180  if( debug ) std::cout << "Constructed " << dual_edges.size() << " dual edges." << std::endl;
181 
182  // construct dual faces
183  Range dual_faces;
184  result = construct_dual_faces( edges, dual_faces );
185  if( MB_SUCCESS != result || dual_faces.size() != edges.size() ) return result;
186  if( debug ) std::cout << "Constructed " << dual_faces.size() << " dual faces." << std::endl;
187 
188  // construct dual cells
189  Range dual_cells;
190  result = construct_dual_cells( vertices, dual_cells );
191  if( MB_SUCCESS != result || dual_cells.size() != vertices.size() ) return result;
192  if( debug ) std::cout << "Constructed " << dual_cells.size() << " dual cells." << std::endl;
193 
194  return MB_SUCCESS;
195 }
196 
197 ErrorCode DualTool::construct_dual_vertices( const Range& all_regions, Range& dual_ents )
198 {
199  if( all_regions.empty() ) return MB_SUCCESS;
200 
201  // make sure they're all regions
202  assert( 3 == CN::Dimension( TYPE_FROM_HANDLE( *all_regions.begin() ) ) &&
203  3 == CN::Dimension( TYPE_FROM_HANDLE( *all_regions.rbegin() ) ) );
204 
206  EntityHandle dual_ent;
207  ErrorCode tmp_result = MB_SUCCESS;
208  ErrorCode result = MB_SUCCESS;
209 
210  for( rit = all_regions.begin(); rit != all_regions.end(); ++rit )
211  {
212  if( tmp_result != MB_SUCCESS ) result = tmp_result;
213 
214  tmp_result = mbImpl->tag_get_data( dualEntity_tag(), &( *rit ), 1, &dual_ent );
215  if( MB_SUCCESS == tmp_result && 0 != dual_ent )
216  {
217  dual_ents.insert( dual_ent );
218  continue;
219  }
220  else if( MB_SUCCESS != tmp_result )
221  continue;
222 
223  tmp_result = construct_dual_vertex( *rit, dual_ent, false, true );
224  if( MB_SUCCESS != tmp_result ) continue;
225 
226  // save it in the list of new dual ents
227  dual_ents.insert( dual_ent );
228  }
229 
230  return result;
231 }
232 
234  EntityHandle& dual_ent,
235  const bool extra,
236  const bool add_graphics_pt )
237 {
238  // no dual entity; construct one; first need the avg coordinates
239  unsigned int is_dual = 0x1;
240  double avg_pos[3];
241  ErrorCode result = MeshTopoUtil( mbImpl ).get_average_position( entity, avg_pos );
242  if( MB_SUCCESS != result ) return result;
243 
244  // now construct the new dual entity
245  result = mbImpl->create_vertex( avg_pos, dual_ent );
246  if( MB_SUCCESS != result ) return result;
247 
248  // tag it indicating it's a dual entity
249  result = mbImpl->tag_set_data( isDualCell_tag(), &dual_ent, 1, &is_dual );
250  if( MB_SUCCESS != result ) return result;
251 
252  // tag the primal entity with its dual entity and vica versa
253  if( extra )
254  result = mbImpl->tag_set_data( extraDualEntity_tag(), &( entity ), 1, &dual_ent );
255  else
256  result = mbImpl->tag_set_data( dualEntity_tag(), &( entity ), 1, &dual_ent );
257  if( MB_SUCCESS != result ) return result;
258 
259  result = mbImpl->tag_set_data( dualEntity_tag(), &dual_ent, 1, &( entity ) );
260  if( MB_SUCCESS != result ) return result;
261 
262  if( add_graphics_pt )
263  // put a graphics point on that vertex too
264  result = add_graphics_point( dual_ent, avg_pos );
265 
266  return result;
267 }
268 
270 {
271  // add a graphics pt, placed at the same position as the vertex
272  double my_pos[3];
273  ErrorCode result;
274 
275  if( NULL == avg_pos )
276  {
277  result = MeshTopoUtil( mbImpl ).get_average_position( entity, my_pos );
278  if( MB_SUCCESS != result ) return result;
279  }
280  else
281  for( int i = 0; i < 3; i++ )
282  my_pos[i] = avg_pos[i];
283 
284  DualTool::GraphicsPoint dum_pt( my_pos, -1 );
285  result = mbImpl->tag_set_data( dualGraphicsPoint_tag(), &entity, 1, &dum_pt );
286  return result;
287 }
288 
289 ErrorCode DualTool::construct_dual_edges( const Range& all_faces, Range& dual_ents )
290 {
291  if( all_faces.empty() ) return MB_SUCCESS;
292 
293  // make sure they're all faces
294  assert( 2 == CN::Dimension( TYPE_FROM_HANDLE( *all_faces.begin() ) ) &&
295  2 == CN::Dimension( TYPE_FROM_HANDLE( *all_faces.rbegin() ) ) );
296 
298  EntityHandle dual_ent;
299  unsigned int is_dual = 0x1;
300  ErrorCode tmp_result = MB_SUCCESS;
301  ErrorCode result = MB_SUCCESS;
302 
303  for( rit = all_faces.begin(); rit != all_faces.end(); ++rit )
304  {
305  if( tmp_result != MB_SUCCESS ) result = tmp_result;
306 
307  tmp_result = mbImpl->tag_get_data( dualEntity_tag(), &( *rit ), 1, &dual_ent );
308  if( MB_SUCCESS == tmp_result && 0 != dual_ent )
309  {
310  dual_ents.insert( dual_ent );
311  continue;
312  }
313 
314  // no dual entity; construct one; get the bounding regions
315  std::vector< EntityHandle > out_ents;
316  tmp_result = mbImpl->get_adjacencies( &( *rit ), 1, 3, false, out_ents );
317  if( MB_SUCCESS != tmp_result || out_ents.empty() ) continue;
318 
319  // get the dual vertices
320  std::vector< EntityHandle > dual_verts( out_ents.size() );
321  tmp_result = mbImpl->tag_get_data( dualEntity_tag(), &out_ents[0], out_ents.size(), &dual_verts[0] );
322  if( MB_SUCCESS != tmp_result ) continue;
323  assert( dual_verts.size() <= 2 );
324 
325  double avg_pos[3];
326  bool bdy_face = ( dual_verts.size() == 1 ? true : false );
327  if( bdy_face )
328  {
329  // boundary face - make a dual vertex at the face center and put in list
330  tmp_result = construct_dual_vertex( *rit, dual_ent, true, true );
331 
332  // put it on vertex list
333  dual_verts.push_back( dual_ent );
334  }
335 
336  assert( dual_verts.size() == 2 );
337 
338  // now create the dual edge
339  tmp_result = mbImpl->create_element( MBEDGE, &dual_verts[0], 2, dual_ent );
340  if( MB_SUCCESS != tmp_result || 0 == dual_ent ) continue;
341 
342  // save it in the list of new dual ents
343  dual_ents.insert( dual_ent );
344 
345  // tag the primal entity with its dual entity and vica versa
346  tmp_result = mbImpl->tag_set_data( dualEntity_tag(), &( *rit ), 1, &dual_ent );
347  if( MB_SUCCESS != tmp_result ) continue;
348 
349  tmp_result = mbImpl->tag_set_data( dualEntity_tag(), &dual_ent, 1, &( *rit ) );
350  if( MB_SUCCESS != tmp_result ) continue;
351 
352  // tag the edge indicating it's a dual entity
353  tmp_result = mbImpl->tag_set_data( isDualCell_tag(), &dual_ent, 1, &is_dual );
354  if( MB_SUCCESS != tmp_result ) continue;
355 
356  // add a graphics point to the edge; position depends on whether it's a
357  // bdy face (mid-pt of dual edge) or not (mid-pt of primal face)
358  if( bdy_face )
359  tmp_result = add_graphics_point( dual_ent );
360  else
361  {
362  // get the face's position
363  tmp_result = MeshTopoUtil( mbImpl ).get_average_position( *rit, avg_pos );
364  if( MB_SUCCESS != tmp_result ) continue;
365  tmp_result = add_graphics_point( dual_ent, avg_pos );
366  }
367  if( MB_SUCCESS != tmp_result ) continue;
368  }
369 
370  return result;
371 }
372 
373 ErrorCode DualTool::construct_dual_faces( const Range& all_edges, Range& dual_ents )
374 {
375  if( all_edges.empty() ) return MB_SUCCESS;
376 
377  // make sure they're all edges
378  assert( 1 == CN::Dimension( TYPE_FROM_HANDLE( *all_edges.begin() ) ) &&
379  1 == CN::Dimension( TYPE_FROM_HANDLE( *all_edges.rbegin() ) ) );
380 
382  EntityHandle dual_ent;
383  unsigned int is_dual = 0x1;
384  ErrorCode tmp_result = MB_SUCCESS;
385  ErrorCode result = MB_SUCCESS;
386  Range equiv_edges;
387 #define TRC \
388  if( MB_SUCCESS != tmp_result ) \
389  { \
390  result = tmp_result; \
391  continue; \
392  }
393  for( rit = all_edges.begin(); rit != all_edges.end(); ++rit )
394  {
395 
396  tmp_result = mbImpl->tag_get_data( dualEntity_tag(), &( *rit ), 1, &dual_ent );
397  if( MB_SUCCESS == tmp_result && 0 != dual_ent )
398  {
399  dual_ents.insert( dual_ent );
400  continue;
401  }
402 
403  // no dual entity; construct one; get the dual vertices bounding the edge in radial order,
404  // then construct the dual face
405  std::vector< EntityHandle > rad_dverts;
406  bool bdy_edge;
407  tmp_result = get_radial_dverts( *rit, rad_dverts, bdy_edge );
408  TRC if( rad_dverts.empty() ) continue;
409 
410  tmp_result = mbImpl->create_element( MBPOLYGON, &rad_dverts[0], rad_dverts.size(), dual_ent );
411  TRC
412 
413  // tag it indicating it's a dual entity, and tag primal/dual with dual/primal
414  tmp_result = mbImpl->tag_set_data( isDualCell_tag(), &dual_ent, 1, &is_dual );
415  TRC tmp_result = mbImpl->tag_set_data( dualEntity_tag(), &( *rit ), 1, &dual_ent );
416  TRC tmp_result = mbImpl->tag_set_data( dualEntity_tag(), &dual_ent, 1, &( *rit ) );
417  TRC
418 
419  // save it in the list of new dual ents
420  dual_ents.insert( dual_ent );
421 
422  // add a graphics point to the cell; position depends on whether it's a
423  // bdy cell (mid-pt of cell's vertices) or not (mid-pt of primal edge)
424  double avg_pos[3];
425  tmp_result = MeshTopoUtil( mbImpl ).get_average_position( *rit, avg_pos );
426  TRC if( bdy_edge )
427  {
428 
429  // add a new dual edge betw last 2 verts
430  EntityHandle new_edge;
431  tmp_result = mbImpl->create_element( MBEDGE, &rad_dverts[rad_dverts.size() - 2], 2, new_edge );
432  TRC tmp_result = mbImpl->tag_set_data( isDualCell_tag(), &new_edge, 1, &is_dual );
433  TRC
434 
435  // tag the new dual edge with the primal edge as it's dual entity; primal
436  // edge IS NOT likewise tagged, since it's already tagged with the 2cell
437  tmp_result = mbImpl->tag_set_data( dualEntity_tag(), &new_edge, 1, &( *rit ) );
438  TRC
439 
440  // add a graphics pt, position is center of primal edge
441  tmp_result = add_graphics_point( dual_ent );
442  TRC tmp_result = add_graphics_point( new_edge, avg_pos );
443  TRC
444  }
445 
446  else
447  {
448  // if inside, point goes on the 2cell, at primal edge mid-pt
449  tmp_result = add_graphics_point( dual_ent, avg_pos );
450  TRC
451  }
452 
453  // check to see whether we have equiv entities; if we find any, save for later fixup
454  Range dum_edges, dum_poly( dual_ent, dual_ent );
455  tmp_result = mbImpl->get_adjacencies( dum_poly, 1, false, dum_edges );
456  if( MB_MULTIPLE_ENTITIES_FOUND == tmp_result )
457  {
458  // we do - need to add adjacencies to disambiguate; use the primal
459  equiv_edges.merge( dum_edges );
460  }
461  }
462 
463  if( !equiv_edges.empty() ) result = check_dual_equiv_edges( equiv_edges );
464 
465  return result;
466 }
467 
469 {
470  // fix equivalent dual edges (i.e. edges whose vertices define multiple edges)
471  // by explicitly adding adjacencies to containing polygons; adjacent polygons
472  // found by going through primal
473  ErrorCode tmp_result, result = MB_SUCCESS;
474 
475  Range all_dedges( dual_edges );
476  // first, go through all dual edges and find equivalent edges (by looking for
477  // up-adjacent edges on the vertices of each edge)
478  for( Range::iterator rit = dual_edges.begin(); rit != dual_edges.end(); ++rit )
479  {
480  Range connect, dum_range( *rit, *rit );
481  tmp_result = mbImpl->get_adjacencies( dum_range, 0, false, connect );
482  if( MB_SUCCESS != tmp_result ) continue;
483  tmp_result = mbImpl->get_adjacencies( connect, 1, false, all_dedges, Interface::UNION );
484  if( MB_SUCCESS != tmp_result ) continue;
485  }
486 
487  // save a copy for checking later
488  Range save_all_2cells;
489 
490  // go through each edge
491  while( !all_dedges.empty() )
492  {
493  EntityHandle this_edge = *all_dedges.begin();
494  all_dedges.erase( all_dedges.begin() );
495 
496  const EntityHandle* connect;
497  int num_connect;
498  result = mbImpl->get_connectivity( this_edge, connect, num_connect );
499  if( MB_SUCCESS != result ) continue;
500 
501  Range dum_edges, verts;
502  verts.insert( connect[0] );
503  verts.insert( connect[1] );
504  tmp_result = mbImpl->get_adjacencies( verts, 1, false, dum_edges );
505  if( MB_SUCCESS != tmp_result )
506  {
507  result = tmp_result;
508  continue;
509  }
510  if( dum_edges.size() == 1 )
511  {
512  // not an equiv edge - already removed from list, so just continue
513  continue;
514  }
515 
516  // ok, have an equiv entity - fix by looking through primal
517  // pre-get the primal of these
518  EntityHandle dedge_quad;
519  tmp_result = mbImpl->tag_get_data( dualEntity_tag(), &this_edge, 1, &dedge_quad );
520  if( MB_SUCCESS != tmp_result )
521  {
522  result = tmp_result;
523  continue;
524  }
525 
526  if( MBQUAD == mbImpl->type_from_handle( dedge_quad ) )
527  {
528 
529  // get the primal edges adj to quad
530  Range dum_quad_range( dedge_quad, dedge_quad ), adj_pedges;
531  tmp_result = mbImpl->get_adjacencies( dum_quad_range, 1, false, adj_pedges );
532  if( MB_SUCCESS != tmp_result )
533  {
534  result = tmp_result;
535  continue;
536  }
537  // get the dual 2cells corresponding to those pedges
538  std::vector< EntityHandle > dcells;
539  dcells.resize( adj_pedges.size() );
540  tmp_result = mbImpl->tag_get_data( dualEntity_tag(), adj_pedges, &dcells[0] );
541  if( MB_SUCCESS != tmp_result )
542  {
543  result = tmp_result;
544  continue;
545  }
546  // now add explicit adjacencies from the dedge to those dcells
547  std::vector< EntityHandle >::iterator vit;
548  for( vit = dcells.begin(); vit != dcells.end(); ++vit )
549  {
550  save_all_2cells.insert( *vit );
551 
552  assert( MBPOLYGON == mbImpl->type_from_handle( *vit ) );
553  tmp_result = mbImpl->add_adjacencies( this_edge, &( *vit ), 1, false );
554  if( MB_SUCCESS != tmp_result )
555  {
556  result = tmp_result;
557  continue;
558  }
559  // check that there are really adjacencies and *vit is in them
560  const EntityHandle* adjs;
561  int num_adjs;
562  tmp_result = reinterpret_cast< Core* >( mbImpl )->a_entity_factory()->get_adjacencies( this_edge, adjs,
563  num_adjs );
564  if( NULL == adjs || std::find( adjs, adjs + num_adjs, *vit ) == adjs + num_adjs )
565  std::cout << "Add_adjacencies failed in construct_dual_faces." << std::endl;
566  }
567  }
568  else
569  {
570  // else, have a dual edge representing a bdy edge - tie directly to
571  // dual entity if its dual entity
572  EntityHandle bdy_dcell;
573  tmp_result = mbImpl->tag_get_data( dualEntity_tag(), &dedge_quad, 1, &bdy_dcell );
574  TRC assert( MBPOLYGON == mbImpl->type_from_handle( bdy_dcell ) );
575 
576  tmp_result = mbImpl->add_adjacencies( this_edge, &bdy_dcell, 1, false );
577  if( MB_SUCCESS != tmp_result )
578  {
579  result = tmp_result;
580  continue;
581  }
582  }
583  }
584 
585  // sanity check - look for adj edges again, and check for equiv entities
586  for( Range::iterator vit = save_all_2cells.begin(); vit != save_all_2cells.end(); ++vit )
587  {
588  Range adj_edges, dum_quad_range;
589  dum_quad_range.insert( *vit );
590  assert( MBPOLYGON == mbImpl->type_from_handle( *vit ) );
591  tmp_result = mbImpl->get_adjacencies( dum_quad_range, 1, false, adj_edges );
592  if( MB_MULTIPLE_ENTITIES_FOUND == tmp_result )
593  {
594  std::cout << "Multiple entities returned for polygon " << mbImpl->id_from_handle( *vit ) << "."
595  << std::endl;
596  continue;
597  }
598  }
599  // success!
600  return result;
601 }
602 
603 ErrorCode DualTool::construct_dual_cells( const Range& all_verts, Range& dual_ents )
604 {
605  if( all_verts.empty() ) return MB_SUCCESS;
606 
607  // make sure they're all edges
608  assert( 0 == CN::Dimension( TYPE_FROM_HANDLE( *all_verts.begin() ) ) &&
609  0 == CN::Dimension( TYPE_FROM_HANDLE( *all_verts.rbegin() ) ) );
610 
612  EntityHandle dual_ent;
613  unsigned int is_dual = 0x1;
614  ErrorCode tmp_result = MB_SUCCESS;
615  ErrorCode result = MB_SUCCESS;
616  std::vector< EntityHandle > edges, dfaces;
617 
618  for( rit = all_verts.begin(); rit != all_verts.end(); ++rit )
619  {
620  if( tmp_result != MB_SUCCESS ) result = tmp_result;
621 
622  tmp_result = mbImpl->tag_get_data( dualEntity_tag(), &( *rit ), 1, &dual_ent );
623  if( MB_SUCCESS == tmp_result && 0 != dual_ent )
624  {
625  dual_ents.insert( dual_ent );
626  continue;
627  }
628 
629  // no dual entity; construct one; get the edges bounding the vertex
630  edges.clear();
631  dfaces.clear();
632  tmp_result = mbImpl->get_adjacencies( &( *rit ), 1, 1, false, edges );
633  if( MB_SUCCESS != tmp_result ) continue;
634 
635  // get the dual faces corresponding to the edges
636  dfaces.resize( edges.size() );
637  tmp_result = mbImpl->tag_get_data( dualEntity_tag(), &edges[0], edges.size(), &dfaces[0] );
638  if( MB_SUCCESS != tmp_result ) continue;
639 
640  // create the dual cell from those faces
641  tmp_result = mbImpl->create_element( MBPOLYHEDRON, &dfaces[0], dfaces.size(), dual_ent );
642  if( MB_SUCCESS != tmp_result || 0 == dual_ent ) continue;
643 
644  // save it in the list of new dual ents
645  dual_ents.insert( dual_ent );
646 
647  // tag it indicating it's a dual entity
648  tmp_result = mbImpl->tag_set_data( isDualCell_tag(), &dual_ent, 1, &is_dual );
649  if( MB_SUCCESS != tmp_result ) continue;
650 
651  // tag the primal entity with its dual entity and vica versa
652  tmp_result = mbImpl->tag_set_data( dualEntity_tag(), &( *rit ), 1, &dual_ent );
653  if( MB_SUCCESS != tmp_result ) continue;
654  tmp_result = mbImpl->tag_set_data( dualEntity_tag(), &dual_ent, 1, &( *rit ) );
655  if( MB_SUCCESS != tmp_result ) continue;
656  }
657 
658  return result;
659 }
660 
661 //! given an edge handle, return a list of dual vertices in radial order
662 //! around the edge
664  std::vector< EntityHandle >& rad_dverts,
665  bool& bdy_edge )
666 {
667  rad_dverts.clear();
668 
669  std::vector< EntityHandle > rad_faces, rad_ents;
670  ErrorCode result = MeshTopoUtil( mbImpl ).star_entities( edge, rad_faces, bdy_edge, 0, &rad_ents );
671  if( MB_SUCCESS != result ) return result;
672 
673  if( bdy_edge )
674  {
675  // if we're a bdy edge, change the order back to what DualTool expects
676  rad_ents.push_back( *rad_faces.rbegin() );
677  rad_ents.push_back( *rad_faces.begin() );
678  }
679 
680  rad_dverts.resize( rad_ents.size() );
681  for( unsigned int i = 0; i < rad_ents.size(); i++ )
682  {
683  EntityHandle dual_ent;
684  result = mbImpl->tag_get_data( dualEntity_tag(), &rad_ents[i], 1, &dual_ent );
685  if( !bdy_edge || i < rad_ents.size() - 2 )
686  rad_dverts[i] = dual_ent;
687  else
688  {
689  // fix up this entry
690  assert( mbImpl->type_from_handle( dual_ent ) == MBEDGE );
691 
692  // get connectivity of that edge
693  const EntityHandle* connect;
694  int num_connect;
695  result = mbImpl->get_connectivity( dual_ent, connect, num_connect );
696  if( MB_SUCCESS != result ) return result;
697 
698  // we want the one that's not already on the list; reuse last_face
699  int last_hex = ( i == rad_ents.size() - 1 ? 0 : i - 1 );
700  EntityHandle last_face = ( connect[0] == rad_dverts[last_hex] ? connect[1] : connect[0] );
701  rad_dverts[i] = last_face;
702  }
703  }
704 
705  return result;
706 }
707 
708 //! construct the dual entities for a hex mesh, including dual surfaces & curves
710 {
711  std::vector< EntityHandle > evec;
712  std::copy( entities.begin(), entities.end(), std::back_inserter( evec ) );
713  return construct_hex_dual( &evec[0], evec.size() );
714 }
715 
716 //! construct the dual entities for a hex mesh, including dual surfaces & curves
718 {
719  // really quite simple:
720 
721  // construct the dual...
722  ErrorCode result = construct_dual( entities, num_entities );
723  if( MB_SUCCESS != result )
724  {
725  std::cerr << "Error constructing dual entities for primal entities." << std::endl;
726  return result;
727  }
728 
729  // now traverse to build 1d and 2d hyperplanes
730  result = construct_dual_hyperplanes( 1, entities, num_entities );
731  if( MB_SUCCESS != result )
732  {
733  std::cerr << "Problem traversing 1d hyperplanes." << std::endl;
734  return result;
735  }
736 
737  result = construct_dual_hyperplanes( 2, entities, num_entities );
738  if( MB_SUCCESS != result )
739  {
740  std::cerr << "Problem traversing 2d hyperplanes." << std::endl;
741  return result;
742  }
743 
744  result = construct_hp_parent_child();
745  if( MB_SUCCESS != result )
746  {
747  std::cerr << "Problem constructing parent/child relations between hyperplanes." << std::endl;
748  return result;
749  }
750 
751  // see? simple, just like I said
752  return MB_SUCCESS;
753 }
754 
755 //! get the cells of the dual
756 ErrorCode DualTool::get_dual_entities( const int dim, EntityHandle* entities, const int num_entities, Range& dual_ents )
757 {
758  if( 0 == isDualCell_tag() ) return MB_SUCCESS;
759  if( 0 > dim || 3 < dim ) return MB_INDEX_OUT_OF_RANGE;
760 
761  unsigned int dum = 0x1;
762  const void* dum_ptr = &dum;
763  static EntityType dual_type[] = { MBVERTEX, MBEDGE, MBPOLYGON, MBPOLYHEDRON };
764 
765  Range dim_ents;
766 
767  ErrorCode result;
768 
769  if( 0 == entities || 0 == num_entities )
770  {
771  // just get all the dual entities of this dimension
772  result = mbImpl->get_entities_by_type_and_tag( 0, dual_type[dim], &isDualCellTag, &dum_ptr, 1, dual_ents );
773  }
774  else
775  {
776  // else look for specific dual entities
777  result = mbImpl->get_adjacencies( entities, num_entities, 3 - dim, false, dim_ents, Interface::UNION );
778  if( MB_SUCCESS != result ) return result;
779  std::vector< EntityHandle > dual_ents_vec( dim_ents.size() );
780  result = mbImpl->tag_get_data( dualEntity_tag(), dim_ents, &dual_ents_vec[0] );
781  if( MB_SUCCESS != result ) return result;
782  std::copy( dual_ents_vec.begin(), dual_ents_vec.end(), range_inserter( dual_ents ) );
783  }
784 
785  return result;
786 }
787 
788 //! get the faces of the dual
791  const int num_entities,
792  std::vector< EntityHandle >& dual_ents )
793 {
794  Range tmp_range;
795  ErrorCode result = get_dual_entities( dim, entities, num_entities, tmp_range );
796  if( MB_SUCCESS != result ) return result;
797 
798  // dual_ents.insert(dual_ents.end(), tmp_range.begin(), tmp_range.end());
799  dual_ents.reserve( dual_ents.size() + tmp_range.size() );
800  for( Range::const_iterator it = tmp_range.begin(); it != tmp_range.end(); ++it )
801  {
802  dual_ents.push_back( *it );
803  }
804  return MB_SUCCESS;
805 }
806 
807 ErrorCode DualTool::get_dual_hyperplanes( const Interface* impl, const int dim, Range& dual_ents )
808 {
809  if( dim != 1 && dim != 2 ) return MB_INDEX_OUT_OF_RANGE;
810 
811  Tag dual_tag;
812  ErrorCode result;
813 
814  if( dim == 1 )
815  result = impl->tag_get_handle( DUAL_CURVE_TAG_NAME, 1, MB_TYPE_HANDLE, dual_tag );
816  else
817  result = impl->tag_get_handle( DUAL_SURFACE_TAG_NAME, 1, MB_TYPE_HANDLE, dual_tag );
818 
819  if( MB_SUCCESS == result )
820  result = impl->get_entities_by_type_and_tag( 0, MBENTITYSET, &dual_tag, NULL, 1, dual_ents, Interface::UNION );
821 
822  return result;
823 }
824 
826 {
827  // this function traverses dual faces of input dimension, constructing
828  // dual hyperplanes of them in sets as it goes
829 
830  // check various inputs
831  int num_quads, num_hexes;
832  if(
833  // this function only makes sense for dim == 1 or dim == 2
834  ( dim != 1 && dim != 2 ) ||
835  // should either be quads or hexes around
836  mbImpl->get_number_entities_by_type( 0, MBQUAD, num_quads ) != MB_SUCCESS ||
837  mbImpl->get_number_entities_by_type( 0, MBHEX, num_hexes ) != MB_SUCCESS ||
838  // if we're asking for 1d dual ents, should be quads around
839  ( num_quads == 0 && dim == 1 ) ||
840  // if we're asking for 2d dual ents, should be hexes around
841  ( num_hexes == 0 && dim == 2 ) )
842  return MB_FAILURE;
843 
844  Tag hp_tag = ( 1 == dim ? dualCurve_tag() : dualSurface_tag() );
845 
846  // two stacks: one completely untreated entities, and the other untreated
847  // entities on the current dual hyperplane
848  std::vector< EntityHandle > tot_untreated;
849 
850  // put dual entities of this dimension on the untreated list
851  ErrorCode result = get_dual_entities( dim, entities, num_entities, tot_untreated );
852  if( MB_SUCCESS != result ) return result;
853 
854  // main part of traversal loop
855  EntityHandle this_ent;
856  EntityHandle this_hp;
857 
858  while( !tot_untreated.empty() )
859  {
860  if( debug && dim == 2 /*(tot_untreated.size()%report == 0)*/ )
861  std::cout << "Untreated list size " << tot_untreated.size() << "." << std::endl;
862 
863  this_ent = tot_untreated.back();
864  tot_untreated.pop_back();
865  result = mbImpl->tag_get_data( hp_tag, &this_ent, 1, &this_hp );
866  if( MB_SUCCESS != result && MB_TAG_NOT_FOUND != result ) return result;
867 
868  // d for this entity having a hyperplane assignment already
869  else if( this_hp != 0 )
870  continue;
871 
872  if( 1 == dim && check_1d_loop_edge( this_ent ) ) continue;
873 
874  // inner loop: traverse the hyperplane 'till we don't have any more
875  result = traverse_hyperplane( hp_tag, this_hp, this_ent );
876  if( MB_SUCCESS != result )
877  {
878  std::cout << "Failed to traverse hyperplane ";
879  if( this_hp )
880  std::cout << mbImpl->id_from_handle( this_hp ) << "." << std::endl;
881  else
882  std::cout << "0." << std::endl;
883  return result;
884  }
885 
886  // ok, now order the edges if it's a chord
887  if( 1 == dim ) order_chord( this_hp );
888  }
889 
890  return MB_SUCCESS;
891 }
892 
894 {
895  Range tmp_star, star, tmp_range, new_hyperplane_ents;
896  std::vector< EntityHandle > hp_untreated;
897  int dim = mbImpl->dimension_from_handle( this_ent );
898  MeshTopoUtil mtu( mbImpl );
899  this_hp = 0;
900  ErrorCode result;
901 
902  unsigned short mark_val = 0x0;
903  Tag mark_tag;
904  result = mbImpl->tag_get_handle( "__hyperplane_mark", 1, MB_TYPE_BIT, mark_tag, MB_TAG_CREAT | MB_TAG_BIT );
905  if( MB_SUCCESS != result ) return result;
906  mark_val = 0x1;
907 
908  while( 0 != this_ent )
909  {
910  EntityHandle tmp_hp = get_dual_hyperplane( this_ent );
911  if( 0 == this_hp && 0 != tmp_hp ) this_hp = tmp_hp;
912 
913  if( 0 == tmp_hp ) new_hyperplane_ents.insert( this_ent );
914 
915  if( debug && hp_untreated.size() % 10 == 0 )
916  std::cout << "Dual surface " << this_hp << ", hp_untreated list size = " << hp_untreated.size() << "."
917  << std::endl;
918 
919  // get the 2nd order adjacencies through lower dimension
920  tmp_range.clear();
921  tmp_star.clear();
922  star.clear();
923  result = mtu.get_bridge_adjacencies( this_ent, dim - 1, dim, star );RR;
924 
925  // get the bridge adjacencies through higher dimension
926  result = mtu.get_bridge_adjacencies( this_ent, dim + 1, dim, tmp_star );RR;
927  tmp_range = subtract( star, tmp_star );
928 
929  for( Range::iterator rit = tmp_range.begin(); rit != tmp_range.end(); ++rit )
930  {
931  if( new_hyperplane_ents.find( *rit ) != new_hyperplane_ents.end() ) continue;
932 
933  // check for tag first, 'cuz it's probably faster than checking adjacencies
934  // assign to avoid valgrind warning
935  unsigned short tmp_mark = 0x0;
936  result = mbImpl->tag_get_data( mark_tag, &( *rit ), 1, &tmp_mark );
937  if( MB_SUCCESS == result && mark_val == tmp_mark ) continue;
938 
939  // if it's on the loop, it's not eligible
940  if( 1 == dim && check_1d_loop_edge( *rit ) ) continue;
941 
942  // have one on this hp; just put it on the hp_untreated list for now,
943  // will get tagged and put in the hp set later
944  hp_untreated.push_back( *rit );
945  result = mbImpl->tag_set_data( mark_tag, &( *rit ), 1, &mark_val );
946  if( MB_SUCCESS != result ) return result;
947  }
948 
949  // end of inner loop; get the next this_ent, or set to zero
950  if( hp_untreated.empty() )
951  this_ent = 0;
952  else
953  {
954  this_ent = hp_untreated.back();
955  hp_untreated.pop_back();
956  }
957  }
958 
959  if( debug_ap )
960  {
961  std::string hp_name;
962  if( 2 == dim )
963  hp_name = "sheet";
964  else
965  hp_name = "chord";
966 
967  if( 0 == this_hp )
968  std::cout << "Constructed new " << hp_name << " with ";
969  else
970  {
971  int this_id;
972  result = mbImpl->tag_get_data( globalId_tag(), &this_hp, 1, &this_id );RR;
973  std::cout << "Added to " << hp_name << " " << this_id << " ";
974  }
975  if( dim == 2 )
976  std::cout << "edges:" << std::endl;
977  else
978  std::cout << "quads:" << std::endl;
979  std::vector< EntityHandle > pents( new_hyperplane_ents.size() );
980  result = mbImpl->tag_get_data( dualEntity_tag(), new_hyperplane_ents, &pents[0] );RR;
981  for( std::vector< EntityHandle >::iterator vit = pents.begin(); vit != pents.end(); ++vit )
982  {
983  if( vit != pents.begin() ) std::cout << ", ";
984  std::cout << mbImpl->id_from_handle( *vit );
985  }
986  std::cout << std::endl;
987  }
988 
989  if( 0 == this_hp )
990  {
991  // ok, doesn't have one; make a new hyperplane
992  int new_id = -1;
993  result = construct_new_hyperplane( dim, this_hp, new_id );
994  if( MB_SUCCESS != result ) return result;
995 
996  if( debug_ap )
997  {
998  std::cout << "New ";
999  if( 2 == dim )
1000  std::cout << " sheet ";
1001  else
1002  std::cout << " chord ";
1003  std::cout << new_id << " constructed." << std::endl;
1004  }
1005  }
1006 
1007  // set the hp_val for entities which didn't have one before
1008  std::vector< EntityHandle > hp_tags( new_hyperplane_ents.size() );
1009  std::fill( hp_tags.begin(), hp_tags.end(), this_hp );
1010  result = mbImpl->tag_set_data( hp_tag, new_hyperplane_ents, &hp_tags[0] );
1011  if( MB_SUCCESS != result ) return result;
1012  result = mbImpl->add_entities( this_hp, new_hyperplane_ents );
1013  if( MB_SUCCESS != result ) return result;
1014 
1015  // unmark the entities by removing the tag
1016  result = mbImpl->tag_delete( mark_tag );
1017  if( MB_SUCCESS != result ) return result;
1018 
1019  return MB_SUCCESS;
1020 }
1021 
1023 {
1024  // re-order the 1cells in the set so they are in order along the chord
1025  // start by finding the vertex dual to a quad
1026  Range verts, one_cells;
1027  ErrorCode result = mbImpl->get_entities_by_dimension( chord_set, 1, one_cells );
1028  if( MB_SUCCESS != result || one_cells.empty() ) return MB_FAILURE;
1029 
1030  result = mbImpl->get_adjacencies( one_cells, 0, false, verts, Interface::UNION );
1031  if( MB_SUCCESS != result || verts.empty() ) return MB_FAILURE;
1032 
1033  EntityHandle last_vert = 0;
1034  for( Range::iterator rit = verts.begin(); rit != verts.end(); ++rit )
1035  {
1036  if( TYPE_FROM_HANDLE( get_dual_entity( *rit ) ) == MBQUAD )
1037  {
1038  last_vert = *rit;
1039  break;
1040  }
1041  }
1042  // if there's no vertex owned by a quad, just start with 1st one
1043  if( 0 == last_vert ) last_vert = *verts.begin();
1044 
1045  // now, skip from vertex to vertex, building a list of 1cells
1046  std::vector< EntityHandle > ordered_1cells;
1047  EntityHandle last_1cell = 0;
1048  Range dum1, dum2;
1049  const EntityHandle* connect;
1050  int num_connect;
1051  ErrorCode tmp_result = MB_SUCCESS;
1052  while( ordered_1cells.size() != one_cells.size() )
1053  {
1054  dum1 = one_cells;
1055  result = mbImpl->get_adjacencies( &last_vert, 1, 1, false, dum1 );
1056  if( 0 != last_1cell ) dum1.erase( last_1cell );
1057  // assert(1 == dum1.size());
1058  if( 0 != last_1cell && 1 != dum1.size() )
1059  {
1060  std::cerr << "unexpected size traversing chord." << std::endl;
1061  tmp_result = MB_FAILURE;
1062  }
1063 
1064  last_1cell = *dum1.begin();
1065  ordered_1cells.push_back( last_1cell );
1066  result = mbImpl->get_connectivity( last_1cell, connect, num_connect );RR;
1067  if( last_vert == connect[0] )
1068  last_vert = connect[1];
1069  else
1070  last_vert = connect[0];
1071  }
1072 
1073  // now have the 1cells in order, replace them in the set
1074  if( MB_SUCCESS == tmp_result )
1075  {
1076  result = mbImpl->remove_entities( chord_set, one_cells );RR;
1077  result = mbImpl->add_entities( chord_set, &ordered_1cells[0], ordered_1cells.size() );RR;
1078  }
1079 
1080  return MB_SUCCESS;
1081 }
1082 
1083 ErrorCode DualTool::construct_new_hyperplane( const int dim, EntityHandle& new_hyperplane, int& id )
1084 {
1085  ErrorCode result;
1086  if( 1 == dim )
1087  result = mbImpl->create_meshset( ( MESHSET_ORDERED | MESHSET_TRACK_OWNER ), new_hyperplane );
1088  else
1089  result = mbImpl->create_meshset( ( MESHSET_SET | MESHSET_TRACK_OWNER ), new_hyperplane );
1090  if( MB_SUCCESS != result ) return result;
1091 
1092  if( -1 == id )
1093  {
1094  Range all_hyperplanes;
1095  result = get_dual_hyperplanes( mbImpl, dim, all_hyperplanes );RR;
1096  std::vector< int > gids( all_hyperplanes.size() );
1097  result = mbImpl->tag_get_data( globalIdTag, all_hyperplanes, ( gids.empty() ) ? NULL : &gids[0] );RR;
1098  for( unsigned int i = 0; i < gids.size(); i++ )
1099  if( gids[i] > id ) id = gids[i];
1100  id++;
1101  if( 0 == id ) id++;
1102  }
1103 
1104  result = mbImpl->tag_set_data( globalId_tag(), &new_hyperplane, 1, &id );RR;
1105  Tag hp_tag = ( 1 == dim ? dualCurve_tag() : dualSurface_tag() );
1106  result = mbImpl->tag_set_data( hp_tag, &new_hyperplane, 1, &new_hyperplane );RR;
1107 
1108  // assign a category name to these sets
1109  static const char dual_category_names[2][CATEGORY_TAG_SIZE] = { "Chord\0", "Sheet\0" };
1110 
1111  result = mbImpl->tag_set_data( categoryTag, &new_hyperplane, 1, dual_category_names[dim - 1] );
1112 
1113  return result;
1114 }
1115 
1117 {
1118  // make sure it's an edge
1119  if( MBEDGE != mbImpl->type_from_handle( this_ent ) ) return false;
1120 
1121  // also has to be a dual entity
1122  unsigned int dum;
1123  ErrorCode result = mbImpl->tag_get_data( isDualCell_tag(), &this_ent, 1, &dum );
1124  if( MB_SUCCESS != result || dum != 0x1 ) return false;
1125 
1126  const EntityHandle* verts;
1127  EntityHandle vert_tags[2];
1128  int num_verts;
1129  result = mbImpl->get_connectivity( this_ent, verts, num_verts );
1130  if( MB_SUCCESS != result ) return false;
1131 
1132  result = mbImpl->tag_get_data( dualEntity_tag(), verts, 2, vert_tags );
1133  if( MB_SUCCESS != result || mbImpl->type_from_handle( vert_tags[0] ) != MBQUAD ||
1134  mbImpl->type_from_handle( vert_tags[1] ) != MBQUAD )
1135  return false;
1136 
1137  else
1138  return true;
1139 }
1140 
1142 {
1143  Range dual_surfs, dual_cells, dual_edges;
1144  ErrorCode result = this->get_dual_hyperplanes( mbImpl, 2, dual_surfs );
1145  if( MB_SUCCESS != result || dual_surfs.empty() ) return result;
1146  std::vector< EntityHandle > dual_curve_sets;
1147 
1148  for( Range::iterator surf_it = dual_surfs.begin(); surf_it != dual_surfs.end(); ++surf_it )
1149  {
1150  // get all the cells, edges in those cells, and chords for those edges
1151  dual_cells.clear();
1152  result = mbImpl->get_entities_by_handle( *surf_it, dual_cells );
1153  if( MB_SUCCESS != result ) return result;
1154  dual_edges.clear();
1155  result = mbImpl->get_adjacencies( dual_cells, 1, false, dual_edges, Interface::UNION );
1156  if( MB_SUCCESS != result ) return result;
1157  dual_curve_sets.resize( dual_edges.size() );
1158  result = mbImpl->tag_get_data( dualCurve_tag(), dual_edges, &dual_curve_sets[0] );
1159  if( MB_SUCCESS != result ) return result;
1160 
1161  // reuse dual_cells to get unique list of chord sets
1162  dual_cells.clear();
1163  for( unsigned int i = 0; i < dual_edges.size(); i++ )
1164  if( dual_curve_sets[i] != 0 ) dual_cells.insert( dual_curve_sets[i] );
1165 
1166  // now connect up this dual surf with all the 1d ones
1167  for( Range::iterator rit = dual_cells.begin(); rit != dual_cells.end(); ++rit )
1168  {
1169  result = mbImpl->add_parent_child( *surf_it, *rit );
1170  if( MB_SUCCESS != result ) return result;
1171  }
1172  }
1173 
1174  return MB_SUCCESS;
1175 }
1176 
1178  std::vector< int >& npts,
1179  std::vector< GraphicsPoint >& points )
1180 {
1181  // shouldn't be a set
1182  assert( MBENTITYSET != mbImpl->type_from_handle( dual_ent ) );
1183 
1184  // get the graphics points comprising the given entity
1185  GraphicsPoint gp_array[DualTool::GP_SIZE];
1186 
1187  ErrorCode result = MB_SUCCESS;
1188 
1189  // switch based on topological dimension
1190  switch( mbImpl->dimension_from_handle( dual_ent ) )
1191  {
1192  case 0:
1193  // just return the vertex point
1194  result = mbImpl->tag_get_data( dualGraphicsPoint_tag(), &dual_ent, 1, gp_array );
1195  if( MB_SUCCESS == result ) points.push_back( gp_array[0] );
1196 
1197  break;
1198 
1199  case 1:
1200  // get my graphics point then those of my vertices
1201  const EntityHandle* connect;
1202  int num_connect;
1203  result = mbImpl->get_connectivity( dual_ent, connect, num_connect );
1204  if( MB_SUCCESS != result ) break;
1205 
1206  result = mbImpl->tag_get_data( dualGraphicsPoint_tag(), connect, 2, gp_array );
1207  if( MB_SUCCESS == result )
1208  {
1209  points.push_back( gp_array[0] );
1210  points.push_back( gp_array[0] );
1211  points.push_back( gp_array[1] );
1212  result = mbImpl->tag_get_data( dualGraphicsPoint_tag(), &dual_ent, 1, gp_array );
1213  if( MB_SUCCESS == result ) points[1] = gp_array[0];
1214  }
1215 
1216  npts.push_back( 3 );
1217 
1218  break;
1219 
1220  case 2:
1221  result = get_cell_points( dual_ent, npts, points );
1222  break;
1223  }
1224 
1225  return result;
1226 }
1227 
1229  std::vector< int >& npts,
1230  std::vector< GraphicsPoint >& points )
1231 {
1232  assert( MBPOLYGON == mbImpl->type_from_handle( dual_ent ) );
1233 
1234  // get the 1cells in this 2cell
1235  Range one_cells;
1236 
1237  Range tc_range;
1238  tc_range.insert( dual_ent );
1239  ErrorCode result = mbImpl->get_adjacencies( tc_range, 1, false, one_cells, Interface::UNION );RR;
1240 
1241  int num_edges = one_cells.size();
1242  std::vector< GraphicsPoint > dum_gps( num_edges + 1 );
1243 
1244  // get graphics points for 0cells and for this cell
1245  result = mbImpl->tag_get_data( dualGraphicsPoint_tag(), one_cells, &dum_gps[0] );RR;
1246  result = mbImpl->tag_get_data( dualGraphicsPoint_tag(), &dual_ent, 1, &( dum_gps[num_edges] ) );RR;
1247 
1248  Range::iterator eit;
1249  const EntityHandle* connect;
1250  int num_connect;
1251  GraphicsPoint vert_gps[2];
1252  int i;
1253  for( i = 0, eit = one_cells.begin(); i < num_edges; i++, ++eit )
1254  {
1255  // get the vertices and the graphics points for them
1256  result = mbImpl->get_connectivity( *eit, connect, num_connect );RR;
1257  result = mbImpl->tag_get_data( dualGraphicsPoint_tag(), connect, 2, vert_gps );RR;
1258 
1259  // make the 2 tris corresponding to this edge; don't worry about order
1260  // for now
1261  npts.push_back( 3 );
1262  points.push_back( dum_gps[num_edges] );
1263  points.push_back( vert_gps[0] );
1264  points.push_back( dum_gps[i] );
1265 
1266  npts.push_back( 3 );
1267  points.push_back( dum_gps[num_edges] );
1268  points.push_back( dum_gps[i] );
1269  points.push_back( vert_gps[1] );
1270  }
1271 
1272  return result;
1273 }
1274 
1276  std::vector< GraphicsPoint >& points,
1277  const bool assign_ids,
1278  const int start_id )
1279 {
1280  // return graphics points on dual entities in in_range or in entities
1281  // in sets in in_range
1282  ErrorCode result;
1283 
1284  // for each dual hyperplane set:
1286 
1287  Range two_cells, all_cells;
1288  for( rit = in_range.begin(); rit != in_range.end(); ++rit )
1289  {
1290  // for each entity:
1291  two_cells.clear();
1292  EntityType this_type = mbImpl->type_from_handle( *rit );
1293  if( MBENTITYSET == this_type )
1294  {
1295  result = mbImpl->get_entities_by_handle( *rit, two_cells );RR;
1296 
1297  std::copy( two_cells.begin(), two_cells.end(), range_inserter( all_cells ) );
1298  }
1299 
1300  else
1301  {
1302  two_cells.insert( *rit );
1303  assert( this_type == MBVERTEX || this_type == MBEDGE || this_type == MBPOLYGON ||
1304  this_type == MBPOLYHEDRON );
1305  }
1306 
1307  result = mbImpl->get_adjacencies( two_cells, 0, false, all_cells, Interface::UNION );RR;
1308  result = mbImpl->get_adjacencies( two_cells, 1, false, all_cells, Interface::UNION );RR;
1309  }
1310 
1311  // get graphics points
1312  points.resize( all_cells.size() );
1313 
1314  result = mbImpl->tag_get_data( dualGraphicsPointTag, all_cells, &points[0] );RR;
1315 
1316  if( assign_ids )
1317  {
1318  int i = start_id;
1319 
1320  for( std::vector< GraphicsPoint >::iterator vit = points.begin(); vit != points.end(); ++vit )
1321  vit->id = i++;
1322 
1323  result = mbImpl->tag_set_data( dualGraphicsPoint_tag(), all_cells, &points[0] );RR;
1324  }
1325 
1326  return result;
1327 }
1328 
1330  const EntityHandle this_v,
1331  const EntityHandle dual_surf )
1332 {
1333  // given two vertices, find the next one on the loop; if one is a dual
1334  // surface, then just choose either one for that surface
1335  assert( ( 0 == last_v || mbImpl->type_from_handle( last_v ) == MBVERTEX ) &&
1336  mbImpl->type_from_handle( this_v ) == MBVERTEX && mbImpl->type_from_handle( dual_surf ) == MBENTITYSET );
1337 
1338  // get the connected vertices
1339  MeshTopoUtil tpu( mbImpl );
1340  Range other_verts;
1341  ErrorCode result = tpu.get_bridge_adjacencies( this_v, 1, 0, other_verts );
1342  if( MB_SUCCESS != result || other_verts.empty() ) return 0;
1343 
1344  // if (mbImpl->type_from_handle(last_v) == MBENTITYSET) {
1345  // dual surface, choose either; first get a 2cell on this surface
1346  Range tcells, tcells2, verts;
1347  result = mbImpl->get_entities_by_type( dual_surf, MBPOLYGON, tcells );
1348  if( MB_SUCCESS != result || tcells.empty() ) return 0;
1349 
1350  // ok, pay attention here: first get 2cells common to dual surface and this_v
1351  verts.insert( this_v );
1352  result = mbImpl->get_adjacencies( verts, 2, false, tcells );
1353  if( MB_SUCCESS != result || tcells.empty() ) return 0;
1354 
1355  // next get vertices common to both 2cells and subtract from other_verts; also
1356  // remove last_v if it's non-zero
1357  verts.clear();
1358  result = mbImpl->get_adjacencies( tcells, 0, false, verts );
1359  if( MB_SUCCESS != result || verts.empty() ) return 0;
1360 
1361  Range tmp_verts = subtract( other_verts, verts );
1362  other_verts.swap( tmp_verts );
1363  if( 0 != last_v ) other_verts.erase( last_v );
1364 
1365  // now get intersection of remaining vertices and 2 2cells vertices
1366  // look at each one successively; should find one, maybe not on both
1367  tmp_verts = other_verts;
1368  Range tmp_faces( *tcells.begin(), *tcells.begin() );
1369  result = mbImpl->get_adjacencies( tmp_faces, 0, false, tmp_verts );
1370  if( MB_SUCCESS == result && !tmp_verts.empty() ) return *tmp_verts.begin();
1371  tmp_faces.clear();
1372  tmp_faces.insert( *tcells.rbegin() );
1373  result = mbImpl->get_adjacencies( tmp_faces, 0, false, other_verts );
1374  if( MB_SUCCESS == result && !other_verts.empty() ) return *other_verts.begin();
1375 
1376  // if we got here, there isn't any
1377  return 0;
1378 }
1379 
1381 {
1382  // get the sheet or chord it's in
1383  std::vector< EntityHandle > adj_sets;
1384  ErrorCode result = mbImpl->get_adjacencies( &ncell, 1, 4, false, adj_sets );
1385  if( MB_SUCCESS != result ) return 0;
1386 
1387  EntityHandle dum_set;
1388  for( std::vector< EntityHandle >::iterator vit = adj_sets.begin(); vit != adj_sets.end(); ++vit )
1389  {
1390  if( mbImpl->tag_get_data( dualCurve_tag(), &( *vit ), 1, &dum_set ) != MB_TAG_NOT_FOUND ||
1391  mbImpl->tag_get_data( dualSurface_tag(), &( *vit ), 1, &dum_set ) != MB_TAG_NOT_FOUND )
1392  return *vit;
1393  }
1394 
1395  return 0;
1396 }
1397 
1398 //! set the dual surface or curve for an entity
1400  const EntityHandle dual_hyperplane,
1401  const int dual_entity_dimension )
1402 {
1403  if( 1 == dual_entity_dimension )
1404  mbImpl->tag_set_data( dualCurve_tag(), &entity, 1, &dual_hyperplane );
1405  else if( 2 == dual_entity_dimension )
1406  mbImpl->tag_set_data( dualSurface_tag(), &entity, 1, &dual_hyperplane );
1407  else
1408  return MB_INDEX_OUT_OF_RANGE;
1409 
1410  return MB_SUCCESS;
1411 }
1412 
1413 //! return the corresponding dual entity
1415 {
1416  EntityHandle dual_ent;
1417  ErrorCode result = mbImpl->tag_get_data( dualEntity_tag(), &this_ent, 1, &dual_ent );
1418  if( MB_SUCCESS != result || MB_TAG_NOT_FOUND == result )
1419  return 0;
1420  else
1421  return dual_ent;
1422 }
1423 
1424 //! return the corresponding dual entity
1426 {
1427  EntityHandle dual_ent;
1428  ErrorCode result = mbImpl->tag_get_data( extraDualEntity_tag(), &this_ent, 1, &dual_ent );
1429  if( MB_SUCCESS != result || MB_TAG_NOT_FOUND == result )
1430  return 0;
1431  else
1432  return dual_ent;
1433 }
1434 
1436 {
1437  if( debug_ap ) ( (Core*)mbImpl )->check_adjacencies();
1438 
1439  if( debug_ap )
1440  {
1441  Range sets;
1442  Tag ms_tag;
1443 
1444  ErrorCode result = mbImpl->tag_get_handle( "MATERIAL_SET", 1, MB_TYPE_INTEGER, ms_tag );
1445  if( MB_SUCCESS == result )
1446  {
1447  result = mbImpl->get_entities_by_type_and_tag( 0, MBENTITYSET, &ms_tag, NULL, 1, sets );
1448  if( MB_SUCCESS == result ) result = mbImpl->delete_entities( sets );
1449  }
1450  }
1451 
1452  std::cout << "-AP(";
1453  print_cell( odedge );
1454  std::cout << ")" << std::endl;
1455 
1456  // perform an atomic pillow operation around dedge
1457 
1458  // grab the quad before deleting the odedge
1459  quad1 = get_dual_entity( odedge );
1460  assert( 0 != quad1 );
1461 
1462  // 0. get star 2cells around odedge (before odedge changes) and 3cells around
1463  // those 2cells (going to delete the 2cells, therefore need to delete the 3cells
1464  // that depend on those too)
1465  MeshTopoUtil mtu( mbImpl );
1466  Range star_cells, tmp_cells;
1467  ErrorCode result = mbImpl->get_adjacencies( &odedge, 1, 2, false, star_cells );RR;
1468  result = mbImpl->get_adjacencies( star_cells, 3, false, tmp_cells, Interface::UNION );RR;
1469  star_cells.merge( tmp_cells );
1470  star_cells.insert( odedge );
1471 
1472  // tear down the dual entities which will be modified by the ap first
1473  result = delete_dual_entities( star_cells );RR;
1474 
1475  // now change the quad to an ap
1476  std::vector< EntityHandle > verts;
1477  result = mbImpl->get_connectivity( &quad1, 1, verts );RR;
1478 
1479  // get average position of vertices
1480  double coords[12], avg[3] = { 0.0, 0.0, 0.0 };
1481  result = mbImpl->get_coords( &verts[0], verts.size(), coords );RR;
1482  for( int i = 0; i < 4; i++ )
1483  {
1484  avg[0] += coords[3 * i];
1485  avg[1] += coords[3 * i + 1];
1486  avg[2] += coords[3 * i + 2];
1487  }
1488  for( int i = 0; i < 3; i++ )
1489  avg[i] *= 0.25;
1490 
1491  // for each position, get a corresponding position 1/2 way to avg
1492  double new_coords[12];
1493  for( int i = 0; i < 4; i++ )
1494  {
1495  new_coords[3 * i] = avg[0] + .5 * ( coords[3 * i] - avg[0] );
1496  new_coords[3 * i + 1] = avg[1] + .5 * ( coords[3 * i + 1] - avg[1] );
1497  new_coords[3 * i + 2] = avg[2] + .5 * ( coords[3 * i + 2] - avg[2] );
1498  }
1499 
1500  // make the 4 new vertices; store in vector long enough for hex connectivity
1501  for( int i = 0; i < 4; i++ )
1502  {
1503  verts.push_back( 0 );
1504  result = mbImpl->create_vertex( &new_coords[3 * i], verts[4 + i] );RR;
1505  }
1506 
1507  // get the hexes connected to the quad
1508  Range hexes;
1509  result = mbImpl->get_adjacencies( &quad1, 1, 3, false, hexes );RR;
1510  assert( hexes.size() <= 2 );
1511 
1512  // remove any explicit adjacency from the first hex, since that'll get connected
1513  // to the new quad; add adjacency between quad and second hex, if there is a 2nd
1514  result = mbImpl->remove_adjacencies( quad1, &( *hexes.begin() ), 1 );RR;
1515  if( hexes.size() == 2 )
1516  {
1517  result = mbImpl->add_adjacencies( quad1, &( *hexes.rbegin() ), 1, false );RR;
1518  }
1519 
1520  // create the new, inner quad, and make it explicitly adjacent to 1st hex;
1521  // make the connectivity of this quad same as the original one
1522  std::vector< EntityHandle > tmp_verts;
1523  std::copy( verts.begin(), verts.end(), std::back_inserter( tmp_verts ) );
1524 
1525  result = mbImpl->create_element( MBQUAD, &tmp_verts[0], 4, quad2 );RR;
1526  result = mbImpl->add_adjacencies( quad2, &( *hexes.begin() ), 1, false );RR;
1527 
1528  // reverse the connectivity of the 1st hex
1529  std::reverse( verts.begin(), verts.begin() + 4 );
1530  std::reverse( verts.begin() + 4, verts.end() );
1531 
1532  // now make two inner hexes; note connectivity array is flipped for the two hexes
1533  EntityHandle new_hexes[2];
1534  result = mbImpl->create_element( MBHEX, &verts[0], 8, new_hexes[0] );RR;
1535  result = mbImpl->create_element( MBHEX, &tmp_verts[0], 8, new_hexes[1] );RR;
1536 
1537  // set the global id tag on the new hexes
1538  int new_hex_ids[2] = { maxHexId + 1, maxHexId + 2 };
1539  maxHexId += 2;
1540  result = mbImpl->tag_set_data( globalId_tag(), new_hexes, 2, new_hex_ids );
1541  if( MB_SUCCESS != result ) return result;
1542 
1543  // by definition, quad1 is adj to new_hexes[0]
1544  result = mbImpl->add_adjacencies( quad1, &new_hexes[0], 1, false );RR;
1545  result = mbImpl->add_adjacencies( quad2, &new_hexes[1], 1, false );RR;
1546 
1547  if( debug_ap ) ( (Core*)mbImpl )->check_adjacencies();
1548 
1549  // now update the dual
1550  result = construct_hex_dual( &new_hexes[0], 2 );RR;
1551 
1552  // get the new dual surface, by getting one of the edges between the center
1553  // and outer vertex rings
1554  Range new_edge;
1555  verts[1] = verts[4];
1556  result = mbImpl->get_adjacencies( &verts[0], 2, 1, false, new_edge );
1557  if( MB_SUCCESS != result || new_edge.size() != 1 ) return result;
1558 
1559  return MB_SUCCESS;
1560 }
1561 
1562 //! effect reverse atomic pillow operation
1564 {
1565  // get the dual entities associated with elements in the pillow; go through
1566  // the elements instead of the pillow sheet so you get all of them, not just
1567  // the ones on the sheet
1568  if( debug_ap ) ( (Core*)mbImpl )->check_adjacencies();
1569 
1570  std::cout << "-AP(";
1571  print_cell( pillow );
1572  std::cout << ")" << std::endl;
1573 
1574  Range dverts;
1575  ErrorCode result = get_dual_entities( pillow, NULL, NULL, &dverts, NULL, NULL );
1576  if( MB_SUCCESS != result ) return result;
1577  assert( 2 == dverts.size() );
1578 
1579  EntityHandle hexes[2];
1580  result = mbImpl->tag_get_data( dualEntity_tag(), dverts, hexes );RR;
1581  assert( hexes[0] != 0 && hexes[1] != 0 );
1582 
1583  std::vector< EntityHandle > dcells[4];
1584  Range pcells[4];
1585  std::copy( hexes, hexes + 2, range_inserter( pcells[3] ) );
1586  std::copy( dverts.begin(), dverts.end(), std::back_inserter( dcells[0] ) );
1587  for( int dim = 0; dim <= 2; dim++ )
1588  {
1589  result = mbImpl->get_adjacencies( hexes, 2, dim, false, pcells[dim], Interface::UNION );RR;
1590  dcells[3 - dim].resize( pcells[dim].size() );
1591  result = mbImpl->tag_get_data( dualEntity_tag(), pcells[dim], &dcells[3 - dim][0] );RR;
1592  }
1593 
1594  // delete the dual entities which are part of the original pillow
1595  result = mbImpl->delete_entities( &pillow, 1 );
1596  if( MB_SUCCESS != result ) return result;
1597 
1598  result = mbImpl->delete_entities( chords );
1599  if( MB_SUCCESS != result ) return result;
1600 
1601  for( int i = 3; i >= 0; i-- )
1602  {
1603  result = delete_dual_entities( &dcells[i][0], dcells[i].size() );RR;
1604  }
1605 
1606  // delete the primal entities inserted by the ap; be careful to get the right
1607  // faces, edges and vertices
1608  Range del_faces, del_edges, del_verts, tmp_faces, tmp_verts;
1609  // faces are the shared 5 and the 1 other one with greater handle (which
1610  // we know will be later in the range)
1611  result = mbImpl->get_adjacencies( hexes, 2, 2, false, del_faces );RR;
1612  assert( 5 == del_faces.size() );
1613  std::copy( pcells[2].begin(), pcells[2].end(), range_inserter( tmp_faces ) );
1614  tmp_faces = subtract( tmp_faces, del_faces );
1615  del_faces.insert( *tmp_faces.rbegin() );
1616  result = mbImpl->get_adjacencies( tmp_faces, 0, false, tmp_verts );RR;
1617  std::copy( pcells[0].begin(), pcells[0].end(), range_inserter( del_verts ) );
1618  del_verts = subtract( del_verts, tmp_verts );
1619  assert( 4 == del_verts.size() );
1620  result = mbImpl->get_adjacencies( del_verts, 1, false, del_edges, Interface::UNION );RR;
1621  assert( 8 == del_edges.size() );
1622 
1623  result = mbImpl->delete_entities( hexes, 2 );RR;
1624  result = mbImpl->delete_entities( del_faces );RR;
1625  result = mbImpl->delete_entities( del_edges );RR;
1626  result = mbImpl->delete_entities( del_verts );RR;
1627 
1628  if( debug_ap ) ( (Core*)mbImpl )->check_adjacencies();
1629 
1630  // recompute the dual for the hexes on either side of the quad affected
1631  // by the ap removal
1632  Range tmp_hexes;
1633  result = mbImpl->get_adjacencies( tmp_verts, 3, false, tmp_hexes, Interface::UNION );RR;
1634  result = construct_hex_dual( tmp_hexes );RR;
1635 
1636  return MB_SUCCESS;
1637 }
1638 
1640 {
1641  Range tmp_ents;
1642  std::copy( entities, entities + num_entities, range_inserter( tmp_ents ) );
1643  return delete_dual_entities( tmp_ents );
1644 }
1645 
1647 {
1648  if( entities.empty() ) return delete_whole_dual();
1649 
1650  EntityHandle null_entity = 0;
1651  ErrorCode result;
1652  Range ents_to_delete;
1653 
1654  while( !entities.empty() )
1655  {
1656  EntityHandle this_entity = entities.pop_back();
1657 
1658  // reset the primal's dual entity
1659  EntityHandle primal = get_dual_entity( this_entity );
1660  if( get_dual_entity( primal ) == this_entity )
1661  {
1662  result = mbImpl->tag_set_data( dualEntity_tag(), &primal, 1, &null_entity );RR;
1663  }
1664  EntityHandle extra = get_extra_dual_entity( primal );
1665  if( 0 != extra )
1666  {
1667  result = mbImpl->tag_set_data( extraDualEntity_tag(), &primal, 1, &null_entity );RR;
1668  }
1669 
1670  ents_to_delete.insert( this_entity );
1671 
1672  // check for extra dual entities
1673  if( mbImpl->type_from_handle( this_entity ) == MBPOLYGON )
1674  {
1675  // for 2cell, might be a loop edge
1676  Range loop_edges;
1677  result = mbImpl->get_adjacencies( &this_entity, 1, 1, false, loop_edges );
1678  for( Range::iterator rit = loop_edges.begin(); rit != loop_edges.end(); ++rit )
1679  if( check_1d_loop_edge( *rit ) ) entities.insert( *rit );
1680  }
1681  else if( extra && extra != this_entity )
1682  // just put it on the list; primal for which we're extra has already been
1683  // reset to not point to extra entity
1684  ents_to_delete.insert( extra );
1685  }
1686 
1687  // now delete the entities (sheets and chords will be updated automatically)
1688  return mbImpl->delete_entities( ents_to_delete );
1689 }
1690 
1692 {
1693  const EntityHandle* connect;
1694  int num_connect;
1695  ErrorCode result = mbImpl->get_connectivity( cell, connect, num_connect );
1696  if( MB_SUCCESS != result ) return;
1697  bool first = true;
1698  EntityHandle primals[20];
1699  std::vector< int > ids;
1700 
1701  assert( num_connect < 20 );
1702  result = mbImpl->tag_get_data( dualEntityTag, connect, num_connect, primals );
1703  if( MB_SUCCESS != result ) return;
1704  ids.resize( num_connect );
1705  result = mbImpl->tag_get_data( globalIdTag, primals, num_connect, &ids[0] );
1706  if( MB_SUCCESS != result ) return;
1707  for( int i = 0; i < num_connect; i++ )
1708  {
1709  if( !first ) std::cout << "-";
1710  EntityType this_type = mbImpl->type_from_handle( primals[i] );
1711  if( this_type == MBHEX )
1712  std::cout << "h";
1713  else if( this_type == MBQUAD )
1714  std::cout << "f";
1715  else
1716  std::cout << "u";
1717 
1718  if( ids[i] != 0 )
1719  std::cout << ids[i];
1720  else
1721  std::cout << mbImpl->id_from_handle( primals[i] );
1722 
1723  first = false;
1724  }
1725 }
1726 
1728 {
1729  if( debug_ap ) ( (Core*)mbImpl )->check_adjacencies();
1730 
1731  MeshTopoUtil mtu( mbImpl );
1732 
1733  std::cout << "OC(";
1734  print_cell( ocl );
1735  std::cout << ")-(";
1736  print_cell( ocr );
1737  std::cout << ")" << std::endl;
1738 
1739  // get the primal entities we're dealing with
1740  EntityHandle split_quads[2] = { 0 }, split_edges[3] = { 0 }, split_nodes[2] = { 0 }, other_edges[6] = { 0 },
1741  other_nodes[6] = { 0 };
1742  Range hexes;
1743  ErrorCode result = foc_get_ents( ocl, ocr, split_quads, split_edges, split_nodes, hexes, other_edges, other_nodes );RR;
1744 
1745  // get star entities around edges, separated into halves
1746  std::vector< EntityHandle > star_dp1[2], star_dp2[2];
1747  result = foc_get_stars( split_quads, split_edges, star_dp1, star_dp2 );RR;
1748 
1749  if( MBQUAD != mbImpl->type_from_handle( split_quads[0] ) || MBQUAD != mbImpl->type_from_handle( split_quads[1] ) )
1750  return MB_TYPE_OUT_OF_RANGE;
1751 
1752  result = foc_delete_dual( split_quads, split_edges, hexes );
1753  if( MB_SUCCESS != result ) return result;
1754 
1755  EntityHandle new_quads[2], new_edges[3], new_nodes[2];
1756  result = split_pair_nonmanifold( split_quads, split_edges, split_nodes, star_dp1, star_dp2, other_edges,
1757  other_nodes, new_quads, new_edges, new_nodes );
1758  if( MB_SUCCESS != result ) return result;
1759 
1760  // now merge entities, the C of foc
1761  EntityHandle keepit, deleteit;
1762 #define MIN( a, b ) ( ( a ) < ( b ) ? ( a ) : ( b ) )
1763 #define MAX( a, b ) ( ( a ) > ( b ) ? ( a ) : ( b ) )
1764 #define KEEP_DELETE( a, b, c, d ) \
1765  { \
1766  ( c ) = MIN( a, b ); \
1767  ( d ) = MAX( a, b ); \
1768  }
1769 
1770  // find how many shared edges there were
1771  int num_shared_edges = ( split_edges[2] ? 3 : ( split_edges[1] ? 2 : 1 ) );
1772 
1773  // first the node(s)
1774  for( int i = 0; i < 3 - num_shared_edges; i++ )
1775  {
1776  KEEP_DELETE( other_nodes[2 + 2 * i], other_nodes[3 + 2 * i], keepit, deleteit );
1777  result = mbImpl->merge_entities( keepit, deleteit, false, true );RR;
1778  }
1779 
1780  // now the edges
1781  for( int i = 0; i < 4 - num_shared_edges; i++ )
1782  {
1783  KEEP_DELETE( other_edges[2 * i], other_edges[2 * i + 1], keepit, deleteit );
1784  result = mbImpl->merge_entities( keepit, deleteit, false, true );RR;
1785  }
1786 
1787  // now the faces
1788  KEEP_DELETE( split_quads[0], split_quads[1], keepit, deleteit );
1789  result = mbImpl->merge_entities( keepit, deleteit, false, true );RR;
1790 
1791  result = mbImpl->merge_entities( new_quads[0], new_quads[1], false, true );RR;
1792 
1793  if( debug_ap ) ( (Core*)mbImpl )->check_adjacencies();
1794 
1795  // reconstruct dual
1796  result = construct_hex_dual( hexes );
1797  if( MB_SUCCESS != result ) return result;
1798 
1799  return check_dual_adjs();
1800 }
1801 
1803  EntityHandle ocr,
1804  EntityHandle* split_quads,
1805  EntityHandle* split_edges,
1806  EntityHandle* split_nodes,
1807  Range& hexes,
1808  EntityHandle* other_edges,
1809  EntityHandle* other_nodes )
1810 {
1811  // get the entities used for foc; ocl and ocr are dual 1-cells
1812  // representing quads to be split; returned from this function:
1813  // quads[2] - 2 quads to be split
1814  // split_edges[2] - edge(s) to be split (2nd is 0 if only one)
1815  // split_node - node to be split, if any (otherwise 0)
1816  // hexes - connected hexes to split_edges
1817  // other_edges[0], [1] - edges in quads[0] and [1] sharing node with
1818  // one end of split_edges[0]
1819  // other_edges[2], [3] - other end of split_edges[0] (or [1] if 2
1820  // split_edges)
1821  // other_edges[4], [5] - edges in quads[0], [1] opposite to split_edges[0]
1822  // other_nodes[0], [1] - nodes on other_edges[0], [1] not shared with
1823  // split_edges[0]
1824  // other_nodes[2], [3] - nodes on other_edges[2], [3] not shared with
1825  // split_edges[0] (if 2 split_edges, there's only 1 opposite node
1826  // in each split quad)
1827  // (for diagram, see Tim's notes from 11/12/07)
1828 
1829  split_quads[0] = get_dual_entity( ocl );
1830  split_quads[1] = get_dual_entity( ocr );
1831  if( MBQUAD != mbImpl->type_from_handle( split_quads[0] ) || MBQUAD != mbImpl->type_from_handle( split_quads[1] ) )
1832  return MB_TYPE_OUT_OF_RANGE;
1833 
1834  Range common_edges;
1835  ErrorCode result = mbImpl->get_adjacencies( split_quads, 2, 1, false, common_edges );
1836  if( MB_SUCCESS != result ) return result;
1837 
1838  if( common_edges.empty() ) return MB_FAILURE;
1839  for( unsigned int i = 0; i < common_edges.size(); i++ )
1840  split_edges[i] = common_edges[i];
1841 
1842  MeshTopoUtil mtu( mbImpl );
1843 
1844  if( common_edges.size() == 3 )
1845  {
1846  // find other (non-shared) edges
1847  for( int i = 0; i < 2; i++ )
1848  {
1849  Range tmp_edges;
1850  result = mbImpl->get_adjacencies( &split_quads[i], 1, 1, false, tmp_edges );
1851  if( MB_SUCCESS != result ) return result;
1852  tmp_edges = subtract( tmp_edges, common_edges );
1853  assert( tmp_edges.size() == 1 );
1854  other_edges[i] = *tmp_edges.begin();
1855  }
1856  assert( other_edges[0] && other_edges[1] && other_edges[0] != other_edges[1] );
1857 
1858  // arrange common edges so middle is in middle
1859  result = mtu.opposite_entity( split_quads[0], other_edges[0], split_edges[1] );RR;
1860  common_edges.erase( split_edges[1] );
1861  split_edges[0] = *common_edges.begin();
1862  split_edges[2] = *common_edges.rbegin();
1863  common_edges.insert( split_edges[1] );
1864 
1865  // get split nodes and other nodes
1866  split_nodes[0] = mtu.common_entity( split_edges[0], split_edges[1], 0 );
1867  split_nodes[1] = mtu.common_entity( split_edges[2], split_edges[1], 0 );
1868  other_nodes[0] = mtu.common_entity( split_edges[0], other_edges[0], 0 );
1869  other_nodes[1] = mtu.common_entity( split_edges[2], other_edges[1], 0 );
1870 
1871  assert( other_nodes[0] && other_nodes[1] && split_nodes[0] && split_nodes[1] );
1872  assert( split_edges[0] && split_edges[1] && split_edges[2] && split_edges[0] != split_edges[1] &&
1873  split_edges[1] != split_edges[2] && split_edges[0] != split_edges[2] );
1874  }
1875  else if( common_edges.size() == 2 )
1876  {
1877  // split node is shared by split edges
1878  split_nodes[0] = mtu.common_entity( split_edges[0], split_edges[1], 0 );
1879  if( 0 == split_nodes[0] ) return MB_FAILURE;
1880  // first two other nodes are on split edges opposite split node
1881  result = mtu.opposite_entity( split_edges[0], split_nodes[0], other_nodes[0] );RR;
1882  result = mtu.opposite_entity( split_edges[1], split_nodes[0], other_nodes[1] );RR;
1883  // over split quads:
1884  for( int i = 0; i < 2; i++ )
1885  {
1886  // 1st other edge is opposite second split edge
1887  result = mtu.opposite_entity( split_quads[i], split_edges[1], other_edges[i] );RR;
1888  // 2nd other edge is opposite first split edge
1889  result = mtu.opposite_entity( split_quads[i], split_edges[0], other_edges[2 + i] );RR;
1890  // last other node is opposite split node on split quad
1891  result = mtu.opposite_entity( split_quads[i], split_nodes[0], other_nodes[2 + i] );RR;
1892  }
1893  }
1894  else
1895  {
1896  const EntityHandle* connect;
1897  int num_connect;
1898  result = mbImpl->get_connectivity( split_edges[0], connect, num_connect );
1899  if( MB_SUCCESS != result ) return result;
1900  // other_nodes[0], [1] are on split edge
1901  other_nodes[0] = connect[0];
1902  other_nodes[1] = connect[1];
1903 
1904  // for each of the split quads
1905  for( int i = 0; i < 2; i++ )
1906  {
1907  // get the other edge on the split quad adj to node 0 on the split edge, by getting
1908  // edges adj to split quad and node and removing split edge; that's other_edge[i]
1909  Range tmp_range1, tmp_range2;
1910  tmp_range1.insert( connect[0] );
1911  tmp_range1.insert( split_quads[i] );
1912  result = mbImpl->get_adjacencies( tmp_range1, 1, false, tmp_range2 );
1913  if( MB_SUCCESS != result ) return result;
1914  tmp_range2.erase( split_edges[0] );
1915  assert( tmp_range2.size() == 1 );
1916  other_edges[i] = *tmp_range2.begin();
1917  // get edge connected to other node on split edge & split quad; that's
1918  // opposite prev other_edges on the split quad; that's other_edges[4+i]
1919  result = mtu.opposite_entity( split_quads[i], other_edges[i], other_edges[4 + i] );RR;
1920  // get the edge on the split quad opposite the split edge; that's other_edges[2+i]
1921  result = mtu.opposite_entity( split_quads[i], split_edges[0], other_edges[2 + i] );RR;
1922  // get nodes on other side of split quad from split edge, by getting common
1923  // node between top/bottom edge and opposite edge
1924  other_nodes[2 + i] = mtu.common_entity( other_edges[i], other_edges[2 + i], 0 );
1925  other_nodes[4 + i] = mtu.common_entity( other_edges[4 + i], other_edges[2 + i], 0 );
1926  if( 0 == other_nodes[2 + i] || 0 == other_nodes[4 + i] ) return MB_FAILURE;
1927  }
1928  }
1929 
1930  result = mbImpl->get_adjacencies( split_edges, common_edges.size(), 3, false, hexes, Interface::UNION );
1931  if( MB_SUCCESS != result ) return result;
1932 
1933  assert( "split node not in other_nodes" && other_nodes[0] != split_nodes[0] && other_nodes[0] != split_nodes[1] &&
1934  other_nodes[1] != split_nodes[0] && other_nodes[1] != split_nodes[1] );
1935  assert( "each split node on an end of a split edge" && mtu.common_entity( other_nodes[0], split_edges[0], 0 ) &&
1936  ( ( ( split_edges[2] && mtu.common_entity( other_nodes[1], split_edges[2], 0 ) ) ||
1937  ( split_edges[1] && mtu.common_entity( other_nodes[1], split_edges[1], 0 ) ) ||
1938  mtu.common_entity( other_nodes[1], split_edges[0], 0 ) ) ) );
1939  assert( "opposite other edges meet at an other node" &&
1940  ( mtu.common_entity( other_edges[0], other_edges[1], 0 ) == other_nodes[0] ||
1941  ( split_edges[2] && mtu.common_entity( other_edges[0], other_edges[1], 0 ) == other_nodes[1] ) ) &&
1942  ( split_edges[2] ||
1943  ( split_edges[1] && mtu.common_entity( other_edges[2], other_edges[3], 0 ) == other_nodes[1] ) ||
1944  mtu.common_entity( other_edges[4], other_edges[5], 0 ) == other_nodes[1] ) );
1945 
1946  return MB_SUCCESS;
1947 }
1948 
1950  EntityHandle* split_edges,
1951  EntityHandle* split_nodes,
1952  std::vector< EntityHandle >* star_dp1,
1953  std::vector< EntityHandle >* star_dp2,
1954  EntityHandle* /*other_edges*/,
1955  EntityHandle* /*other_nodes*/,
1956  EntityHandle* new_quads,
1957  EntityHandle* new_edges,
1958  EntityHandle* new_nodes )
1959 {
1960 
1961  // if there's a bdy in the star around the shared edge(s), get the quads on that
1962  // bdy so we know which edges to merge after the split-nonmanifold
1963  MeshTopoUtil mtu( mbImpl );
1964  ErrorCode result;
1965 
1966  // get which star the split faces are in, and choose the other one
1967  int new_side = -1;
1968  if( std::find( star_dp1[0].begin(), star_dp1[0].end(), split_quads[0] ) != star_dp1[0].end() )
1969  new_side = 1;
1970  else if( std::find( star_dp1[1].begin(), star_dp1[1].end(), split_quads[0] ) != star_dp1[1].end() )
1971  new_side = 0;
1972  assert( -1 != new_side );
1973  if( -1 == new_side ) return MB_FAILURE;
1974 
1975  //=============== split faces
1976 
1977  for( int i = 0; i < 2; i++ )
1978  {
1979  // get a hex in star_dp2[new_side] that's adj to this split quad, to tell
1980  // mtu which one the new quad should go with; there should be at least one,
1981  // if we have any hexes connected to the split quad
1982  EntityHandle gowith_hex = 0;
1983  for( std::vector< EntityHandle >::iterator vit = star_dp2[new_side].begin(); vit != star_dp2[new_side].end();
1984  ++vit )
1985  {
1986  if( mtu.common_entity( *vit, split_quads[i], 2 ) )
1987  {
1988  gowith_hex = *vit;
1989  break;
1990  }
1991  }
1992  assert( 0 != gowith_hex );
1993 
1994  // split manifold each of the split_quads, and put the results on the merge list
1995  result =
1996  mtu.split_entities_manifold( split_quads + i, 1, new_quads + i, NULL, ( gowith_hex ? &gowith_hex : NULL ) );RR;
1997  }
1998 
1999  // make ranges of faces which need to be explicitly adj to old, new
2000  // edge; faces come from stars and new_quads (which weren't in the stars);
2001  // new_quads go with side j, which does not have split quads
2002  Range tmp_addl_faces[2], addl_faces[2];
2003  for( int i = 0; i < 2; i++ )
2004  {
2005  std::copy( star_dp1[i].begin(), star_dp1[i].end(), range_inserter( tmp_addl_faces[i] ) );
2006  tmp_addl_faces[new_side].insert( new_quads[i] );
2007  }
2008 #ifndef NDEBUG
2009  bool cond1 = ( "split_quads on 1, new_quads on 0" &&
2010  tmp_addl_faces[0].find( split_quads[0] ) == tmp_addl_faces[0].end() &&
2011  tmp_addl_faces[0].find( split_quads[1] ) == tmp_addl_faces[0].end() &&
2012  tmp_addl_faces[1].find( split_quads[0] ) != tmp_addl_faces[1].end() &&
2013  tmp_addl_faces[1].find( split_quads[1] ) != tmp_addl_faces[1].end() &&
2014  tmp_addl_faces[0].find( new_quads[0] ) != tmp_addl_faces[0].end() &&
2015  tmp_addl_faces[0].find( new_quads[1] ) != tmp_addl_faces[0].end() &&
2016  tmp_addl_faces[1].find( new_quads[0] ) == tmp_addl_faces[1].end() &&
2017  tmp_addl_faces[1].find( new_quads[1] ) == tmp_addl_faces[1].end() ),
2018  cond2 = ( "split_quads on 0, new_quads on 1" &&
2019  tmp_addl_faces[0].find( split_quads[0] ) != tmp_addl_faces[0].end() &&
2020  tmp_addl_faces[0].find( split_quads[1] ) != tmp_addl_faces[0].end() &&
2021  tmp_addl_faces[1].find( split_quads[0] ) == tmp_addl_faces[1].end() &&
2022  tmp_addl_faces[1].find( split_quads[1] ) == tmp_addl_faces[1].end() &&
2023  tmp_addl_faces[0].find( new_quads[0] ) == tmp_addl_faces[0].end() &&
2024  tmp_addl_faces[0].find( new_quads[1] ) == tmp_addl_faces[0].end() &&
2025  tmp_addl_faces[1].find( new_quads[0] ) != tmp_addl_faces[1].end() &&
2026  tmp_addl_faces[1].find( new_quads[1] ) != tmp_addl_faces[1].end() );
2027 
2028  assert( cond1 || cond2 );
2029 #endif
2030 
2031  //=============== split edge(s)
2032  for( int j = 0; j < 3; j++ )
2033  {
2034  if( !split_edges[j] ) break;
2035 
2036  // filter add'l faces to only those adj to split_edges[j]
2037  addl_faces[0] = tmp_addl_faces[0];
2038  addl_faces[1] = tmp_addl_faces[1];
2039  for( int i = 0; i < 2; i++ )
2040  {
2041  result = mbImpl->get_adjacencies( &split_edges[j], 1, 2, false, addl_faces[i] );RR;
2042  }
2043 
2044  // split edge
2045  result = mtu.split_entity_nonmanifold( split_edges[j], addl_faces[1 - new_side], addl_faces[new_side],
2046  new_edges[j] );RR;
2047  }
2048 
2049  //=============== split node(s)
2050 
2051  for( int j = 0; j < 2; j++ )
2052  {
2053  if( !split_nodes[j] ) break;
2054 
2055  // if we're splitting multiple edges, there might be other edges that have the split
2056  // node; also need to know which side they're on
2057  Range tmp_addl_edges[2];
2058  result = foc_get_addl_ents( star_dp1, star_dp2, split_edges, split_nodes[j], tmp_addl_edges );RR;
2059 
2060  // also, we need to know which of the split/new edges go
2061  // with the split/new node; new edges go with side 0, split with 1
2062  for( int i = 0; i < 3; i++ )
2063  {
2064  if( !split_edges[i] ) break;
2065  tmp_addl_edges[new_side].insert( new_edges[i] );
2066  tmp_addl_edges[1 - new_side].insert( split_edges[i] );
2067  }
2068 
2069  // same for star faces and hexes
2070  for( int i = 0; i < 2; i++ )
2071  {
2072  std::copy( star_dp1[i].begin(), star_dp1[i].end(), range_inserter( tmp_addl_edges[i] ) );
2073  std::copy( star_dp2[i].begin(), star_dp2[i].end(), range_inserter( tmp_addl_edges[i] ) );
2074  }
2075 
2076  // finally, new quads
2077  for( int i = 0; i < 2; i++ )
2078  tmp_addl_edges[new_side].insert( new_quads[i] );
2079 
2080  // filter the entities, keeping only the ones adjacent to this node
2081  Range addl_edges[2];
2082  for( int i = 0; i < 2; i++ )
2083  {
2084  for( Range::reverse_iterator rit = tmp_addl_edges[i].rbegin(); rit != tmp_addl_edges[i].rend(); ++rit )
2085  {
2086  if( mtu.common_entity( *rit, split_nodes[j], 0 ) ) addl_edges[i].insert( *rit );
2087  }
2088  }
2089 
2090  // now split the node too
2091  result = mtu.split_entity_nonmanifold( split_nodes[j], addl_edges[1 - new_side], addl_edges[new_side],
2092  new_nodes[j] );RR;
2093  }
2094 
2095  return MB_SUCCESS;
2096 }
2097 
2098 ErrorCode DualTool::foc_get_addl_ents( std::vector< EntityHandle >* star_dp1,
2099  std::vector< EntityHandle >* /*star_dp2*/,
2100  EntityHandle* split_edges,
2101  EntityHandle split_node,
2102  Range* addl_ents )
2103 {
2104  // if we're splitting 2 edges, there might be other edges that have the split
2105  // node; also need to know which side they're on
2106 
2107  // algorithm: for a given star_dp1 (faces) on a side:
2108  // - get all edges adj to all faces -> R1
2109  // - get all edges adj to split_node -> R2
2110  // - R3 = R1 & R2 (edges connected to split_node & adj to a star face)
2111  // - R3 -= split_edges (take split edges off addl_ents)
2112 
2113  Range R2;
2114  MeshTopoUtil mtu( mbImpl );
2115  ErrorCode result = mbImpl->get_adjacencies( &split_node, 1, 1, false, R2 );RR;
2116  Range::iterator rit;
2117 
2118  for( int i = 0; i < 2; i++ )
2119  {
2120  Range R1, R3;
2121  result = mbImpl->get_adjacencies( &star_dp1[i][0], star_dp1[i].size(), 1, false, R1, Interface::UNION );RR;
2122  R3 = intersect( R1, R2 );
2123  for( int j = 0; j < 3; j++ )
2124  if( split_edges[j] ) R3.erase( split_edges[j] );
2125  addl_ents[i].merge( R3 );
2126  }
2127 
2128  return MB_SUCCESS;
2129 }
2130 
2132  EntityHandle* split_edges,
2133  std::vector< EntityHandle >* star_dp1,
2134  std::vector< EntityHandle >* star_dp2 )
2135 {
2136  bool on_bdy = false, on_bdy_tmp;
2137  ErrorCode result;
2138  MeshTopoUtil mtu( mbImpl );
2139 
2140  // get the star around the split_edge
2141  std::vector< EntityHandle > qstar, hstar;
2142  unsigned int qpos = 0;
2143 
2144  for( int i = 0; i < 3; i++ )
2145  {
2146  if( !split_edges[i] ) break;
2147 
2148  // get the star around this split edge
2149  unsigned int qpos_tmp = 0;
2150  std::vector< EntityHandle > qstar_tmp, hstar_tmp;
2151  result = mtu.star_entities( split_edges[i], qstar_tmp, on_bdy_tmp, 0, &hstar_tmp );RR;
2152  // if we're on the bdy, add a null to the hex star too
2153  if( on_bdy_tmp )
2154  {
2155  assert( hstar_tmp.size() == qstar_tmp.size() - 1 );
2156  hstar_tmp.push_back( 0 );
2157  on_bdy = true;
2158  }
2159 
2160  // get the position of first split quad in star
2161  while( qpos_tmp < qstar_tmp.size() && qstar_tmp[qpos_tmp] != split_quads[0] )
2162  qpos_tmp++;
2163  if( qpos_tmp == qstar_tmp.size() ) return MB_FAILURE;
2164 
2165  bool forward;
2166  // 1st iteration is forward by definition
2167  if( 0 == i ) forward = true;
2168 
2169  // need to be careful about direction on later iters
2170  else if( hstar[qpos] == hstar_tmp[qpos_tmp] )
2171  forward = true;
2172  else if( hstar[qpos] == hstar_tmp[( qpos_tmp + qstar_tmp.size() - 1 ) % qstar_tmp.size()] &&
2173  hstar_tmp[qpos_tmp] == hstar[( qpos + qstar.size() - 1 ) % qstar.size()] )
2174  forward = false;
2175  else
2176  return MB_FAILURE;
2177 
2178  if( forward )
2179  {
2180  // 1st half of star
2181  // save hex right after split_quad[0] first
2182  star_dp2[0].push_back( hstar_tmp[qpos_tmp] );
2183  qpos_tmp = ( qpos_tmp + 1 ) % qstar_tmp.size();
2184  while( qstar_tmp[qpos_tmp] != split_quads[1] )
2185  {
2186  star_dp1[0].push_back( qstar_tmp[qpos_tmp] );
2187  star_dp2[0].push_back( hstar_tmp[qpos_tmp] );
2188  qpos_tmp = ( qpos_tmp + 1 ) % qstar_tmp.size();
2189  }
2190  // 2nd half of star
2191  // save hex right after split_quad[1] first
2192  star_dp2[1].push_back( hstar_tmp[qpos_tmp] );
2193  qpos_tmp = ( qpos_tmp + 1 ) % qstar_tmp.size();
2194  while( qstar_tmp[qpos_tmp] != split_quads[0] )
2195  {
2196  star_dp1[1].push_back( qstar_tmp[qpos_tmp] );
2197  star_dp2[1].push_back( hstar_tmp[qpos_tmp] );
2198  qpos_tmp = ( qpos_tmp + 1 ) % qstar_tmp.size();
2199  }
2200  }
2201  else
2202  {
2203  // go in reverse - take prev hex instead of current
2204  // one, and step in reverse
2205 
2206  // save hex right after split_quad[0] first
2207  qpos_tmp = ( qpos_tmp + qstar_tmp.size() - 1 ) % qstar_tmp.size();
2208  star_dp2[0].push_back( hstar_tmp[qpos_tmp] );
2209  while( qstar_tmp[qpos_tmp] != split_quads[1] )
2210  {
2211  star_dp1[0].push_back( qstar_tmp[qpos_tmp] );
2212  qpos_tmp = ( qpos_tmp + qstar_tmp.size() - 1 ) % qstar_tmp.size();
2213  star_dp2[0].push_back( hstar_tmp[qpos_tmp] );
2214  }
2215  // 2nd half of star
2216  // save hex right after split_quad[1] first
2217  qpos_tmp = ( qpos_tmp + qstar_tmp.size() - 1 ) % qstar_tmp.size();
2218  star_dp2[1].push_back( hstar_tmp[qpos_tmp] );
2219  while( qstar_tmp[qpos_tmp] != split_quads[0] )
2220  {
2221  star_dp1[1].push_back( qstar_tmp[qpos_tmp] );
2222  qpos_tmp = ( qpos_tmp + qstar_tmp.size() - 1 ) % qstar_tmp.size();
2223  star_dp2[1].push_back( hstar_tmp[qpos_tmp] );
2224  }
2225  }
2226 
2227  if( 0 == i )
2228  {
2229  // if we're on the first iteration, save results and continue, other iters
2230  // get compared to this one
2231  qstar.swap( qstar_tmp );
2232  hstar.swap( hstar_tmp );
2233  on_bdy = on_bdy_tmp;
2234  qpos = qpos_tmp;
2235  }
2236  }
2237 
2238  // split quads go on list with NULLs, if any, otherwise on 2nd
2239  if( on_bdy )
2240  {
2241  if( std::find( star_dp2[0].begin(), star_dp2[0].end(), 0 ) != star_dp2[0].end() )
2242  {
2243  // remove *all* the zeros
2244  star_dp2[0].erase( std::remove( star_dp2[0].begin(), star_dp2[0].end(), 0 ), star_dp2[0].end() );
2245  // put the split quads on this half
2246  star_dp1[0].push_back( split_quads[0] );
2247  star_dp1[0].push_back( split_quads[1] );
2248  }
2249  else
2250  {
2251  star_dp2[1].erase( std::remove( star_dp2[1].begin(), star_dp2[1].end(), 0 ), star_dp2[1].end() );
2252  // put the split quads on this half
2253  star_dp1[1].push_back( split_quads[0] );
2254  star_dp1[1].push_back( split_quads[1] );
2255  }
2256  }
2257  else
2258  {
2259  star_dp1[1].push_back( split_quads[0] );
2260  star_dp1[1].push_back( split_quads[1] );
2261  }
2262 
2263  // some error checking
2264  if( !( ( ( std::find( star_dp1[0].begin(), star_dp1[0].end(), split_quads[0] ) == star_dp1[0].end() &&
2265  std::find( star_dp1[0].begin(), star_dp1[0].end(), split_quads[1] ) == star_dp1[0].end() &&
2266  std::find( star_dp1[1].begin(), star_dp1[1].end(), split_quads[0] ) != star_dp1[1].end() &&
2267  std::find( star_dp1[1].begin(), star_dp1[1].end(), split_quads[1] ) != star_dp1[1].end() ) ||
2268 
2269  ( std::find( star_dp1[1].begin(), star_dp1[1].end(), split_quads[0] ) == star_dp1[1].end() &&
2270  std::find( star_dp1[1].begin(), star_dp1[1].end(), split_quads[1] ) == star_dp1[1].end() &&
2271  std::find( star_dp1[0].begin(), star_dp1[0].end(), split_quads[0] ) != star_dp1[0].end() &&
2272  std::find( star_dp1[0].begin(), star_dp1[0].end(), split_quads[1] ) != star_dp1[0].end() ) ) ) )
2273  {
2274  std::cerr << "foc_get_stars: both split quads should be on the same star list half and not "
2275  << "on the other, failed" << std::endl;
2276  return MB_FAILURE;
2277  }
2278 
2279  if( !( std::find( star_dp2[0].begin(), star_dp2[0].end(), 0 ) == star_dp2[0].end() &&
2280  std::find( star_dp2[1].begin(), star_dp2[1].end(), 0 ) == star_dp2[1].end() ) )
2281  {
2282  std::cerr << "foc_get_stars: no NULLs on the hstar lists, failed";
2283  return MB_FAILURE;
2284  }
2285 
2286  return MB_SUCCESS;
2287 }
2288 
2290 {
2291  // special delete dual procedure, because in some cases we need to delete
2292  // a sheet too since it'll get merged into another
2293 
2294  // figure out whether we'll need to delete a sheet
2295  EntityHandle sheet1 = get_dual_hyperplane( get_dual_entity( split_edges[0] ) );
2296  if( split_edges[1] ) sheet1 = get_dual_hyperplane( get_dual_entity( split_edges[1] ) );
2297  EntityHandle chordl = get_dual_hyperplane( get_dual_entity( split_quads[0] ) );
2298  EntityHandle chordr = get_dual_hyperplane( get_dual_entity( split_quads[1] ) );
2299  assert( 0 != sheet1 && 0 != chordl && 0 != chordr );
2300  Range parentsl, parentsr;
2301  ErrorCode result = mbImpl->get_parent_meshsets( chordl, parentsl );
2302  if( MB_SUCCESS != result ) return result;
2303  result = mbImpl->get_parent_meshsets( chordr, parentsr );
2304  if( MB_SUCCESS != result ) return result;
2305  parentsl.erase( sheet1 );
2306  parentsr.erase( sheet1 );
2307 
2308  // before deciding which one to delete, collect the other cells which must
2309  // be deleted, and all the chords/sheets they're on
2310  Range adj_ents, dual_ents, cells1or2;
2311  for( int i = 0; i < 3; i++ )
2312  {
2313  result = mbImpl->get_adjacencies( hexes, i, false, adj_ents, Interface::UNION );
2314  if( MB_SUCCESS != result ) return result;
2315  }
2316 
2317  // cache any adjacent hexes, for rebuilding the dual later
2318  result = mbImpl->get_adjacencies( adj_ents, 3, false, hexes, Interface::UNION );
2319  if( MB_SUCCESS != result ) return result;
2320 
2321  for( Range::iterator rit = adj_ents.begin(); rit != adj_ents.end(); ++rit )
2322  {
2323  EntityHandle this_ent = get_dual_entity( *rit );
2324  dual_ents.insert( this_ent );
2325  int dim = mbImpl->dimension_from_handle( this_ent );
2326  if( 1 == dim || 2 == dim ) cells1or2.insert( this_ent );
2327  }
2328 
2329  Range dual_hps;
2330  for( Range::iterator rit = cells1or2.begin(); rit != cells1or2.end(); ++rit )
2331  dual_hps.insert( get_dual_hyperplane( *rit ) );
2332 
2333  result = delete_dual_entities( dual_ents );
2334  if( MB_SUCCESS != result ) return result;
2335 
2336  // now decide which sheet to delete (to be merged into the other)
2337  EntityHandle sheet_delete = 0;
2338  if( is_blind( *parentsl.begin() ) )
2339  sheet_delete = *parentsl.begin();
2340  else if( is_blind( *parentsr.begin() ) )
2341  sheet_delete = *parentsr.begin();
2342  else
2343  {
2344  // neither is blind, take the one with fewer cells
2345  Range tmp_ents;
2346  int numl, numr;
2347  result = mbImpl->get_number_entities_by_handle( *parentsl.begin(), numl );
2348  if( MB_SUCCESS != result ) return result;
2349  result = mbImpl->get_number_entities_by_handle( *parentsr.begin(), numr );
2350  if( MB_SUCCESS != result ) return result;
2351  sheet_delete = ( numl > numr ? *parentsr.begin() : *parentsl.begin() );
2352  }
2353  assert( 0 != sheet_delete );
2354 
2355  // after deleting cells, check for empty chords & sheets, and delete those too
2356  for( Range::iterator rit = dual_hps.begin(); rit != dual_hps.end(); ++rit )
2357  {
2358  Range tmp_ents;
2359  result = mbImpl->get_entities_by_handle( *rit, tmp_ents );
2360  if( MB_SUCCESS != result ) return result;
2361  if( tmp_ents.empty() )
2362  {
2363  result = mbImpl->delete_entities( &( *rit ), 1 );
2364  if( MB_SUCCESS != result ) return result;
2365  }
2366  else if( *rit == sheet_delete )
2367  {
2368  // delete the sheet
2369  result = mbImpl->delete_entities( &( *rit ), 1 );
2370  if( MB_SUCCESS != result ) return result;
2371  }
2372  }
2373 
2374  // now just to be safe, add the hexes bridge-adjacent across vertices
2375  // to the hexes we already have
2376  Range tmp_hexes;
2377  MeshTopoUtil mtu( mbImpl );
2378  for( Range::iterator rit = hexes.begin(); rit != hexes.end(); ++rit )
2379  {
2380  result = mtu.get_bridge_adjacencies( *rit, 0, 3, tmp_hexes );
2381  if( MB_SUCCESS != result ) return result;
2382  }
2383  hexes.merge( tmp_hexes );
2384 
2385  return MB_SUCCESS;
2386 }
2387 
2388 //! returns true if all vertices are dual to hexes (not faces)
2389 bool DualTool::is_blind( const EntityHandle chord_or_sheet )
2390 {
2391  // must be an entity set
2392  if( TYPE_FROM_HANDLE( chord_or_sheet ) != MBENTITYSET ) return false;
2393 
2394  // get the vertices
2395  Range verts, ents;
2396  ErrorCode result = mbImpl->get_entities_by_handle( chord_or_sheet, ents );
2397  if( MB_SUCCESS != result || ents.empty() ) return false;
2398 
2399  result = mbImpl->get_adjacencies( ents, 0, false, verts, Interface::UNION );
2400  if( MB_SUCCESS != result || verts.empty() ) return false;
2401 
2402  for( Range::iterator rit = verts.begin(); rit != verts.end(); ++rit )
2403  {
2404  // get dual entity for this vertex
2405  EntityHandle dual_ent = get_dual_entity( *rit );
2406  if( 0 == dual_ent ) continue;
2407  if( TYPE_FROM_HANDLE( dual_ent ) == MBQUAD ) return false;
2408  }
2409 
2410  // if none of the vertices' duals were quads, chord_or_sheet must be blind
2411  return true;
2412 }
2413 
2414 //! given a 1-cell and a chord, return the neighboring vertices on the
2415 //! chord, in the same order as the 1-cell's vertices
2417 {
2418  // get the edges on the chord, in order, and move to middle_edge
2419  std::vector< EntityHandle > chord_edges;
2420  const EntityHandle* connect;
2421  int num_connect;
2422 
2423  ErrorCode result = mbImpl->get_entities_by_handle( chord, chord_edges );RR;
2424  std::vector< EntityHandle >::iterator vit = std::find( chord_edges.begin(), chord_edges.end(), middle_edge );
2425  result = mbImpl->get_connectivity( middle_edge, connect, num_connect );RR;
2426 
2427  if(
2428  // middle_edge isn't on this chord
2429  vit == chord_edges.end() ||
2430  // chord only has 1 edge
2431  chord_edges.size() == 1 ||
2432  // middle_edge is at beginning or end and chord isn't blind
2433  ( ( vit == chord_edges.begin() || vit == chord_edges.end() - 1 ) && !is_blind( chord ) ) )
2434  return MB_FAILURE;
2435 
2436  else if( chord_edges.size() == 2 )
2437  {
2438  // easier if it's a 2-edge blind chord, just get vertices in opposite order
2439  verts[0] = connect[1];
2440  verts[1] = connect[0];
2441  return MB_SUCCESS;
2442  }
2443 
2444  // get vertices with the prev edge & subtract vertices of 1-cell
2445  if( vit == chord_edges.begin() )
2446  vit = chord_edges.end() - 1;
2447  else
2448  --vit;
2449  Range dum_connect, middle_connect;
2450  result = mbImpl->get_connectivity( &middle_edge, 1, middle_connect );RR;
2451  result = mbImpl->get_connectivity( &( *vit ), 1, dum_connect );RR;
2452  dum_connect = subtract( dum_connect, middle_connect );
2453  if( dum_connect.size() != 1 )
2454  {
2455  std::cerr << "Trouble traversing chord." << std::endl;
2456  return MB_FAILURE;
2457  }
2458 
2459  // put in verts[0]
2460  verts[0] = *dum_connect.begin();
2461 
2462  // same with prev edge
2463  ++vit;
2464  if( vit == chord_edges.end() ) vit = chord_edges.begin();
2465  ++vit;
2466  dum_connect.clear();
2467  result = mbImpl->get_connectivity( &( *vit ), 1, dum_connect );RR;
2468  dum_connect = subtract( dum_connect, middle_connect );
2469  if( dum_connect.size() != 1 )
2470  {
2471  std::cerr << "Trouble traversing chord." << std::endl;
2472  return MB_FAILURE;
2473  }
2474 
2475  // put in verts[1]
2476  verts[1] = *dum_connect.begin();
2477 
2478  // if verts[0] and 1st vertex of 1cell don't have common edge, switch verts
2479  MeshTopoUtil mtu( mbImpl );
2480  if( 0 == mtu.common_entity( verts[0], connect[0], 1 ) )
2481  {
2482  EntityHandle dum_h = verts[0];
2483  verts[0] = verts[1];
2484  verts[1] = dum_h;
2485  }
2486 
2487  if( 0 == mtu.common_entity( verts[0], connect[0], 1 ) )
2488  {
2489  std::cerr << "Trouble traversing chord." << std::endl;
2490  return MB_FAILURE;
2491  }
2492 
2493  return MB_SUCCESS;
2494 }
2495 
2497  Range* dcells,
2498  Range* dedges,
2499  Range* dverts,
2500  Range* dverts_loop,
2501  Range* dedges_loop )
2502 {
2503  ErrorCode result = MB_SUCCESS;
2504 
2505  if( NULL != dcells )
2506  {
2507  result = mbImpl->get_entities_by_type( dual_ent, MBPOLYGON, *dcells );
2508  if( MB_SUCCESS != result ) return result;
2509  }
2510 
2511  if( NULL != dedges )
2512  {
2513  if( NULL != dcells )
2514  result = mbImpl->get_adjacencies( *dcells, 1, false, *dedges, Interface::UNION );
2515  else
2516  result = mbImpl->get_entities_by_type( dual_ent, MBEDGE, *dedges );
2517 
2518  if( MB_SUCCESS != result ) return result;
2519  }
2520 
2521  if( NULL != dverts )
2522  {
2523  if( NULL != dcells )
2524  result = mbImpl->get_adjacencies( *dcells, 0, false, *dverts, Interface::UNION );
2525  else if( NULL != dedges )
2526  result = mbImpl->get_adjacencies( *dedges, 0, false, *dverts, Interface::UNION );
2527  else
2528  {
2529  Range all_ents;
2530  result = mbImpl->get_entities_by_handle( dual_ent, all_ents );RR;
2531  result = mbImpl->get_adjacencies( all_ents, 0, false, *dverts, Interface::UNION );
2532  }
2533 
2534  if( MB_SUCCESS != result ) return result;
2535  }
2536 
2537  if( NULL != dverts_loop && NULL != dverts )
2538  {
2539  static std::vector< EntityHandle > dual_ents;
2540  dual_ents.resize( dverts->size() );
2541  result = mbImpl->tag_get_data( dualEntity_tag(), *dverts, &dual_ents[0] );
2542  if( MB_SUCCESS != result ) return result;
2543  Range::iterator rit;
2544  unsigned int i;
2545  for( rit = dverts->begin(), i = 0; rit != dverts->end(); ++rit, i++ )
2546  if( 0 != dual_ents[i] && mbImpl->type_from_handle( dual_ents[i] ) == MBQUAD ) dverts_loop->insert( *rit );
2547  }
2548 
2549  if( NULL != dedges_loop && NULL != dedges )
2550  {
2551  static std::vector< EntityHandle > dual_ents;
2552  dual_ents.resize( dedges->size() );
2553  result = mbImpl->tag_get_data( dualEntity_tag(), *dedges, &dual_ents[0] );
2554  if( MB_SUCCESS != result ) return result;
2555  Range::iterator rit;
2556  unsigned int i;
2557  for( rit = dedges->begin(), i = 0; rit != dedges->end(); ++rit, i++ )
2558  if( 0 != dual_ents[i] && mbImpl->type_from_handle( dual_ents[i] ) == MBEDGE ) dedges_loop->insert( *rit );
2559  }
2560 
2561  return result;
2562 }
2563 
2564 ErrorCode DualTool::list_entities( const EntityHandle* entities, const int num_entities ) const
2565 {
2566  Range temp_range;
2567  ErrorCode result;
2568  if( NULL == entities && 0 == num_entities )
2569  return mbImpl->list_entities( entities, num_entities );
2570 
2571  else if( NULL == entities && 0 < num_entities )
2572  {
2573 
2574  // list all entities of all types
2575  std::cout << std::endl;
2576  for( EntityType this_type = MBVERTEX; this_type < MBMAXTYPE; this_type++ )
2577  {
2578  result = mbImpl->get_entities_by_type( 0, this_type, temp_range );
2579  if( MB_SUCCESS != result ) return result;
2580  }
2581  }
2582 
2583  else
2584  {
2585  std::copy( entities, entities + num_entities, range_inserter( temp_range ) );
2586  }
2587 
2588  return list_entities( temp_range );
2589 }
2590 
2592 {
2593  // now print each entity, listing the dual information first then calling Interface to do
2594  // the rest
2595  ErrorCode result = MB_SUCCESS, tmp_result;
2596  for( Range::const_iterator iter = entities.begin(); iter != entities.end(); ++iter )
2597  {
2598  EntityType this_type = TYPE_FROM_HANDLE( *iter );
2599  std::cout << CN::EntityTypeName( this_type ) << " " << ID_FROM_HANDLE( *iter ) << ":" << std::endl;
2600 
2601  EntityHandle dual_ent = get_dual_entity( *iter );
2602  if( 0 != dual_ent )
2603  {
2604  std::cout << "Dual to " << CN::EntityTypeName( mbImpl->type_from_handle( dual_ent ) ) << " "
2605  << mbImpl->id_from_handle( dual_ent ) << std::endl;
2606  }
2607 
2608  if( TYPE_FROM_HANDLE( *iter ) == MBENTITYSET )
2609  {
2610  EntityHandle chord = 0, sheet = 0;
2611  int id;
2612  result = mbImpl->tag_get_data( dualCurve_tag(), &( *iter ), 1, &chord );
2613  if( MB_SUCCESS != result ) return result;
2614  result = mbImpl->tag_get_data( dualSurface_tag(), &( *iter ), 1, &sheet );
2615  if( MB_SUCCESS != result ) return result;
2616  result = mbImpl->tag_get_data( globalId_tag(), &( *iter ), 1, &id );
2617  if( MB_SUCCESS != result ) return result;
2618 
2619  if( 0 != chord ) std::cout << "(Dual chord " << id << ")" << std::endl;
2620  if( 0 != sheet ) std::cout << "(Dual sheet " << id << ")" << std::endl;
2621  }
2622 
2623  tmp_result = mbImpl->list_entity( *iter );
2624  if( MB_SUCCESS != tmp_result ) result = tmp_result;
2625  }
2626 
2627  return result;
2628 }
2629 
2631 {
2632  // some preliminary checking
2633  if( mbImpl->type_from_handle( odedge ) != MBEDGE ) return MB_TYPE_OUT_OF_RANGE;
2634 
2635  if( debug_ap ) ( (Core*)mbImpl )->check_adjacencies();
2636 
2637  std::cout << "FS(";
2638  print_cell( odedge );
2639  std::cout << ")" << std::endl;
2640 
2641  EntityHandle quads[4], hexes[2];
2642  std::vector< EntityHandle > connects[4], side_quads[2];
2643 
2644  // get the quads along the chord through the 2 hexes, and the vertices
2645  // for those quads
2646  ErrorCode result = fs_get_quads( odedge, quads, hexes, connects );
2647  if( MB_SUCCESS != result ) return result;
2648 
2649  // flip/rotate connect arrays so they align & are same sense
2650  result = fs_check_quad_sense( hexes[0], quads[0], connects );
2651  if( MB_SUCCESS != result ) return result;
2652 
2653  // get the quad loops along the "side" surfaces
2654  result = fs_get_quad_loops( hexes, connects, side_quads );
2655  if( MB_SUCCESS != result ) return result;
2656 
2657  // ok, done with setup; now delete dual entities affected by this operation,
2658  // which is all the entities adjacent to vertices of dual edge
2659  Range adj_verts, adj_edges, dual_ents, cells1or2;
2660  MeshTopoUtil mtu( mbImpl );
2661  result = mtu.get_bridge_adjacencies( odedge, 0, 1, adj_edges );
2662  if( MB_SUCCESS != result ) return result;
2663  result = mbImpl->get_adjacencies( adj_edges, 0, false, adj_verts, Interface::UNION );
2664  if( MB_SUCCESS != result ) return result;
2665  for( int i = 1; i <= 3; i++ )
2666  {
2667  result = mbImpl->get_adjacencies( adj_verts, i, false, dual_ents, Interface::UNION );
2668  if( MB_SUCCESS != result ) return result;
2669  }
2670 
2671  // before deleting dual, grab the 1- and 2-cells
2672  for( Range::iterator rit = dual_ents.begin(); rit != dual_ents.end(); ++rit )
2673  {
2674  int dim = mbImpl->dimension_from_handle( *rit );
2675  if( 1 == dim || 2 == dim ) cells1or2.insert( *rit );
2676  }
2677  Range dual_hps;
2678  for( Range::iterator rit = cells1or2.begin(); rit != cells1or2.end(); ++rit )
2679  dual_hps.insert( get_dual_hyperplane( *rit ) );
2680 
2681  dual_ents.insert( odedge );
2682  result = delete_dual_entities( dual_ents );
2683  if( MB_SUCCESS != result ) return result;
2684 
2685  // after deleting cells, check for empty chords & sheets, and delete those too
2686  for( Range::iterator rit = dual_hps.begin(); rit != dual_hps.end(); ++rit )
2687  {
2688  Range tmp_ents;
2689  result = mbImpl->get_entities_by_handle( *rit, tmp_ents );
2690  if( MB_SUCCESS != result ) return result;
2691  if( tmp_ents.empty() )
2692  {
2693  result = mbImpl->delete_entities( &( *rit ), 1 );
2694  if( MB_SUCCESS != result ) return result;
2695  }
2696  }
2697 
2698  // remove any explicit adjacencies between side quads and hexes; don't check
2699  // for error, since there might not be adjacencies
2700  for( int i = 0; i < 4; i++ )
2701  {
2702  for( int j = 0; j < 2; j++ )
2703  {
2704  result = mbImpl->remove_adjacencies( side_quads[j][i], &hexes[j], 1 );
2705  }
2706  }
2707 
2708  // make inner ring of vertices
2709  // get centroid of quad2
2710  double q2coords[12], avg[3] = { 0.0, 0.0, 0.0 };
2711  result = mbImpl->get_coords( &connects[1][0], 4, q2coords );
2712  if( MB_SUCCESS != result ) return result;
2713  for( int i = 0; i < 4; i++ )
2714  {
2715  avg[0] += q2coords[3 * i];
2716  avg[1] += q2coords[3 * i + 1];
2717  avg[2] += q2coords[3 * i + 2];
2718  }
2719  avg[0] *= .25;
2720  avg[1] *= .25;
2721  avg[2] *= .25;
2722  // position new vertices
2723  connects[3].resize( 4 );
2724  for( int i = 0; i < 4; i++ )
2725  {
2726  q2coords[3 * i] = avg[0] + .25 * ( q2coords[3 * i] - avg[0] );
2727  q2coords[3 * i + 1] = avg[1] + .25 * ( q2coords[3 * i + 1] - avg[1] );
2728  q2coords[3 * i + 2] = avg[2] + .25 * ( q2coords[3 * i + 2] - avg[2] );
2729  result = mbImpl->create_vertex( &q2coords[3 * i], connects[3][i] );
2730  if( MB_SUCCESS != result ) return result;
2731  }
2732 
2733  // ok, now have the 4 connectivity arrays for 4 quads; construct hexes
2734  EntityHandle hconnect[8], new_hexes[4];
2735  int new_hex_ids[4];
2736 
2737  for( int i = 0; i < 4; i++ )
2738  {
2739  int i1 = i, i2 = ( i + 1 ) % 4;
2740  hconnect[0] = connects[0][i1];
2741  hconnect[1] = connects[0][i2];
2742  hconnect[2] = connects[3][i2];
2743  hconnect[3] = connects[3][i1];
2744 
2745  hconnect[4] = connects[1][i1];
2746  hconnect[5] = connects[1][i2];
2747  hconnect[6] = connects[2][i2];
2748  hconnect[7] = connects[2][i1];
2749 
2750  result = mbImpl->create_element( MBHEX, hconnect, 8, new_hexes[i] );
2751  if( MB_SUCCESS != result ) return result;
2752 
2753  // test for equiv entities from the side quads, and make explicit adjacencies
2754  // if there are any
2755  for( int j = 0; j < 2; j++ )
2756  {
2757  if( mtu.equivalent_entities( side_quads[j][i] ) )
2758  {
2759  result = mbImpl->add_adjacencies( side_quads[j][i], &new_hexes[i], 1, false );
2760  if( MB_SUCCESS != result ) return result;
2761  }
2762  }
2763 
2764  new_hex_ids[i] = ++maxHexId;
2765  }
2766 
2767  // set the global id tag on the new hexes
2768  result = mbImpl->tag_set_data( globalId_tag(), new_hexes, 4, new_hex_ids );
2769  if( MB_SUCCESS != result ) return result;
2770 
2771  // now fixup other two hexes; start by getting hex through quads 0, 1
2772  // make this first hex switch to the other side, to make the dual look like
2773  // a hex push
2774  int tmp_ids[2];
2775  result = mbImpl->tag_get_data( globalId_tag(), hexes, 2, tmp_ids );
2776  if( MB_SUCCESS != result ) return result;
2777 
2778  result = mbImpl->delete_entities( hexes, 2 );
2779  if( MB_SUCCESS != result ) return result;
2780  result = mbImpl->delete_entities( &quads[1], 1 );
2781  if( MB_SUCCESS != result ) return result;
2782  for( int i = 0; i < 4; i++ )
2783  {
2784  hconnect[i] = connects[3][i];
2785  hconnect[4 + i] = connects[2][i];
2786  }
2787  result = mbImpl->create_element( MBHEX, hconnect, 8, hexes[0] );
2788  if( MB_SUCCESS != result ) return result;
2789 
2790  for( int i = 0; i < 4; i++ )
2791  {
2792  hconnect[i] = connects[0][i];
2793  hconnect[4 + i] = connects[3][i];
2794  }
2795  result = mbImpl->create_element( MBHEX, hconnect, 8, hexes[1] );
2796  if( MB_SUCCESS != result ) return result;
2797 
2798  // check for and fix any explicit adjacencies on either end quad
2799  if( mtu.equivalent_entities( quads[0] ) ) mbImpl->add_adjacencies( quads[0], &hexes[1], 1, false );
2800  if( mtu.equivalent_entities( quads[2] ) ) mbImpl->add_adjacencies( quads[2], &hexes[0], 1, false );
2801 
2802  // re-set the global ids for the hexes to what they were
2803  result = mbImpl->tag_set_data( globalId_tag(), hexes, 2, tmp_ids );
2804  if( MB_SUCCESS != result ) return result;
2805 
2806  if( debug_ap ) ( (Core*)mbImpl )->check_adjacencies();
2807 
2808  // now update the dual
2809  Range tmph;
2810  result = mtu.get_bridge_adjacencies( hexes[0], 0, 3, tmph );
2811  if( MB_SUCCESS != result ) return result;
2812  result = mtu.get_bridge_adjacencies( hexes[1], 0, 3, tmph );
2813  if( MB_SUCCESS != result ) return result;
2814  tmph.insert( hexes[1] );
2815  result = construct_hex_dual( tmph );
2816  if( MB_SUCCESS != result ) return result;
2817 
2818  return result;
2819 }
2820 
2822  std::vector< EntityHandle >* connects,
2823  std::vector< EntityHandle >* side_quads )
2824 {
2825  for( int i = 0; i < 4; i++ )
2826  {
2827  for( int j = 0; j < 2; j++ )
2828  {
2829  Range adj_ents, dum_quads;
2830  adj_ents.insert( hexes[j] );
2831  adj_ents.insert( connects[j][i] );
2832  adj_ents.insert( connects[j][( i + 1 ) % 4] );
2833  adj_ents.insert( connects[j + 1][i] );
2834  adj_ents.insert( connects[j + 1][( i + 1 ) % 4] );
2835 
2836  ErrorCode result = mbImpl->get_adjacencies( adj_ents, 2, false, dum_quads );
2837  if( MB_SUCCESS != result ) return result;
2838  assert( 1 == dum_quads.size() );
2839  side_quads[j].push_back( *dum_quads.begin() );
2840  }
2841  }
2842 
2843  return MB_SUCCESS;
2844 }
2845 
2846 ErrorCode DualTool::fs_check_quad_sense( EntityHandle hex0, EntityHandle quad0, std::vector< EntityHandle >* connects )
2847 {
2848  // check sense of 0th quad wrt hex; since sense is out of element,
2849  // switch if quad is NOT reversed wrt hex
2850  int dum1, dum2, sense = 0;
2851  ErrorCode result = mbImpl->side_number( hex0, quad0, dum1, sense, dum2 );
2852  if( MB_SUCCESS != result ) return result;
2853  assert( 0 != sense );
2854  if( 1 == sense )
2855  {
2856  // just switch sense of this one; others will get switched next
2857  EntityHandle dum = connects[0][0];
2858  connects[0][0] = connects[0][2];
2859  connects[0][2] = dum;
2860  }
2861 
2862  // check sense of 1st, 2nd quads, rotate if necessary to align connect arrays
2863  int index0 = -1, index2 = -1, sense0 = 0, sense2 = 0;
2864  MeshTopoUtil mtu( mbImpl );
2865  for( int i = 0; i < 4; i++ )
2866  {
2867  if( 0 != mtu.common_entity( connects[0][0], connects[1][i], 1 ) )
2868  {
2869  index0 = i;
2870  if( 0 != mtu.common_entity( connects[0][1], connects[1][( i + 1 ) % 4], 1 ) )
2871  sense0 = 1;
2872  else if( 0 != mtu.common_entity( connects[0][1], connects[1][( i + 4 - 1 ) % 4], 1 ) )
2873  sense0 = -1;
2874  break;
2875  }
2876  }
2877 
2878  assert( index0 != -1 && sense0 != 0 );
2879 
2880  if( sense0 == -1 )
2881  {
2882  EntityHandle dumh = connects[1][0];
2883  connects[1][0] = connects[1][2];
2884  connects[1][2] = dumh;
2885  if( index0 % 2 == 0 ) index0 = ( index0 + 2 ) % 4;
2886  }
2887 
2888  if( index0 != 0 )
2889  {
2890  std::vector< EntityHandle > tmpc;
2891  for( int i = 0; i < 4; i++ )
2892  tmpc.push_back( connects[1][( index0 + i ) % 4] );
2893  connects[1].swap( tmpc );
2894  }
2895 
2896  for( int i = 0; i < 4; i++ )
2897  {
2898  if( 0 != mtu.common_entity( connects[1][0], connects[2][i], 1 ) )
2899  {
2900  index2 = i;
2901  if( 0 != mtu.common_entity( connects[1][1], connects[2][( i + 1 ) % 4], 1 ) )
2902  sense2 = 1;
2903  else if( 0 != mtu.common_entity( connects[1][1], connects[2][( i + 4 - 1 ) % 4], 1 ) )
2904  sense2 = -1;
2905  break;
2906  }
2907  }
2908 
2909  assert( index2 != -1 && sense2 != 0 );
2910 
2911  if( sense2 == -1 )
2912  {
2913  EntityHandle dumh = connects[2][0];
2914  connects[2][0] = connects[2][2];
2915  connects[2][2] = dumh;
2916  if( index2 % 2 == 0 ) index2 = ( index2 + 2 ) % 4;
2917  }
2918 
2919  if( index2 != 0 )
2920  {
2921  std::vector< EntityHandle > tmpc;
2922  for( int i = 0; i < 4; i++ )
2923  tmpc.push_back( connects[2][( index2 + i ) % 4] );
2924  connects[2].swap( tmpc );
2925  }
2926 
2927  return MB_SUCCESS;
2928 }
2929 
2930 //! effect reverse face shrink operation
2932 {
2933  if( debug_ap ) ( (Core*)mbImpl )->check_adjacencies();
2934 
2935  // some preliminary checking
2936  if( mbImpl->type_from_handle( odedge ) != MBEDGE ) return MB_TYPE_OUT_OF_RANGE;
2937 
2938  std::cout << "-FS(";
2939  print_cell( odedge );
2940  std::cout << ")" << std::endl;
2941 
2942  EntityHandle quads[4], hexes[2];
2943  std::vector< EntityHandle > connects[4], side_quads[2];
2944 
2945  // get three quads (shared quad & 2 end quads), hexes, and quad
2946  // connects
2947  ErrorCode result = fs_get_quads( odedge, quads, hexes, connects );
2948  if( MB_SUCCESS != result ) return result;
2949 
2950  // adjust sense & rotation so they're aligned, together & wrt first
2951  // hex
2952  result = fs_check_quad_sense( hexes[0], quads[0], connects );
2953  if( MB_SUCCESS != result ) return result;
2954 
2955  result = fsr_get_fourth_quad( connects, side_quads );
2956  if( MB_SUCCESS != result )
2957  {
2958  std::cout << "Can't do -FS here, two hexes must be adjacent to ring of 4 hexes." << std::endl;
2959  return result;
2960  }
2961 
2962  Range adj_ents, outer_hexes, all_adjs;
2963 
2964  // first get the entities connected to interior 4 verts
2965  for( int i = 1; i <= 3; i++ )
2966  {
2967  result = mbImpl->get_adjacencies( &connects[1][0], 4, i, false, adj_ents, Interface::UNION );
2968  if( MB_SUCCESS != result ) return result;
2969  }
2970 
2971  // next get all entities adjacent to those; these will have their dual
2972  // entities deleted
2973  for( int i = 0; i < 3; i++ )
2974  {
2975  result = mbImpl->get_adjacencies( adj_ents, i, false, all_adjs, Interface::UNION );
2976  if( MB_SUCCESS != result ) return result;
2977  }
2978 
2979  // get the dual entities and delete them
2980  Range dual_ents, dual_hps;
2981  for( Range::iterator rit = all_adjs.begin(); rit != all_adjs.end(); ++rit )
2982  {
2983  EntityHandle this_ent = get_dual_entity( *rit );
2984  dual_ents.insert( this_ent );
2985  }
2986 
2987  // before deleting dual, grab the 1- and 2-cells
2988  for( Range::iterator rit = dual_ents.begin(); rit != dual_ents.end(); ++rit )
2989  {
2990  int dim = mbImpl->dimension_from_handle( *rit );
2991  if( 1 == dim || 2 == dim ) dual_hps.insert( get_dual_hyperplane( *rit ) );
2992  }
2993 
2994  result = delete_dual_entities( dual_ents );
2995  if( MB_SUCCESS != result ) return result;
2996 
2997  // after deleting cells, check for empty chords & sheets, and delete those too
2998  for( Range::iterator rit = dual_hps.begin(); rit != dual_hps.end(); ++rit )
2999  {
3000  Range tmp_ents;
3001  result = mbImpl->get_entities_by_handle( *rit, tmp_ents );
3002  if( MB_SUCCESS != result ) return result;
3003  if( tmp_ents.empty() )
3004  {
3005  result = mbImpl->delete_entities( &( *rit ), 1 );
3006  if( MB_SUCCESS != result ) return result;
3007  }
3008  }
3009 
3010  // before re-connecting two hexes, check for existing quad on 4th quad vertices;
3011  // if there is a quad there, need to add explicit adjs to any adj hexes, since
3012  // by definition there'll be another quad on those vertices
3013  bool need_explicit = false;
3014  Range adj_quads;
3015  result = mbImpl->get_adjacencies( &connects[3][0], 4, 2, false, adj_quads );
3016  if( MB_MULTIPLE_ENTITIES_FOUND == result || !adj_quads.empty() )
3017  {
3018  // there's already a quad for these 4 vertices; by definition,
3019  // we'll be creating equivalent entities, so that original quad
3020  // needs explicit adj's to its bounding elements
3021  need_explicit = true;
3022  for( Range::iterator rit = adj_quads.begin(); rit != adj_quads.end(); ++rit )
3023  {
3024  Range adj_hexes;
3025  result = mbImpl->get_adjacencies( &( *rit ), 1, 3, false, adj_hexes );RR;
3026  result = mbImpl->add_adjacencies( *rit, adj_hexes, false );RR;
3027  }
3028  }
3029 
3030  // re-connect the two hexes
3031  std::vector< EntityHandle > new_connect;
3032  std::copy( connects[3].begin(), connects[3].end(), std::back_inserter( new_connect ) );
3033  std::copy( connects[2].begin(), connects[2].end(), std::back_inserter( new_connect ) );
3034  result = mbImpl->set_connectivity( hexes[0], &new_connect[0], 8 );
3035  if( MB_SUCCESS != result ) return result;
3036 
3037  new_connect.clear();
3038  std::copy( connects[0].begin(), connects[0].end(), std::back_inserter( new_connect ) );
3039  std::copy( connects[3].begin(), connects[3].end(), std::back_inserter( new_connect ) );
3040  result = mbImpl->set_connectivity( hexes[1], &new_connect[0], 8 );
3041  if( MB_SUCCESS != result ) return result;
3042 
3043  // test for equiv entities from the side quads, and make explicit
3044  // adjacencies if there are any
3045  MeshTopoUtil mtu( mbImpl );
3046  for( int j = 0; j < 2; j++ )
3047  {
3048  for( int i = 0; i < 4; i++ )
3049  {
3050  if( mtu.equivalent_entities( side_quads[j][i] ) )
3051  {
3052  result = mbImpl->add_adjacencies( side_quads[j][i], &hexes[j], 1, false );
3053  if( MB_SUCCESS != result ) return result;
3054  }
3055  }
3056  }
3057 
3058  // remove hexes we want to keep
3059  adj_ents.erase( hexes[0] );
3060  adj_ents.erase( hexes[1] );
3061 
3062  // delete the other interior entities
3063  result = mbImpl->delete_entities( adj_ents );
3064  if( MB_SUCCESS != result ) return result;
3065 
3066  EntityHandle new_quad;
3067  result = mbImpl->create_element( MBQUAD, &connects[3][0], 4, new_quad );RR;
3068  if( need_explicit )
3069  {
3070  result = mbImpl->add_adjacencies( new_quad, hexes, 2, false );RR;
3071  }
3072 
3073  if( debug_ap ) ( (Core*)mbImpl )->check_adjacencies();
3074 
3075  // now update the dual
3076  result = construct_hex_dual( hexes, 2 );
3077  if( MB_SUCCESS != result ) return result;
3078 
3079  return MB_SUCCESS;
3080 }
3081 
3082 ErrorCode DualTool::fsr_get_fourth_quad( std::vector< EntityHandle >* connects,
3083  std::vector< EntityHandle >* side_quads )
3084 {
3085  // given the first three quad connectivities in ordered vectors, get the fourth,
3086  // where the fourth is really the 4 vertices originally shared by the 2 hexes
3087  // before the face shrink on them
3088 
3089  // vertex on 4th quad is in quad adj to other 3 verts
3090  for( int i = 0; i < 4; i++ )
3091  {
3092  Range start_verts, tmp_verts, quads;
3093  for( int j = 0; j < 3; j++ )
3094  start_verts.insert( connects[j][i] );
3095  ErrorCode result = mbImpl->get_adjacencies( start_verts, 2, false, quads );
3096  if( MB_SUCCESS != result ) return result;
3097  assert( quads.size() == 1 );
3098  result = mbImpl->get_adjacencies( &( *quads.begin() ), 1, 0, false, tmp_verts );RR;
3099  tmp_verts = subtract( tmp_verts, start_verts );
3100  assert( 1 == tmp_verts.size() );
3101  connects[3].push_back( *tmp_verts.begin() );
3102  }
3103 
3104  // now get the side quads
3105  for( int i = 0; i < 4; i++ )
3106  {
3107  Range dum_ents, hexes;
3108  dum_ents.insert( connects[1][i] );
3109  dum_ents.insert( connects[1][( i + 1 ) % 4] );
3110  dum_ents.insert( connects[3][i] );
3111  ErrorCode result = mbImpl->get_adjacencies( dum_ents, 3, false, hexes );
3112  if( MB_SUCCESS != result ) return result;
3113  assert( 1 == hexes.size() );
3114 
3115  hexes.insert( connects[0][i] );
3116  hexes.insert( connects[0][( i + 1 ) % 4] );
3117  hexes.insert( connects[3][i] );
3118  hexes.insert( connects[3][( i + 1 ) % 4] );
3119  dum_ents.clear();
3120  result = mbImpl->get_adjacencies( hexes, 2, false, dum_ents );
3121  if( MB_SUCCESS != result ) return result;
3122  assert( dum_ents.size() == 1 );
3123  side_quads[0].push_back( *dum_ents.begin() );
3124 
3125  hexes.erase( connects[0][i] );
3126  hexes.erase( connects[0][( i + 1 ) % 4] );
3127  hexes.insert( connects[2][i] );
3128  hexes.insert( connects[2][( i + 1 ) % 4] );
3129  dum_ents.clear();
3130  result = mbImpl->get_adjacencies( hexes, 2, false, dum_ents );
3131  if( MB_SUCCESS != result ) return result;
3132  side_quads[1].push_back( *dum_ents.begin() );
3133  }
3134 
3135  return MB_SUCCESS;
3136 }
3137 
3139  EntityHandle* quads,
3140  EntityHandle* hexes,
3141  std::vector< EntityHandle >* connects )
3142 {
3143  // need to get the three quads along the chord
3144  EntityHandle chord = get_dual_hyperplane( odedge );
3145  if( 0 == chord ) return MB_FAILURE;
3146 
3147  std::vector< EntityHandle > edges;
3148  ErrorCode result = mbImpl->get_entities_by_handle( chord, edges );
3149  if( MB_FAILURE == result ) return result;
3150 
3151  std::vector< EntityHandle >::iterator vit = std::find( edges.begin(), edges.end(), odedge );
3152  // shouldn't be first or last edge on chord
3153  if( vit == edges.end() || *edges.begin() == *vit || *edges.rbegin() == *vit ) return MB_FAILURE;
3154 
3155  // get quads/connectivity for first 3 quads
3156  quads[0] = get_dual_entity( *( vit - 1 ) );
3157  quads[1] = get_dual_entity( *vit );
3158  quads[2] = get_dual_entity( *( vit + 1 ) );
3159  for( int i = 0; i < 3; i++ )
3160  {
3161  result = mbImpl->get_connectivity( &quads[i], 1, connects[i], true );
3162  if( MB_SUCCESS != result ) return result;
3163  }
3164 
3165  Range tmph;
3166  result = mbImpl->get_adjacencies( quads, 2, 3, false, tmph );
3167  if( MB_SUCCESS != result ) return result;
3168  assert( tmph.size() == 1 );
3169  hexes[0] = *tmph.begin();
3170 
3171  tmph.clear();
3172  result = mbImpl->get_adjacencies( &quads[1], 2, 3, false, tmph );
3173  if( MB_SUCCESS != result ) return result;
3174  assert( tmph.size() == 1 );
3175  hexes[1] = *tmph.begin();
3176 
3177  return MB_SUCCESS;
3178 }
3179 
3181 {
3182  // delete dual hyperplanes
3183  Range dual_surfs, dual_curves;
3184  ErrorCode result = this->get_dual_hyperplanes( mbImpl, 2, dual_surfs );RR;
3185  result = mbImpl->delete_entities( dual_surfs );RR;
3186  result = this->get_dual_hyperplanes( mbImpl, 1, dual_curves );RR;
3187  result = mbImpl->delete_entities( dual_curves );RR;
3188 
3189  // gather up all dual entities
3190  Range dual_ents;
3191  result = mbImpl->get_entities_by_type_and_tag( 0, MBVERTEX, &isDualCellTag, NULL, 1, dual_ents, Interface::UNION );RR;
3192  result = mbImpl->get_entities_by_type_and_tag( 0, MBEDGE, &isDualCellTag, NULL, 1, dual_ents, Interface::UNION );RR;
3193  result = mbImpl->get_entities_by_type_and_tag( 0, MBPOLYGON, &isDualCellTag, NULL, 1, dual_ents, Interface::UNION );RR;
3194  result =
3196 
3197  // delete them, in reverse order of dimension
3198  ErrorCode tmp_result;
3199  for( Range::reverse_iterator rit = dual_ents.rbegin(); rit != dual_ents.rend(); ++rit )
3200  {
3201  tmp_result = mbImpl->delete_entities( &( *rit ), 1 );
3202  if( MB_SUCCESS != tmp_result ) result = tmp_result;
3203  }
3204  RR;
3205 
3206  // delete dual-related tags
3207  if( 0 != dualSurfaceTag )
3208  {
3209  tmp_result = mbImpl->tag_delete( dualSurfaceTag );
3210  if( MB_SUCCESS != tmp_result && MB_TAG_NOT_FOUND != tmp_result ) result = tmp_result;
3211  }
3212  if( 0 != dualCurveTag )
3213  {
3214  tmp_result = mbImpl->tag_delete( dualCurveTag );
3215  if( MB_SUCCESS != tmp_result && MB_TAG_NOT_FOUND != tmp_result ) result = tmp_result;
3216  }
3217  if( 0 != dualEntityTag )
3218  {
3219  tmp_result = mbImpl->tag_delete( dualEntityTag );
3220  if( MB_SUCCESS != tmp_result && MB_TAG_NOT_FOUND != tmp_result ) result = tmp_result;
3221  }
3222  if( 0 != extraDualEntityTag )
3223  {
3224  tmp_result = mbImpl->tag_delete( extraDualEntityTag );
3225  if( MB_SUCCESS != tmp_result && MB_TAG_NOT_FOUND != tmp_result ) result = tmp_result;
3226  }
3227  if( 0 != dualGraphicsPointTag )
3228  {
3229  tmp_result = mbImpl->tag_delete( dualGraphicsPointTag );
3230  if( MB_SUCCESS != tmp_result && MB_TAG_NOT_FOUND != tmp_result ) result = tmp_result;
3231  }
3232 
3233  return MB_SUCCESS;
3234 }
3235 
3237 {
3238  // check primal-dual correspondence
3239 
3240  // get the primal entities
3241  Range pents[4];
3242  ErrorCode result = mbImpl->get_entities_by_type( 0, MBHEX, pents[3] );RR;
3243  for( int i = 2; i >= 0; i-- )
3244  {
3245  result = mbImpl->get_adjacencies( pents[3], 2, false, pents[2], Interface::UNION );RR;
3246  }
3247 
3248  // for each primal entity of dimension pd
3249 #define PRENT( ent ) CN::EntityTypeName( TYPE_FROM_HANDLE( ent ) ) << " " << ID_FROM_HANDLE( ent )
3250  ErrorCode overall_result = MB_SUCCESS;
3251  for( int pd = 1; pd <= 3; pd++ )
3252  {
3253  for( Range::iterator prit = pents[pd].begin(); prit != pents[pd].end(); ++prit )
3254  {
3255  // get corresponding dual entity of dimension dd = 3-pd
3256  EntityHandle dual_ent = get_dual_entity( *prit );
3257  if( 0 == dual_ent ) std::cerr << "Problem getting dual entity for " << PRENT( *prit ) << std::endl;
3258 
3259  // for each sub dimension sd = 0..pd-1
3260  for( int sd = 0; sd < pd; sd++ )
3261  {
3262  Range R1, R2, R3;
3263  // R1 = entities bounding primal entity of dim sd
3264  result = mbImpl->get_adjacencies( &( *prit ), 1, sd, false, R1 );RR;
3265 
3266  // R2 = entities bounded by dual entity, of dim 3-sd
3267  result = mbImpl->get_adjacencies( &dual_ent, 1, 3 - sd, false, R2 );RR;
3268 
3269  if( R1.size() != R2.size() )
3270  {
3271  std::cerr << PRENT( *prit ) << ": number of adj ents in "
3272  << "primal/dual don't agree for dimension " << sd << "." << std::endl;
3273  overall_result = MB_FAILURE;
3274  }
3275 
3276  // for each entity in R1, get its dual and look for it in R2
3277  for( Range::iterator r1it = R1.begin(); r1it != R1.end(); ++r1it )
3278  {
3279  EntityHandle tmp_dual = get_dual_entity( *r1it );
3280  if( R2.find( tmp_dual ) == R2.end() )
3281  {
3282  std::cerr << PRENT( *prit ) << ": adj entity " << PRENT( *r1it ) << " isn't adjacent in dual."
3283  << std::endl;
3284  overall_result = MB_FAILURE;
3285  }
3286  }
3287  // ditto for R2
3288  for( Range::iterator r2it = R2.begin(); r2it != R2.end(); ++r2it )
3289  {
3290  EntityHandle tmp_prim = get_dual_entity( *r2it );
3291  if( R1.find( tmp_prim ) == R1.end() )
3292  {
3293  std::cerr << PRENT( *prit ) << ": adj entity " << PRENT( *r2it ) << " isn't adjacent in primal."
3294  << std::endl;
3295  overall_result = MB_FAILURE;
3296  }
3297  }
3298  }
3299  }
3300  }
3301 
3302  return overall_result;
3303 }
3304 
3305 } // namespace moab