1 // Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
22 // File : StdMeshers_HexaFromSkin_3D.cxx
23 // Created : Wed Jan 27 12:28:07 2010
24 // Author : Edward AGAPOV (eap)
27 #include "StdMeshers_HexaFromSkin_3D.hxx"
29 #include "SMDS_VolumeOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMESH_Block.hxx"
32 #include "SMESH_MesherHelper.hxx"
34 #include "utilities.h"
36 // Define error message
38 #define BAD_MESH_ERR \
39 error(SMESH_Comment("Can't detect block-wise structure of the input 2D mesh.\n" \
40 __FILE__ ":" )<<__LINE__)
42 #define BAD_MESH_ERR \
43 error(SMESH_Comment("Can't detect block-wise structure of the input 2D mesh"))
47 #define _DUMP_(msg) // cout << msg << endl
53 enum EBoxSides //!< sides of the block
55 B_BOTTOM=0, B_RIGHT, B_TOP, B_LEFT, B_FRONT, B_BACK, B_UNDEFINED
57 const char* SBoxSides[] = //!< names of block sides
59 "BOTTOM", "RIGHT", "TOP", "LEFT", "FRONT", "BACK", "UNDEFINED"
61 enum EQuadEdge //!< edges of quadrangle side
63 Q_BOTTOM, Q_RIGHT, Q_TOP, Q_LEFT, Q_UNDEFINED
66 //================================================================================
68 * \brief return true if a node is at block corner
70 //================================================================================
72 bool isCornerNode( const SMDS_MeshNode* n )
74 return n && n->NbInverseElements( SMDSAbs_Face ) % 2;
77 //================================================================================
79 * \brief check element type
81 //================================================================================
83 bool isQuadrangle(const SMDS_MeshElement* e)
85 return ( e && e->NbNodes() == ( e->IsQuadratic() ? 8 : 4 ));
88 //================================================================================
90 * \brief return opposite node of a quadrangle face
92 //================================================================================
94 const SMDS_MeshNode* oppositeNode(const SMDS_MeshElement* quad, int iNode)
96 return quad->GetNode( (iNode+2) % 4 );
99 //================================================================================
101 * \brief Convertor of a pair of integers to a sole index
106 _Indexer( int xSize=0, int ySize=0 ): _xSize(xSize), _ySize(ySize) {}
107 int size() const { return _xSize * _ySize; }
108 int operator()(int x, int y) const { return y * _xSize + x; }
110 //================================================================================
112 * \brief Oriented convertor of a pair of integers to a sole index
114 class _OrientedIndexer : public _Indexer
117 enum OriFlags //!< types of block side orientation
119 REV_X = 1, REV_Y = 2, SWAP_XY = 4, MAX_ORI = REV_X|REV_Y|SWAP_XY
121 _OrientedIndexer( const _Indexer& indexer, const int oriFlags ):
122 _Indexer( indexer._xSize, indexer._ySize ),
123 _xSize (indexer._xSize), _ySize(indexer._ySize),
124 _xRevFun((oriFlags & REV_X) ? & reverse : & lazy),
125 _yRevFun((oriFlags & REV_Y) ? & reverse : & lazy),
126 _swapFun((oriFlags & SWAP_XY ) ? & swap : & lazy)
128 (*_swapFun)( _xSize, _ySize );
130 //!< Return index by XY
131 int operator()(int x, int y) const
133 (*_xRevFun)( x, const_cast<int&>( _xSize ));
134 (*_yRevFun)( y, const_cast<int&>( _ySize ));
136 return _Indexer::operator()(x,y);
138 //!< Return index for a corner
139 int corner(bool xMax, bool yMax) const
141 int x = xMax, y = yMax, size = 2;
142 (*_xRevFun)( x, size );
143 (*_yRevFun)( y, size );
145 return _Indexer::operator()(x ? _Indexer::_xSize-1 : 0 , y ? _Indexer::_ySize-1 : 0);
147 int xSize() const { return _xSize; }
148 int ySize() const { return _ySize; }
153 typedef void (*TFun)(int& x, int& y);
154 TFun _xRevFun, _yRevFun, _swapFun;
156 static void lazy(int&, int&) {}
157 static void reverse(int& x, int& size) { x = size - x - 1; }
158 static void swap(int& x, int& y) { std::swap(x,y); }
160 //================================================================================
162 * \brief Structure corresponding to the meshed side of block
166 vector<const SMDS_MeshNode*> _grid;
168 int _nbBlocksExpected;
172 #define _grid_access_(args) _grid.at( args )
174 #define _grid_access_(args) _grid[ args ]
176 //!< Return node at XY
177 const SMDS_MeshNode* getNode(int x, int y) const { return _grid_access_( _index( x, y )); }
179 void setNode(int x, int y, const SMDS_MeshNode* n) { _grid_access_( _index( x, y )) = n; }
180 //!< Return a corner node
181 const SMDS_MeshNode* getCornerNode(bool isXMax, bool isYMax) const
183 return getNode( isXMax ? _index._xSize-1 : 0 , isYMax ? _index._ySize-1 : 0 );
185 const SMDS_MeshElement* getCornerFace(const SMDS_MeshNode* cornerNode) const;
186 //!< True if all blocks this side belongs to have beem found
187 bool isBound() const { return _nbBlocksExpected <= _nbBlocksFound; }
188 //!< Return coordinates of node at XY
189 gp_XYZ getXYZ(int x, int y) const { return SMESH_MeshEditor::TNodeXYZ( getNode( x, y )); }
190 //!< Return gravity center of the four corners and the middle node
195 getXYZ( _index._xSize-1, 0 ) +
196 getXYZ( 0, _index._ySize-1 ) +
197 getXYZ( _index._xSize-1, _index._ySize-1 ) +
198 getXYZ( _index._xSize/2, _index._ySize/2 );
201 //!< Return number of mesh faces
202 int getNbFaces() const { return (_index._xSize-1) * (_index._ySize-1); }
204 //================================================================================
206 * \brief _BlockSide with changed orientation
208 struct _OrientedBlockSide
211 _OrientedIndexer _index;
213 _OrientedBlockSide( _BlockSide* side=0, const int oriFlags=0 ):
214 _side(side), _index(side ? side->_index : _Indexer(), oriFlags ) {}
215 //!< return coordinates by XY
216 gp_XYZ xyz(int x, int y) const
218 return SMESH_MeshEditor::TNodeXYZ( _side->_grid_access_( _index( x, y )) );
220 //!< safely return a node by XY
221 const SMDS_MeshNode* node(int x, int y) const
223 int i = _index( x, y );
224 return ( i < 0 || i >= _side->_grid.size()) ? 0 : _side->_grid[i];
226 //!< Return a corner node
227 const SMDS_MeshNode* cornerNode(bool isXMax, bool isYMax) const
229 return _side->_grid_access_( _index.corner( isXMax, isYMax ));
231 //!< return its size in nodes
232 int getHoriSize() const { return _index.xSize(); }
233 int getVertSize() const { return _index.ySize(); }
234 //!< True if _side has been initialized
235 operator bool() const { return _side; }
236 //! Direct access to _side
237 const _BlockSide* operator->() const { return _side; }
238 _BlockSide* operator->() { return _side; }
240 //================================================================================
242 * \brief Meshed skin of block
246 _OrientedBlockSide _side[6]; // 6 sides of a sub-block
248 const _OrientedBlockSide& getSide(int i) const { return _side[i]; }
250 //================================================================================
252 * \brief Skin mesh possibly containing several meshed blocks
258 int findBlocks(SMESH_Mesh& mesh);
259 //!< return i-th block
260 const _Block& getBlock(int i) const { return _blocks[i]; }
261 //!< return error description
262 const SMESH_Comment& error() const { return _error; }
265 bool fillSide( _BlockSide& side, const SMDS_MeshElement* cornerQuad);
266 bool fillRowsUntilCorner(const SMDS_MeshElement* quad,
267 const SMDS_MeshNode* n1,
268 const SMDS_MeshNode* n2,
269 vector<const SMDS_MeshNode*>& verRow1,
270 vector<const SMDS_MeshNode*>& verRow2,
272 _OrientedBlockSide findBlockSide( EBoxSides startBlockSide,
273 EQuadEdge sharedSideEdge1,
274 EQuadEdge sharedSideEdge2);
275 //!< update own data and data of the side bound to block
276 void setSideBoundToBlock( _BlockSide& side )
278 side._nbBlocksFound++;
279 if ( side.isBound() )
281 _corner2sides[ side.getCornerNode(0,0) ].erase( &side );
282 _corner2sides[ side.getCornerNode(1,0) ].erase( &side );
283 _corner2sides[ side.getCornerNode(0,1) ].erase( &side );
284 _corner2sides[ side.getCornerNode(1,1) ].erase( &side );
287 //!< store reason of error
288 int error(const SMESH_Comment& reason) { _error = reason; return 0; }
290 SMESH_Comment _error;
292 list< _BlockSide > _allSides;
293 vector< _Block > _blocks;
295 map< const SMDS_MeshNode*, set< _BlockSide* > > _corner2sides;
298 //================================================================================
300 * \brief Find and return number of submeshes corresponding to blocks
302 //================================================================================
304 int _Skin::findBlocks(SMESH_Mesh& mesh)
306 SMESHDS_Mesh* meshDS = mesh.GetMeshDS();
308 // Find a node at any block corner
310 SMDS_NodeIteratorPtr nIt = meshDS->nodesIterator(/*idInceasingOrder=*/true);
311 if ( !nIt->more() ) return error("Empty mesh");
313 const SMDS_MeshNode* nCorner = 0;
314 while ( nIt->more() )
316 nCorner = nIt->next();
317 if ( isCornerNode( nCorner ))
325 // --------------------------------------------------------------------
326 // Find all block sides starting from mesh faces sharing the corner node
327 // --------------------------------------------------------------------
329 int nbFacesOnSides = 0;
330 TIDSortedElemSet cornerFaces; // corner faces of found _BlockSide's
331 list< const SMDS_MeshNode* > corners( 1, nCorner );
332 list< const SMDS_MeshNode* >::iterator corner = corners.begin();
333 while ( corner != corners.end() )
335 SMDS_ElemIteratorPtr faceIt = (*corner)->GetInverseElementIterator( SMDSAbs_Face );
336 while ( faceIt->more() )
338 const SMDS_MeshElement* face = faceIt->next();
339 if ( !cornerFaces.insert( face ).second )
340 continue; // already loaded block side
342 if ( !isQuadrangle( face ))
343 return error("Non-quadrangle elements in the input mesh");
345 if ( _allSides.empty() || !_allSides.back()._grid.empty() )
346 _allSides.push_back( _BlockSide() );
348 _BlockSide& side = _allSides.back();
349 if ( !fillSide( side, face ) )
351 if ( !_error.empty() )
356 for ( int isXMax = 0; isXMax < 2; ++isXMax )
357 for ( int isYMax = 0; isYMax < 2; ++isYMax )
359 const SMDS_MeshNode* nCorner = side.getCornerNode(isXMax,isYMax );
360 if ( !isCornerNode( nCorner ))
362 _corner2sides[ nCorner ].insert( &side );
363 corners.push_back( nCorner );
364 cornerFaces.insert( side.getCornerFace( nCorner ));
366 nbFacesOnSides += side.getNbFaces();
371 // find block sides of other domains if any
372 if ( corner == corners.end() && nbFacesOnSides < mesh.NbQuadrangles() )
374 while ( nIt->more() )
376 nCorner = nIt->next();
377 if ( isCornerNode( nCorner ))
378 corner = corners.insert( corner, nCorner );
380 nbFacesOnSides = mesh.NbQuadrangles();
384 if ( _allSides.empty() )
386 if ( _allSides.back()._grid.empty() )
387 _allSides.pop_back();
389 // ---------------------------
390 // Organize sides into blocks
391 // ---------------------------
393 // analyse sharing of sides by blocks
394 int nbBlockSides = 0; // nb of block sides taking into account their sharing
395 list < _BlockSide >::iterator sideIt = _allSides.begin();
396 for ( ; sideIt != _allSides.end(); ++sideIt )
398 _BlockSide& side = *sideIt;
399 bool isSharedSide = true;
400 for ( int isXMax = 0; isXMax < 2; ++isXMax )
401 for ( int isYMax = 0; isYMax < 2; ++isYMax )
402 if ( _corner2sides[ side.getCornerNode(isXMax,isYMax) ].size() < 5 )
403 isSharedSide = false;
404 side._nbBlocksFound = 0;
405 side._nbBlocksExpected = isSharedSide ? 2 : 1;
406 nbBlockSides += side._nbBlocksExpected;
409 // find sides of each block
411 while ( nbBlockSides >= 6 )
413 // get any side not bound to all blocks to belongs to
414 sideIt = _allSides.begin();
415 while ( sideIt != _allSides.end() && sideIt->isBound())
418 // start searching for block sides from the got side
420 if ( _blocks.empty() || _blocks.back()._side[B_FRONT] )
421 _blocks.resize( _blocks.size() + 1 );
423 _Block& block = _blocks.back();
424 block._side[B_FRONT] = &(*sideIt);
425 setSideBoundToBlock( *sideIt );
428 // edges of neighbour sides of B_FRONT corresponding to front's edges
429 EQuadEdge edgeOfFront[4] = { Q_BOTTOM, Q_RIGHT, Q_TOP, Q_LEFT };
430 EQuadEdge edgeToFind [4] = { Q_BOTTOM, Q_LEFT, Q_BOTTOM, Q_LEFT };
431 for ( int i = Q_BOTTOM; ok && i <= Q_LEFT; ++i )
432 ok = ( block._side[i] = findBlockSide( B_FRONT, edgeOfFront[i], edgeToFind[i]));
434 ok = ( block._side[B_BACK] = findBlockSide( B_TOP, Q_TOP, Q_TOP ));
436 // count the found sides
438 for (int i = 0; i < B_UNDEFINED; ++i )
440 _DUMP_("** Block side "<< SBoxSides[i]);
441 if ( block._side[ i ] )
443 if ( ok && i != B_FRONT)
445 setSideBoundToBlock( *block._side[ i ]._side );
448 _DUMP_("Corner 0,0 "<< block._side[ i ].cornerNode(0,0));
449 _DUMP_("Corner 1,0 "<< block._side[ i ].cornerNode(1,0));
450 _DUMP_("Corner 1,1 "<< block._side[ i ].cornerNode(1,1));
451 _DUMP_("Corner 0,1 "<< block._side[ i ].cornerNode(0,1));
455 _DUMP_("Not found"<<endl);
459 block._side[0] = block._side[1] = block._side[2] =
460 block._side[3] = block._side[4] = block._side[5] = 0;
464 if ( nbBlocks == 0 && _error.empty() )
470 //================================================================================
472 * \brief Fill block side data starting from its corner quadrangle
474 //================================================================================
476 bool _Skin::fillSide( _BlockSide& side, const SMDS_MeshElement* cornerQuad)
478 // Find out size of block side mesured in nodes and by the way find two rows
479 // of nodes in two directions.
482 const SMDS_MeshElement* firstQuad = cornerQuad;
484 // get corner node of cornerQuad
485 const SMDS_MeshNode* nCorner = 0;
486 for ( int i = 0; i < 4 && !nCorner; ++i )
487 if ( isCornerNode( firstQuad->GetNode(i)))
488 nCorner = firstQuad->GetNode(i);
489 if ( !nCorner ) return false;
491 // get a node on block edge
492 int iCorner = firstQuad->GetNodeIndex( nCorner );
493 const SMDS_MeshNode* nOnEdge = firstQuad->GetNode( (iCorner+1) % 4);
495 // find out size of block side
496 vector<const SMDS_MeshNode*> horRow1, horRow2, verRow1, verRow2;
497 if ( !fillRowsUntilCorner( firstQuad, nCorner, nOnEdge, horRow1, horRow2, true ) ||
498 !fillRowsUntilCorner( firstQuad, nCorner, nOnEdge, verRow1, verRow2, false ))
500 nbX = horRow1.size(), nbY = verRow1.size();
503 side._index._xSize = horRow1.size();
504 side._index._ySize = verRow1.size();
505 side._grid.resize( side._index.size(), NULL );
507 for ( x = 0; x < horRow1.size(); ++x )
509 side.setNode( x, 0, horRow1[x] );
510 side.setNode( x, 1, horRow2[x] );
512 for ( y = 0; y < verRow1.size(); ++y )
514 side.setNode( 0, y, verRow1[y] );
515 side.setNode( 1, y, verRow2[y] );
518 // Find the rest nodes
520 y = 1; // y of the row to fill
521 TIDSortedElemSet emptySet, avoidSet;
524 // get next firstQuad in the next row of quadrangles
529 // o---o o o o o <- found nodes
532 int i1down, i2down, i2up;
533 const SMDS_MeshNode* n1down = side.getNode( 0, y-1 );
534 const SMDS_MeshNode* n2down = side.getNode( 1, y-1 );
535 avoidSet.clear(); avoidSet.insert( firstQuad );
536 firstQuad = SMESH_MeshEditor::FindFaceInSet( n1down, n2down, emptySet, avoidSet,
538 if ( !isQuadrangle( firstQuad ))
541 const SMDS_MeshNode* n2up = oppositeNode( firstQuad, i1down );
542 avoidSet.clear(); avoidSet.insert( firstQuad );
544 // find the rest nodes in the y-th row by faces in the row
549 const SMDS_MeshElement* quad = SMESH_MeshEditor::FindFaceInSet( n2up, n2down, emptySet,
550 avoidSet, &i2up, &i2down);
551 if ( !isQuadrangle( quad ))
554 n2up = oppositeNode( quad, i2down );
555 n2down = oppositeNode( quad, i2up );
556 avoidSet.clear(); avoidSet.insert( quad );
558 side.setNode( x, y, n2up );
565 //================================================================================
567 * \brief Try to find a block side adjacent to the given side by given edge
569 //================================================================================
571 _OrientedBlockSide _Skin::findBlockSide( EBoxSides startBlockSide,
572 EQuadEdge sharedSideEdge1,
573 EQuadEdge sharedSideEdge2)
575 _Block& block = _blocks.back();
576 _OrientedBlockSide& side1 = block._side[ startBlockSide ];
578 // get corner nodes of the given block edge
579 bool xMax1=0, yMax1=0, xMax2=1, yMax2=1;
580 switch( sharedSideEdge1 )
582 case Q_BOTTOM: yMax2 = 0; break;
583 case Q_RIGHT: xMax1 = 1; break;
584 case Q_TOP: yMax1 = 1; break;
585 case Q_LEFT: xMax2 = 0; break;
587 error(SMESH_Comment("Internal error at")<<__FILE__<<":"<<__LINE__);
590 const SMDS_MeshNode* n1 = side1.cornerNode( xMax1, yMax1);
591 const SMDS_MeshNode* n2 = side1.cornerNode( xMax2, yMax2);
593 set< _BlockSide* >& sidesWithN1 = _corner2sides[ n1 ];
594 set< _BlockSide* >& sidesWithN2 = _corner2sides[ n2 ];
595 if ( sidesWithN1.empty() || sidesWithN2.empty() )
597 _DUMP_("no sides by nodes "<< n1->GetID() << " " << n2->GetID() );
601 // find all sides sharing both nodes n1 and n2
602 set< _BlockSide* > sidesOnEdge;
603 set< _BlockSide* >::iterator sideIt;
604 std::set_intersection( sidesWithN1.begin(), sidesWithN1.end(),
605 sidesWithN2.begin(), sidesWithN2.end(),
606 inserter( sidesOnEdge, sidesOnEdge.begin()));
608 // exclude loaded sides of block from sidesOnEdge
609 int nbLoadedSides = 0;
610 for (int i = 0; i < B_UNDEFINED; ++i )
612 if ( block._side[ i ] )
615 sidesOnEdge.erase( block._side[ i ]._side );
618 int nbSidesOnEdge = sidesOnEdge.size();
619 _DUMP_("nbSidesOnEdge "<< nbSidesOnEdge);
620 if ( nbSidesOnEdge == 0 )
623 _BlockSide* foundSide = 0;
624 if ( nbSidesOnEdge == 1 || nbSidesOnEdge == 2 && nbLoadedSides == 1 )
626 foundSide = *sidesOnEdge.begin();
630 // Select one of found sides most close to startBlockSide
632 // gravity center of already loaded block sides
634 for (int i = 0; i < B_UNDEFINED; ++i )
635 if ( block._side[ i ] )
637 gc += block._side[ i ]._side->getGC();
642 gp_XYZ p1 ( n1->X(),n1->Y(),n1->Z()), p2 (n2->X(),n2->Y(),n2->Z());
643 gp_Vec p2p1( p1 - p2 );
645 const SMDS_MeshElement* face1 = side1->getCornerFace( n1 );
646 gp_XYZ p1Op = SMESH_MeshEditor::TNodeXYZ( oppositeNode( face1, face1->GetNodeIndex(n1)));
647 gp_Vec side1Dir( p1, p1Op );
649 map < double , _BlockSide* > angleOfSide;
650 for (sideIt = sidesOnEdge.begin(); sideIt != sidesOnEdge.end(); ++sideIt )
652 _BlockSide* sideI = *sideIt;
653 const SMDS_MeshElement* faceI = sideI->getCornerFace( n1 );
654 gp_XYZ p1Op = SMESH_MeshEditor::TNodeXYZ( oppositeNode( faceI, faceI->GetNodeIndex(n1)));
655 gp_Vec sideIDir( p1, p1Op );
656 double angle = side1Dir.AngleWithRef( sideIDir, p2p1 );
657 if ( angle < 0 ) angle += 2 * PI;
658 angleOfSide.insert( make_pair( angle, sideI ));
660 gp_Vec gcDir( p1, gc );
661 double gcAngle = side1Dir.AngleWithRef( gcDir, p2p1 );
662 foundSide = gcAngle < 0 ? angleOfSide.rbegin()->second : angleOfSide.begin()->second;
665 // Orient the found side correctly
667 // corners of found side corresponding to nodes n1 and n2
668 xMax1= yMax1 = 0; xMax2 = yMax2 = 1;
669 switch( sharedSideEdge2 )
671 case Q_BOTTOM: yMax2 = 0; break;
672 case Q_RIGHT: xMax1 = 1; break;
673 case Q_TOP: yMax1 = 1; break;
674 case Q_LEFT: xMax2 = 0; break;
676 error(SMESH_Comment("Internal error at")<<__FILE__<<":"<<__LINE__);
679 for ( int ori = 0; ori < _OrientedIndexer::MAX_ORI+1; ++ori )
681 _OrientedBlockSide orientedSide( foundSide, ori );
682 const SMDS_MeshNode* n12 = orientedSide.cornerNode( xMax1, yMax1);
683 const SMDS_MeshNode* n22 = orientedSide.cornerNode( xMax2, yMax2);
684 if ( n1 == n12 && n2 == n22 )
687 error(SMESH_Comment("Failed to orient a block side found by edge ")<<sharedSideEdge1
688 << " of side " << startBlockSide
689 << " of block " << _blocks.size());
693 //================================================================================
695 * \brief: Fill rows (which are actually columns,if !alongN1N2) of nodes starting
696 * from the given quadrangle until another block corner encounters.
697 * n1 and n2 are at bottom of quad, n1 is at block corner.
699 //================================================================================
701 bool _Skin::fillRowsUntilCorner(const SMDS_MeshElement* quad,
702 const SMDS_MeshNode* n1,
703 const SMDS_MeshNode* n2,
704 vector<const SMDS_MeshNode*>& row1,
705 vector<const SMDS_MeshNode*>& row2,
706 const bool alongN1N2 )
708 const SMDS_MeshNode* corner1 = n1;
710 // Store nodes of quad in the rows and find new n1 and n2 to get
711 // the next face so that new n2 is on block edge
712 int i1 = quad->GetNodeIndex( n1 );
713 int i2 = quad->GetNodeIndex( n2 );
714 row1.clear(); row2.clear();
715 row1.push_back( n1 );
718 row1.push_back( n2 );
719 row2.push_back( oppositeNode( quad, i2 ));
720 row2.push_back( n1 = oppositeNode( quad, i1 ));
724 row2.push_back( n2 );
725 row1.push_back( n2 = oppositeNode( quad, i2 ));
726 row2.push_back( n1 = oppositeNode( quad, i1 ));
729 // Find the rest nodes
730 TIDSortedElemSet emptySet, avoidSet;
731 while ( !isCornerNode( n2 ))
733 avoidSet.clear(); avoidSet.insert( quad );
734 quad = SMESH_MeshEditor::FindFaceInSet( n1, n2, emptySet, avoidSet, &i1, &i2 );
735 if ( !isQuadrangle( quad ))
738 row1.push_back( n2 = oppositeNode( quad, i1 ));
739 row2.push_back( n1 = oppositeNode( quad, i2 ));
741 return n1 != corner1;
744 //================================================================================
746 * \brief Return a corner face by a corner node
748 //================================================================================
750 const SMDS_MeshElement* _BlockSide::getCornerFace(const SMDS_MeshNode* cornerNode) const
752 int x, y, isXMax, isYMax, found = 0;
753 for ( isXMax = 0; isXMax < 2; ++isXMax )
755 for ( isYMax = 0; isYMax < 2; ++isYMax )
757 x = isXMax ? _index._xSize-1 : 0;
758 y = isYMax ? _index._ySize-1 : 0;
759 found = ( getNode(x,y) == cornerNode );
764 if ( !found ) return 0;
765 int dx = isXMax ? -1 : +1;
766 int dy = isYMax ? -1 : +1;
767 const SMDS_MeshNode* n1 = getNode(x,y);
768 const SMDS_MeshNode* n2 = getNode(x+dx,y);
769 const SMDS_MeshNode* n3 = getNode(x,y+dy);
770 const SMDS_MeshNode* n4 = getNode(x+dx,y+dy);
771 return SMDS_Mesh::FindFace(n1, n2, n3, n4 );
776 //=======================================================================
777 //function : StdMeshers_HexaFromSkin_3D
779 //=======================================================================
781 StdMeshers_HexaFromSkin_3D::StdMeshers_HexaFromSkin_3D(int hypId, int studyId, SMESH_Gen* gen)
782 :SMESH_3D_Algo(hypId, studyId, gen)
784 MESSAGE("StdMeshers_HexaFromSkin_3D::StdMeshers_HexaFromSkin_3D");
785 _name = "HexaFromSkin_3D";
788 StdMeshers_HexaFromSkin_3D::~StdMeshers_HexaFromSkin_3D()
790 MESSAGE("StdMeshers_HexaFromSkin_3D::~StdMeshers_HexaFromSkin_3D");
793 //================================================================================
795 * \brief Main method, which generates hexaheda
797 //================================================================================
799 bool StdMeshers_HexaFromSkin_3D::Compute(SMESH_Mesh & aMesh, SMESH_MesherHelper* aHelper)
802 int nbBlocks = skin.findBlocks(aMesh);
804 return error( skin.error());
806 vector< vector< const SMDS_MeshNode* > > columns;
807 int x, xSize, y, ySize, z, zSize;
810 for ( int i = 0; i < nbBlocks; ++i )
812 const _Block& block = skin.getBlock( i );
814 // ------------------------------------------
815 // Fill columns of nodes with existing nodes
816 // ------------------------------------------
818 xSize = block.getSide(B_BOTTOM).getHoriSize();
819 ySize = block.getSide(B_BOTTOM).getVertSize();
820 zSize = block.getSide(B_FRONT ).getVertSize();
821 int X = xSize - 1, Y = ySize - 1, Z = zSize - 1;
822 colIndex = _Indexer( xSize, ySize );
823 columns.resize( colIndex.size() );
825 // fill node columns by front and back box sides
826 for ( x = 0; x < xSize; ++x ) {
827 vector< const SMDS_MeshNode* >& column0 = columns[ colIndex( x, 0 )];
828 vector< const SMDS_MeshNode* >& column1 = columns[ colIndex( x, Y )];
829 column0.resize( zSize );
830 column1.resize( zSize );
831 for ( z = 0; z < zSize; ++z ) {
832 column0[ z ] = block.getSide(B_FRONT).node( x, z );
833 column1[ z ] = block.getSide(B_BACK) .node( x, z );
836 // fill node columns by left and right box sides
837 for ( y = 1; y < ySize-1; ++y ) {
838 vector< const SMDS_MeshNode* >& column0 = columns[ colIndex( 0, y )];
839 vector< const SMDS_MeshNode* >& column1 = columns[ colIndex( X, y )];
840 column0.resize( zSize );
841 column1.resize( zSize );
842 for ( z = 0; z < zSize; ++z ) {
843 column0[ z ] = block.getSide(B_LEFT) .node( y, z );
844 column1[ z ] = block.getSide(B_RIGHT).node( y, z );
847 // get nodes from top and bottom box sides
848 for ( x = 1; x < xSize-1; ++x ) {
849 for ( y = 1; y < ySize-1; ++y ) {
850 vector< const SMDS_MeshNode* >& column = columns[ colIndex( x, y )];
851 column.resize( zSize );
852 column.front() = block.getSide(B_BOTTOM).node( x, y );
853 column.back() = block.getSide(B_TOP) .node( x, y );
857 // ----------------------------
858 // Add internal nodes of a box
859 // ----------------------------
860 // projection points of internal nodes on box subshapes by which
861 // coordinates of internal nodes are computed
862 vector<gp_XYZ> pointOnShape( SMESH_Block::ID_Shell );
864 // projections on vertices are constant
865 pointOnShape[ SMESH_Block::ID_V000 ] = block.getSide(B_BOTTOM).xyz( 0, 0 );
866 pointOnShape[ SMESH_Block::ID_V100 ] = block.getSide(B_BOTTOM).xyz( X, 0 );
867 pointOnShape[ SMESH_Block::ID_V010 ] = block.getSide(B_BOTTOM).xyz( 0, Y );
868 pointOnShape[ SMESH_Block::ID_V110 ] = block.getSide(B_BOTTOM).xyz( X, Y );
869 pointOnShape[ SMESH_Block::ID_V001 ] = block.getSide(B_TOP).xyz( 0, 0 );
870 pointOnShape[ SMESH_Block::ID_V101 ] = block.getSide(B_TOP).xyz( X, 0 );
871 pointOnShape[ SMESH_Block::ID_V011 ] = block.getSide(B_TOP).xyz( 0, Y );
872 pointOnShape[ SMESH_Block::ID_V111 ] = block.getSide(B_TOP).xyz( X, Y );
874 for ( x = 1; x < xSize-1; ++x )
876 gp_XYZ params; // normalized parameters of internal node within a unit box
877 params.SetCoord( 1, x / double(X) );
878 for ( y = 1; y < ySize-1; ++y )
880 params.SetCoord( 2, y / double(Y) );
881 // column to fill during z loop
882 vector< const SMDS_MeshNode* >& column = columns[ colIndex( x, y )];
883 // projections on horizontal edges
884 pointOnShape[ SMESH_Block::ID_Ex00 ] = block.getSide(B_BOTTOM).xyz( x, 0 );
885 pointOnShape[ SMESH_Block::ID_Ex10 ] = block.getSide(B_BOTTOM).xyz( x, Y );
886 pointOnShape[ SMESH_Block::ID_E0y0 ] = block.getSide(B_BOTTOM).xyz( 0, y );
887 pointOnShape[ SMESH_Block::ID_E1y0 ] = block.getSide(B_BOTTOM).xyz( X, y );
888 pointOnShape[ SMESH_Block::ID_Ex01 ] = block.getSide(B_TOP).xyz( x, 0 );
889 pointOnShape[ SMESH_Block::ID_Ex11 ] = block.getSide(B_TOP).xyz( x, Y );
890 pointOnShape[ SMESH_Block::ID_E0y1 ] = block.getSide(B_TOP).xyz( 0, y );
891 pointOnShape[ SMESH_Block::ID_E1y1 ] = block.getSide(B_TOP).xyz( X, y );
892 // projections on horizontal sides
893 pointOnShape[ SMESH_Block::ID_Fxy0 ] = block.getSide(B_BOTTOM).xyz( x, y );
894 pointOnShape[ SMESH_Block::ID_Fxy1 ] = block.getSide(B_TOP) .xyz( x, y );
895 for ( z = 1; z < zSize-1; ++z ) // z loop
897 params.SetCoord( 3, z / double(Z) );
898 // projections on vertical edges
899 pointOnShape[ SMESH_Block::ID_E00z ] = block.getSide(B_FRONT).xyz( 0, z );
900 pointOnShape[ SMESH_Block::ID_E10z ] = block.getSide(B_FRONT).xyz( X, z );
901 pointOnShape[ SMESH_Block::ID_E01z ] = block.getSide(B_BACK).xyz( 0, z );
902 pointOnShape[ SMESH_Block::ID_E11z ] = block.getSide(B_BACK).xyz( X, z );
903 // projections on vertical sides
904 pointOnShape[ SMESH_Block::ID_Fx0z ] = block.getSide(B_FRONT).xyz( x, z );
905 pointOnShape[ SMESH_Block::ID_Fx1z ] = block.getSide(B_BACK) .xyz( x, z );
906 pointOnShape[ SMESH_Block::ID_F0yz ] = block.getSide(B_LEFT) .xyz( y, z );
907 pointOnShape[ SMESH_Block::ID_F1yz ] = block.getSide(B_RIGHT).xyz( y, z );
909 // compute internal node coordinates
911 SMESH_Block::ShellPoint( params, pointOnShape, coords );
912 column[ z ] = aHelper->AddNode( coords.X(), coords.Y(), coords.Z() );
916 //cout << "----------------------------------------------------------------------"<<endl;
917 //for ( int id = SMESH_Block::ID_V000; id < SMESH_Block::ID_Shell; ++id)
919 // gp_XYZ p = pointOnShape[ id ];
920 // SMESH_Block::DumpShapeID( id,cout)<<" ( "<<p.X()<<", "<<p.Y()<<", "<<p.Z()<<" )"<<endl;
922 //cout << "Params: ( "<< params.X()<<", "<<params.Y()<<", "<<params.Z()<<" )"<<endl;
923 //cout << "coords: ( "<< coords.X()<<", "<<coords.Y()<<", "<<coords.Z()<<" )"<<endl;
932 // find out orientation
933 const SMDS_MeshNode* n000 = block.getSide(B_BOTTOM).cornerNode( 0, 0 );
934 const SMDS_MeshNode* n100 = block.getSide(B_BOTTOM).cornerNode( 1, 0 );
935 const SMDS_MeshNode* n010 = block.getSide(B_BOTTOM).cornerNode( 0, 1 );
936 const SMDS_MeshNode* n110 = block.getSide(B_BOTTOM).cornerNode( 1, 1 );
937 const SMDS_MeshNode* n001 = block.getSide(B_TOP).cornerNode( 0, 0 );
938 const SMDS_MeshNode* n101 = block.getSide(B_TOP).cornerNode( 1, 0 );
939 const SMDS_MeshNode* n011 = block.getSide(B_TOP).cornerNode( 0, 1 );
940 const SMDS_MeshNode* n111 = block.getSide(B_TOP).cornerNode( 1, 1 );
941 SMDS_VolumeOfNodes probeVolume (n000,n010,n110,n100,
942 n001,n011,n111,n101);
943 bool isForw = SMDS_VolumeTool( &probeVolume ).IsForward();
946 for ( x = 0; x < xSize-1; ++x ) {
947 for ( y = 0; y < ySize-1; ++y ) {
948 vector< const SMDS_MeshNode* >& col00 = columns[ colIndex( x, y )];
949 vector< const SMDS_MeshNode* >& col10 = columns[ colIndex( x+1, y )];
950 vector< const SMDS_MeshNode* >& col01 = columns[ colIndex( x, y+1 )];
951 vector< const SMDS_MeshNode* >& col11 = columns[ colIndex( x+1, y+1 )];
952 // bottom face normal of a hexa mush point outside the volume
954 for ( z = 0; z < zSize-1; ++z )
955 aHelper->AddVolume(col00[z], col01[z], col11[z], col10[z],
956 col00[z+1], col01[z+1], col11[z+1], col10[z+1]);
958 for ( z = 0; z < zSize-1; ++z )
959 aHelper->AddVolume(col00[z], col10[z], col11[z], col01[z],
960 col00[z+1], col10[z+1], col11[z+1], col01[z+1]);
968 //================================================================================
970 * \brief Evaluate nb of hexa
972 //================================================================================
974 bool StdMeshers_HexaFromSkin_3D::Evaluate(SMESH_Mesh & aMesh,
975 const TopoDS_Shape & aShape,
976 MapShapeNbElems& aResMap)
979 int nbBlocks = skin.findBlocks(aMesh);
981 return error( skin.error());
983 bool secondOrder = aMesh.NbFaces( ORDER_QUADRATIC );
985 int entity = secondOrder ? SMDSEntity_Quad_Hexa : SMDSEntity_Hexa;
986 vector<int>& nbByType = aResMap[ aMesh.GetSubMesh( aShape )];
987 if ( entity >= nbByType.size() )
988 nbByType.resize( SMDSEntity_Last, 0 );
990 for ( int i = 0; i < nbBlocks; ++i )
992 const _Block& block = skin.getBlock( i );
994 int nbX = block.getSide(B_BOTTOM).getHoriSize();
995 int nbY = block.getSide(B_BOTTOM).getVertSize();
996 int nbZ = block.getSide(B_FRONT ).getVertSize();
998 int nbHexa = (nbX-1) * (nbY-1) * (nbZ-1);
999 int nbNodes = (nbX-2) * (nbY-2) * (nbZ-2);
1002 (nbX-2) * (nbY-2) * (nbZ-1) +
1003 (nbX-2) * (nbY-1) * (nbZ-2) +
1004 (nbX-1) * (nbY-2) * (nbZ-2);
1007 nbByType[ entity ] += nbHexa;
1008 nbByType[ SMDSEntity_Node ] += nbNodes;
1014 //================================================================================
1016 * \brief Abstract method must be defined but does nothing
1018 //================================================================================
1020 bool StdMeshers_HexaFromSkin_3D::CheckHypothesis(SMESH_Mesh&, const TopoDS_Shape&,
1021 Hypothesis_Status& aStatus)
1023 aStatus = SMESH_Hypothesis::HYP_OK;
1027 //================================================================================
1029 * \brief Abstract method must be defined but just reports an error as this
1030 * algo is not intended to work with shapes
1032 //================================================================================
1034 bool StdMeshers_HexaFromSkin_3D::Compute(SMESH_Mesh&, const TopoDS_Shape&)
1036 return error("Algorithm can't work with geometrical shapes");