Salome HOME
Copyrights update 2015.
[plugins/hexablockplugin.git] / src / HEXABLOCKPlugin / HEXABLOCKPlugin_FromSkin_3D.cxx
1 // Copyright (C) 2009-2015  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 // File      : SMESH_HexaFromSkin_3D.cxx
21 // Created   : Wed Jan 27 12:28:07 2010
22 // Author    : Edward AGAPOV (eap)
23 //
24 #include "HEXABLOCKPlugin_FromSkin_3D.hxx"
25
26 #include "SMDS_VolumeOfNodes.hxx"
27 #include "SMDS_VolumeTool.hxx"
28 #include "SMESH_Block.hxx"
29 #include "SMESH_MesherHelper.hxx"
30 #include "SMESH_MeshEditor.hxx"
31 #include "SMESH_MeshAlgos.hxx"
32
33 #include <gp_Ax2.hxx>
34
35 //#include "utilities.h"
36 #include <limits>
37
38 // Define error message and _MYDEBUG_ if needed
39 #ifdef _DEBUG_
40 #define BAD_MESH_ERR \
41   error(SMESH_Comment("Can't detect block-wise structure of the input 2D mesh.\n" \
42                       __FILE__ ":" )<<__LINE__)
43 //#define _MYDEBUG_
44 #else
45 #define BAD_MESH_ERR \
46   error(SMESH_Comment("Can't detect block-wise structure of the input 2D mesh"))
47 #endif
48
49 // Debug output
50 #ifdef _MYDEBUG_
51 #define _DUMP_(msg) cout << msg << endl
52 #else
53 #define _DUMP_(msg)
54 #endif
55
56
57 #ifdef _DEBUG_
58 static int MYDEBUG = HEXA_NS::on_debug ();
59 #else
60 static int MYDEBUG = 0;
61 #endif
62
63
64 namespace
65 {
66   enum EBoxSides //!< sides of the block
67     {
68       B_BOTTOM=0, B_RIGHT, B_TOP, B_LEFT, B_FRONT, B_BACK, NB_BLOCK_SIDES
69     };
70 #ifdef _MYDEBUG_
71   const char* SBoxSides[] = //!< names of block sides
72     {
73       "BOTTOM", "RIGHT", "TOP", "LEFT", "FRONT", "BACK", "UNDEFINED"
74     };
75 #endif    
76   enum EQuadEdge //!< edges of quadrangle side
77     {
78       Q_BOTTOM = 0, Q_RIGHT, Q_TOP, Q_LEFT, NB_QUAD_SIDES
79     };
80
81
82   //================================================================================
83   /*!
84    * \brief return logical coordinates (i.e. min or max) of ends of edge
85    */
86   //================================================================================
87
88   bool getEdgeEnds(EQuadEdge edge, bool& xMax1, bool& yMax1, bool& xMax2, bool& yMax2 )
89   {
90     xMax1=0, yMax1=0, xMax2=1, yMax2=1;
91     switch( edge )
92     {
93     case Q_BOTTOM: yMax2 = 0; break;
94     case Q_RIGHT:  xMax1 = 1; break;
95     case Q_TOP:    yMax1 = 1; break;
96     case Q_LEFT:   xMax2 = 0; break;
97     default:
98       return false;
99     }
100     return true;
101   }
102
103   //================================================================================
104   /*!
105    * \brief return true if a node is at block corner
106    *
107    * This check is valid for simple cases only
108    */
109   //================================================================================
110
111   bool isCornerNode( const SMDS_MeshNode* n )
112   {
113     int nbF = n ? n->NbInverseElements( SMDSAbs_Face ) : 1;
114     if ( nbF % 2 )
115       return true;
116
117     set<const SMDS_MeshNode*> nodesInInverseFaces;
118     SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face );
119     while ( fIt->more() )
120     {
121       const SMDS_MeshElement* face = fIt->next();
122       nodesInInverseFaces.insert( face->begin_nodes(), face->end_nodes() );
123     }
124
125     return nodesInInverseFaces.size() != ( 6 + (nbF/2-1)*3 );
126   }
127
128   //================================================================================
129   /*!
130    * \brief check element type
131    */
132   //================================================================================
133
134   bool isQuadrangle(const SMDS_MeshElement* e)
135   {
136     return ( e && e->NbCornerNodes() == 4 );
137   }
138
139   //================================================================================
140   /*!
141    * \brief return opposite node of a quadrangle face
142    */
143   //================================================================================
144
145   const SMDS_MeshNode* oppositeNode(const SMDS_MeshElement* quad, int iNode)
146   {
147     return quad->GetNode( (iNode+2) % 4 );
148   }
149
150   //================================================================================
151   /*!
152    * \brief Convertor of a pair of integers to a sole index
153    */
154   struct _Indexer
155   {
156     int _xSize, _ySize;
157     _Indexer( int xSize=0, int ySize=0 ): _xSize(xSize), _ySize(ySize) {}
158     int size() const { return _xSize * _ySize; }
159     int operator()(int x, int y) const { return y * _xSize + x; }
160   };
161   //================================================================================
162   /*!
163    * \brief Oriented convertor of a pair of integers to a sole index 
164    */
165   class _OrientedIndexer : public _Indexer
166   {
167   public:
168     enum OriFlags //!< types of block side orientation
169       {
170         REV_X = 1, REV_Y = 2, SWAP_XY = 4, MAX_ORI = REV_X|REV_Y|SWAP_XY
171       };
172     _OrientedIndexer( const _Indexer& indexer, const int oriFlags ):
173       _Indexer( indexer._xSize, indexer._ySize ),
174       _xSize (indexer._xSize), _ySize(indexer._ySize),
175       _xRevFun((oriFlags & REV_X) ? & reverse : & lazy),
176       _yRevFun((oriFlags & REV_Y) ? & reverse : & lazy),
177       _swapFun((oriFlags & SWAP_XY ) ? & swap : & lazy)
178     {
179       (*_swapFun)( _xSize, _ySize );
180     }
181     //!< Return index by XY
182     int operator()(int x, int y) const
183     {
184       (*_xRevFun)( x, const_cast<int&>( _xSize ));
185       (*_yRevFun)( y, const_cast<int&>( _ySize ));
186       (*_swapFun)( x, y );
187       return _Indexer::operator()(x,y);
188     }
189     //!< Return index for a corner
190     int corner(bool xMax, bool yMax) const
191     {
192       int x = xMax, y = yMax, size = 2;
193       (*_xRevFun)( x, size );
194       (*_yRevFun)( y, size );
195       (*_swapFun)( x, y );
196       return _Indexer::operator()(x ? _Indexer::_xSize-1 : 0 , y ? _Indexer::_ySize-1 : 0);
197     }
198     int xSize() const { return _xSize; }
199     int ySize() const { return _ySize; }
200   private:
201     _Indexer _indexer;
202     int _xSize, _ySize;
203
204     typedef void (*TFun)(int& x, int& y);
205     TFun _xRevFun, _yRevFun, _swapFun;
206     
207     static void lazy   (int&, int&) {}
208     static void reverse(int& x, int& size) { x = size - x - 1; }
209     static void swap   (int& x, int& y) { std::swap(x,y); }
210   };
211   //================================================================================
212   /*!
213    * \brief Structure corresponding to the meshed side of block
214    */
215   struct _BlockSide
216   {
217     vector<const SMDS_MeshNode*> _grid;
218     _Indexer                     _index;
219     int                          _nbBlocksExpected;
220     int                          _nbBlocksFound;
221
222 #ifdef _DEBUG_ // want to get SIGSEGV in case of invalid index
223 #define _grid_access_(pobj, i) pobj->_grid[ ((i) < pobj->_grid.size()) ? i : int(1e100)]
224 #else
225 #define _grid_access_(pobj, i) pobj->_grid[ i ]
226 #endif
227     //!< Return node at XY
228     const SMDS_MeshNode* getNode(int x, int y) const { return _grid_access_(this, _index( x,y ));}
229     //!< Set node at XY
230     void setNode(int x, int y, const SMDS_MeshNode* n) { _grid_access_(this, _index( x,y )) = n; }
231     //!< Return an edge
232     SMESH_OrientedLink getEdge(EQuadEdge edge) const
233     {
234       bool x1, y1, x2, y2; getEdgeEnds( edge, x1, y1, x2, y2 );
235       return SMESH_OrientedLink( getCornerNode ( x1, y1 ), getCornerNode( x2, y2 ));
236     }
237     //!< Return a corner node
238     const SMDS_MeshNode* getCornerNode(bool isXMax, bool isYMax) const
239     {
240       return getNode( isXMax ? _index._xSize-1 : 0 , isYMax ? _index._ySize-1 : 0 );
241     }
242     const SMDS_MeshElement* getCornerFace(const SMDS_MeshNode* cornerNode) const;
243     //!< True if all blocks this side belongs to have been found
244     bool isBound() const { return _nbBlocksExpected <= _nbBlocksFound; }
245     //!< Return coordinates of node at XY
246     gp_XYZ getXYZ(int x, int y) const { return SMESH_TNodeXYZ( getNode( x, y )); }
247     //!< Return gravity center of the four corners and the middle node
248     gp_XYZ getGC() const
249     {
250       gp_XYZ xyz =
251         getXYZ( 0, 0 ) +
252         getXYZ( _index._xSize-1, 0 ) +
253         getXYZ( 0, _index._ySize-1 ) +
254         getXYZ( _index._xSize-1, _index._ySize-1 ) +
255         getXYZ( _index._xSize/2, _index._ySize/2 );
256       return xyz / 5;
257     }
258     //!< Return number of mesh faces
259     int getNbFaces() const { return (_index._xSize-1) * (_index._ySize-1); }
260   };
261   //================================================================================
262   /*!
263    * \brief _BlockSide with changed orientation
264    */
265   struct _OrientedBlockSide
266   {
267     _BlockSide*       _side;
268     _OrientedIndexer  _index;
269
270     _OrientedBlockSide( _BlockSide* side=0, const int oriFlags=0 ):
271       _side(side), _index(side ? side->_index : _Indexer(), oriFlags ) {}
272     //!< return coordinates by XY
273     gp_XYZ xyz(int x, int y) const
274     {
275       return SMESH_TNodeXYZ( _grid_access_(_side, _index( x, y )) );
276     }
277     //!< safely return a node by XY
278     const SMDS_MeshNode* node(int x, int y) const
279     {
280       int i = _index( x, y );
281       return ( i < 0 || i >= _side->_grid.size()) ? 0 : _side->_grid[i];
282     }
283     //!< Return an edge
284     SMESH_OrientedLink edge(EQuadEdge edge) const
285     {
286       bool x1, y1, x2, y2; getEdgeEnds( edge, x1, y1, x2, y2 );
287       return SMESH_OrientedLink( cornerNode ( x1, y1 ), cornerNode( x2, y2 ));
288     }
289     //!< Return a corner node
290     const SMDS_MeshNode* cornerNode(bool isXMax, bool isYMax) const
291     {
292       return _grid_access_(_side, _index.corner( isXMax, isYMax ));
293     }
294     //!< return its size in nodes
295     int getHoriSize() const { return _index.xSize(); }
296     int getVertSize() const  { return _index.ySize(); }
297     //!< True if _side has been initialized
298     operator bool() const { return _side; }
299     //! Direct access to _side
300     const _BlockSide* operator->() const { return _side; }
301     _BlockSide* operator->() { return _side; }
302   };
303   //================================================================================
304   /*!
305    * \brief Meshed skin of block
306    */
307   struct _Block
308   {
309     _OrientedBlockSide        _side[6]; // 6 sides of a sub-block
310     set<const SMDS_MeshNode*> _corners;
311
312     const _OrientedBlockSide& getSide(int i) const { return _side[i]; }
313     bool setSide( int i, const _OrientedBlockSide& s)
314     {
315       if (( _side[i] = s ))
316       {
317         _corners.insert( s.cornerNode(0,0));
318         _corners.insert( s.cornerNode(1,0));
319         _corners.insert( s.cornerNode(0,1));
320         _corners.insert( s.cornerNode(1,1));
321       }
322       return s;
323     }
324     void clear() { for (int i=0;i<6;++i) _side[i]=0; _corners.clear(); }
325     bool hasSide( const _OrientedBlockSide& s) const
326     {
327       if ( s ) for (int i=0;i<6;++i) if ( _side[i] && _side[i]._side == s._side ) return true;
328       return false;
329     }
330     int nbSides() const { int n=0; for (int i=0;i<6;++i) if ( _side[i] ) ++n; return n; }
331     bool isValid() const;
332   };
333   //================================================================================
334   /*!
335    * \brief Skin mesh possibly containing several meshed blocks
336    */
337   class _Skin
338   {
339   public:
340
341     int findBlocks(SMESH_Mesh& mesh);
342     //!< return i-th block
343     const _Block& getBlock(int i) const { return _blocks[i]; }
344     //!< return error description
345     const SMESH_Comment& error() const { return _error; }
346
347   private:
348     bool fillSide( _BlockSide&             side,
349                    const SMDS_MeshElement* cornerQuad,
350                    const SMDS_MeshNode*    cornerNode);
351     bool fillRowsUntilCorner(const SMDS_MeshElement* quad,
352                              const SMDS_MeshNode*    n1,
353                              const SMDS_MeshNode*    n2,
354                              vector<const SMDS_MeshNode*>& verRow1,
355                              vector<const SMDS_MeshNode*>& verRow2,
356                              bool alongN1N2 );
357     _OrientedBlockSide findBlockSide( EBoxSides           startBlockSide,
358                                       EQuadEdge           sharedSideEdge1,
359                                       EQuadEdge           sharedSideEdge2,
360                                       bool                withGeometricAnalysis,
361                                       set< _BlockSide* >& sidesAround);
362     //!< update own data and data of the side bound to block
363     void setSideBoundToBlock( _BlockSide& side )
364     {
365       if ( side._nbBlocksFound++, side.isBound() )
366         for ( int e = 0; e < int(NB_QUAD_SIDES); ++e )
367           _edge2sides[ side.getEdge( (EQuadEdge) e ) ].erase( &side );
368     }
369     //!< store reason of error
370     int error(const SMESH_Comment& reason) { _error = reason; return 0; }
371
372     SMESH_Comment      _error;
373
374     list< _BlockSide > _allSides;
375     vector< _Block >   _blocks;
376
377     //map< const SMDS_MeshNode*, set< _BlockSide* > > _corner2sides;
378     map< SMESH_OrientedLink, set< _BlockSide* > > _edge2sides;
379   };
380
381   //================================================================================
382   /*!
383    * \brief Find and return number of submeshes corresponding to blocks
384    */
385   //================================================================================
386
387   int _Skin::findBlocks(SMESH_Mesh& mesh)
388   {
389     SMESHDS_Mesh* meshDS = mesh.GetMeshDS();
390
391     // Find a node at any block corner
392
393     SMDS_NodeIteratorPtr nIt = meshDS->nodesIterator(/*idInceasingOrder=*/true);
394     if ( !nIt->more() ) return error("Empty mesh");
395
396     const SMDS_MeshNode* nCorner = 0;
397     while ( nIt->more() )
398     {
399       nCorner = nIt->next();
400       if ( isCornerNode( nCorner ))
401         break;
402       else
403         nCorner = 0;
404     }
405     if ( !nCorner )
406       return BAD_MESH_ERR;
407
408     // --------------------------------------------------------------------
409     // Find all block sides starting from mesh faces sharing the corner node
410     // --------------------------------------------------------------------
411
412     int nbFacesOnSides = 0;
413     TIDSortedElemSet cornerFaces; // corner faces of found _BlockSide's
414     list< const SMDS_MeshNode* > corners( 1, nCorner );
415     list< const SMDS_MeshNode* >::iterator corner = corners.begin();
416     while ( corner != corners.end() )
417     {
418       SMDS_ElemIteratorPtr faceIt = (*corner)->GetInverseElementIterator( SMDSAbs_Face );
419       while ( faceIt->more() )
420       {
421         const SMDS_MeshElement* face = faceIt->next();
422         if ( !cornerFaces.insert( face ).second )
423           continue; // already loaded block side
424
425         if ( !isQuadrangle( face ))
426           return error("Non-quadrangle elements in the input mesh");
427
428         if ( _allSides.empty() || !_allSides.back()._grid.empty() )
429           _allSides.push_back( _BlockSide() );
430
431         _BlockSide& side = _allSides.back();
432         if ( !fillSide( side, face, *corner ) )
433         {
434           if ( !_error.empty() )
435             return false;
436         }
437         else
438         {
439           for ( int isXMax = 0; isXMax < 2; ++isXMax )
440             for ( int isYMax = 0; isYMax < 2; ++isYMax )
441             {
442               const SMDS_MeshNode* nCorner = side.getCornerNode(isXMax,isYMax );
443               corners.push_back( nCorner );
444               cornerFaces.insert( side.getCornerFace( nCorner ));
445             }
446           for ( int e = 0; e < int(NB_QUAD_SIDES); ++e )
447             _edge2sides[ side.getEdge( (EQuadEdge) e ) ].insert( &side );
448
449           nbFacesOnSides += side.getNbFaces();
450         }
451       }
452       ++corner;
453
454       // find block sides of other domains if any
455       if ( corner == corners.end() && nbFacesOnSides < mesh.NbQuadrangles() )
456       {
457         while ( nIt->more() )
458         {
459           nCorner = nIt->next();
460           if ( isCornerNode( nCorner ))
461             corner = corners.insert( corner, nCorner );
462         }
463         nbFacesOnSides = mesh.NbQuadrangles();
464       }
465     }
466     
467     if ( _allSides.empty() )
468       return BAD_MESH_ERR;
469     if ( _allSides.back()._grid.empty() )
470       _allSides.pop_back();
471     _DUMP_("Nb detected sides "<< _allSides.size());
472
473     // ---------------------------
474     // Organize sides into blocks
475     // ---------------------------
476
477     // analyse sharing of sides by blocks and sort sides by nb of adjacent sides
478     int nbBlockSides = 0; // total nb of block sides taking into account their sharing
479     multimap<int, _BlockSide* > sortedSides;
480     {
481       list < _BlockSide >::iterator sideIt = _allSides.begin();
482       for ( ; sideIt != _allSides.end(); ++sideIt )
483       {
484         _BlockSide& side = *sideIt;
485         bool isSharedSide = true;
486         int nbAdjacent = 0;
487         for ( int e = 0; e < int(NB_QUAD_SIDES) && isSharedSide; ++e )
488         {
489           int nbAdj = _edge2sides[ side.getEdge( (EQuadEdge) e ) ].size();
490           nbAdjacent += nbAdj;
491           isSharedSide = ( nbAdj > 2 );
492         }
493         side._nbBlocksFound = 0;
494         side._nbBlocksExpected = isSharedSide ? 2 : 1;
495         nbBlockSides += side._nbBlocksExpected;
496         sortedSides.insert( make_pair( nbAdjacent, & side ));
497       }
498     }
499
500     // find sides of each block
501     int nbBlocks = 0;
502     while ( nbBlockSides >= 6 )
503     {
504       // get any side not bound to all blocks it belongs to
505       multimap<int, _BlockSide*>::iterator i_side = sortedSides.begin();
506       while ( i_side != sortedSides.end() && i_side->second->isBound())
507         ++i_side;
508
509       // start searching for block sides from the got side
510       bool ok = true;
511       if ( _blocks.empty() || _blocks.back()._side[B_FRONT] )
512         _blocks.resize( _blocks.size() + 1 );
513
514       _Block& block = _blocks.back();
515       block.setSide( B_FRONT, i_side->second );
516       setSideBoundToBlock( *i_side->second );
517       nbBlockSides--;
518
519       // edges of adjacent sides of B_FRONT corresponding to front's edges
520       EQuadEdge edgeOfFront[4] = { Q_BOTTOM, Q_RIGHT, Q_TOP, Q_LEFT };
521       EQuadEdge edgeOfAdj  [4] = { Q_BOTTOM, Q_LEFT, Q_BOTTOM, Q_LEFT };
522       // first find all sides detectable w/o advanced analysis,
523       // then repeat the search, which then may pass without advanced analysis
524       set< _BlockSide* > sidesAround;
525       for ( int advAnalys = 0; advAnalys < 2; ++advAnalys )
526       {
527         // try to find 4 sides adjacent to a FRONT side
528         for ( int i = 0; (ok || !advAnalys) && i < NB_QUAD_SIDES; ++i )
529           if ( !block._side[i] )
530             ok = block.setSide( i, findBlockSide( B_FRONT, edgeOfFront[i], edgeOfAdj[i],
531                                                   advAnalys, sidesAround));
532         // try to find a BACK side by a TOP one
533         if ( ok || !advAnalys)
534           if ( !block._side[B_BACK] && block._side[B_TOP] )
535             ok = block.setSide( B_BACK, findBlockSide( B_TOP, Q_TOP, Q_TOP,
536                                                        advAnalys, sidesAround ));
537         if ( !advAnalys ) ok = true;
538       }
539       ok = block.isValid();
540       if ( ok )
541       {
542         // check if just found block is same as one of previously found blocks
543         bool isSame = false;
544         for ( int i = 1; i < _blocks.size() && !isSame; ++i )
545           isSame = ( block._corners == _blocks[i-1]._corners );
546         ok = !isSame;
547       }
548
549       // count the found sides
550       _DUMP_(endl << "** Block " << _blocks.size() << " valid: " << block.isValid());
551       for (int i = 0; i < NB_BLOCK_SIDES; ++i )
552       {
553         _DUMP_("\tSide "<< SBoxSides[i] <<" "<< block._side[ i ]._side);
554         if ( block._side[ i ] )
555         {
556           if ( ok && i != B_FRONT)
557           {
558             setSideBoundToBlock( *block._side[ i ]._side );
559             nbBlockSides--;
560           }
561           _DUMP_("\t corners "<<
562                  block._side[ i ].cornerNode(0,0)->GetID() << ", " <<
563                  block._side[ i ].cornerNode(1,0)->GetID() << ", " <<
564                  block._side[ i ].cornerNode(1,1)->GetID() << ", " <<
565                  block._side[ i ].cornerNode(0,1)->GetID() << ", "<<endl);
566         }
567         else
568         {
569           _DUMP_("\t not found"<<endl);
570         }
571       }
572       if ( !ok )
573         block.clear();
574       else
575         nbBlocks++;
576     }
577     _DUMP_("Nb found blocks "<< nbBlocks <<endl);
578
579     if ( nbBlocks == 0 && _error.empty() )
580       return BAD_MESH_ERR;
581
582     return nbBlocks;
583   }
584
585   //================================================================================
586   /*!
587    * \brief Fill block side data starting from its corner quadrangle
588    */
589   //================================================================================
590
591   bool _Skin::fillSide( _BlockSide&             side,
592                         const SMDS_MeshElement* cornerQuad,
593                         const SMDS_MeshNode*    nCorner)
594   {
595     // Find out size of block side mesured in nodes and by the way find two rows
596     // of nodes in two directions.
597
598     int x, y, nbX, nbY;
599     const SMDS_MeshElement* firstQuad = cornerQuad;
600     {
601       // get a node on block edge
602       int iCorner = firstQuad->GetNodeIndex( nCorner );
603       const SMDS_MeshNode* nOnEdge = firstQuad->GetNode( (iCorner+1) % 4);
604
605       // find out size of block side
606       vector<const SMDS_MeshNode*> horRow1, horRow2, verRow1, verRow2;
607       if ( !fillRowsUntilCorner( firstQuad, nCorner, nOnEdge, horRow1, horRow2, true ) ||
608            !fillRowsUntilCorner( firstQuad, nCorner, nOnEdge, verRow1, verRow2, false ))
609         return false;
610       nbX = horRow1.size(), nbY = verRow1.size();
611
612       // store found nodes
613       side._index._xSize = horRow1.size();
614       side._index._ySize = verRow1.size();
615       side._grid.resize( side._index.size(), NULL );
616
617       for ( x = 0; x < horRow1.size(); ++x )
618       {
619         side.setNode( x, 0, horRow1[x] );
620         side.setNode( x, 1, horRow2[x] );
621       }
622       for ( y = 0; y < verRow1.size(); ++y )
623       {
624         side.setNode( 0, y, verRow1[y] );
625         side.setNode( 1, y, verRow2[y] );
626       }
627     }
628     // Find the rest nodes
629
630     y = 1; // y of the row to fill
631     TIDSortedElemSet emptySet, avoidSet;
632     while ( ++y < nbY )
633     {
634       // get next firstQuad in the next row of quadrangles
635       //
636       //          n2up
637       //     o---o               <- y row
638       //     |   |
639       //     o---o  o  o  o  o   <- found nodes
640       //n1down    n2down       
641       //
642       int i1down, i2down, i2up;
643       const SMDS_MeshNode* n1down = side.getNode( 0, y-1 );
644       const SMDS_MeshNode* n2down = side.getNode( 1, y-1 );
645       avoidSet.clear(); avoidSet.insert( firstQuad );
646       firstQuad = SMESH_MeshAlgos::FindFaceInSet( n1down, n2down, emptySet, avoidSet,
647                                                   &i1down, &i2down);
648       if ( !isQuadrangle( firstQuad ))
649         return BAD_MESH_ERR;
650
651       const SMDS_MeshNode* n2up = oppositeNode( firstQuad, i1down );
652       avoidSet.clear(); avoidSet.insert( firstQuad );
653
654       // find the rest nodes in the y-th row by faces in the row
655
656       x = 1; 
657       while ( ++x < nbX )
658       {
659         const SMDS_MeshElement* quad = SMESH_MeshAlgos::FindFaceInSet( n2up, n2down, emptySet,
660                                                                        avoidSet, &i2up, &i2down);
661         if ( !isQuadrangle( quad ))
662           return BAD_MESH_ERR;
663
664         n2up   = oppositeNode( quad, i2down );
665         n2down = oppositeNode( quad, i2up );
666         avoidSet.clear(); avoidSet.insert( quad );
667
668         side.setNode( x, y, n2up );
669       }
670     }
671
672     // check side validity
673     bool ok =
674       side.getCornerFace( side.getCornerNode( 0, 0 )) &&
675       side.getCornerFace( side.getCornerNode( 1, 0 )) &&
676       side.getCornerFace( side.getCornerNode( 0, 1 )) &&
677       side.getCornerFace( side.getCornerNode( 1, 1 ));
678
679     return ok;
680   }
681
682   //================================================================================
683   /*!
684    * \brief Return true if it's possible to make a loop over corner2Sides starting
685    * from the startSide
686    */
687   //================================================================================
688
689   bool isClosedChainOfSides( _BlockSide*                                        startSide,
690                              map< const SMDS_MeshNode*, list< _BlockSide* > > & corner2Sides )
691   {
692     // get start and end nodes
693     const SMDS_MeshNode *n1 = 0, *n2 = 0, *n;
694     for ( int y = 0; y < 2; ++y )
695       for ( int x = 0; x < 2; ++x )
696       {
697         n = startSide->getCornerNode(x,y);
698         if ( !corner2Sides.count( n )) continue;
699         if ( n1 )
700           n2 = n;
701         else
702           n1 = n;
703       }
704     if ( !n2 ) return false;
705
706     map< const SMDS_MeshNode*, list< _BlockSide* > >::iterator
707       c2sides = corner2Sides.find( n1 );
708     if ( c2sides == corner2Sides.end() ) return false;
709
710     int nbChainLinks = 1;
711     n = n1;
712     _BlockSide* prevSide = startSide;
713     while ( n != n2 )
714     {
715       // get the next side sharing n
716       list< _BlockSide* > & sides = c2sides->second;
717       _BlockSide* nextSide = ( sides.back() == prevSide ? sides.front() : sides.back() );
718       if ( nextSide == prevSide ) return false;
719
720       // find the next corner of the nextSide being in corner2Sides
721       n1 = n;
722       n = 0;
723       for ( int y = 0; y < 2 && !n; ++y )
724         for ( int x = 0; x < 2; ++x )
725         {
726           n = nextSide->getCornerNode(x,y);
727           c2sides = corner2Sides.find( n );
728           if ( n == n1 || c2sides == corner2Sides.end() )
729             n = 0;
730           else
731             break;
732         }
733       if ( !n ) return false;
734
735       prevSide = nextSide;
736       nbChainLinks++;
737     }
738
739     return ( n == n2 && nbChainLinks == NB_QUAD_SIDES );
740   }
741
742   //================================================================================
743   /*!
744    * \brief Try to find a block side adjacent to the given side by given edge
745    */
746   //================================================================================
747
748   _OrientedBlockSide _Skin::findBlockSide( EBoxSides           startBlockSide,
749                                            EQuadEdge           sharedSideEdge1,
750                                            EQuadEdge           sharedSideEdge2,
751                                            bool                withGeometricAnalysis,
752                                            set< _BlockSide* >& sidesAround)
753   {
754     _Block& block = _blocks.back();
755     _OrientedBlockSide& side1 = block._side[ startBlockSide ];
756
757     // get corner nodes of the given block edge
758     SMESH_OrientedLink edge = side1.edge( sharedSideEdge1 );
759     const SMDS_MeshNode* n1 = edge.node1();
760     const SMDS_MeshNode* n2 = edge.node2();
761     if ( edge._reversed ) swap( n1, n2 );
762
763     // find all sides sharing both nodes n1 and n2
764     set< _BlockSide* > sidesOnEdge = _edge2sides[ edge ]; // copy a set
765
766     // exclude loaded sides of block from sidesOnEdge
767     for (int i = 0; i < NB_BLOCK_SIDES; ++i )
768       if ( block._side[ i ] )
769         sidesOnEdge.erase( block._side[ i ]._side );
770
771     int nbSidesOnEdge = sidesOnEdge.size();
772     _DUMP_("nbSidesOnEdge "<< nbSidesOnEdge << " " << n1->GetID() << "-" << n2->GetID() );
773     if ( nbSidesOnEdge == 0 )
774       return 0;
775
776     _BlockSide* foundSide = 0;
777     if ( nbSidesOnEdge == 1 )
778     {
779       foundSide = *sidesOnEdge.begin();
780     }
781     else
782     {
783       set< _BlockSide* >::iterator sideIt = sidesOnEdge.begin();
784       int nbLoadedSides = block.nbSides();
785       if ( nbLoadedSides > 1 )
786       {
787         // Find the side having more than 2 corners common with already loaded sides
788         for (; !foundSide && sideIt != sidesOnEdge.end(); ++sideIt )
789         {
790           _BlockSide* sideI = *sideIt;
791           int nbCommonCorners =
792             block._corners.count( sideI->getCornerNode(0,0)) +
793             block._corners.count( sideI->getCornerNode(1,0)) +
794             block._corners.count( sideI->getCornerNode(0,1)) +
795             block._corners.count( sideI->getCornerNode(1,1));
796           if ( nbCommonCorners > 2 )
797             foundSide = sideI;
798         }
799       }
800
801       if ( !foundSide )
802       {
803         if ( !withGeometricAnalysis )
804         {
805           sidesAround.insert( sidesOnEdge.begin(), sidesOnEdge.end() );
806           return 0;
807         }
808         if ( nbLoadedSides == 1 )
809         {
810           // Issue 0021529. There are at least 2 sides by each edge and
811           // position of block gravity center is undefined.
812           // Find a side starting from which we can walk around the startBlockSide
813
814           // fill in corner2Sides
815           map< const SMDS_MeshNode*, list< _BlockSide* > > corner2Sides;
816           for ( sideIt = sidesAround.begin(); sideIt != sidesAround.end(); ++sideIt )
817           {
818             _BlockSide* sideI = *sideIt;
819             corner2Sides[ sideI->getCornerNode(0,0) ].push_back( sideI );
820             corner2Sides[ sideI->getCornerNode(1,0) ].push_back( sideI );
821             corner2Sides[ sideI->getCornerNode(0,1) ].push_back( sideI );
822             corner2Sides[ sideI->getCornerNode(1,1) ].push_back( sideI );
823           }
824           // remove corners of startBlockSide from corner2Sides
825           set<const SMDS_MeshNode*>::iterator nIt = block._corners.begin();
826           for ( ; nIt != block._corners.end(); ++nIt )
827             corner2Sides.erase( *nIt );
828
829           // select a side
830           for ( sideIt = sidesOnEdge.begin(); sideIt != sidesOnEdge.end(); ++sideIt )
831           {
832             if ( isClosedChainOfSides( *sideIt, corner2Sides ))
833             {
834               foundSide = *sideIt;
835               break;
836             }
837           }
838           if ( !foundSide )
839             return 0;
840         }
841         else
842         {
843           // Select one of found sides most close to startBlockSide
844
845           gp_XYZ p1 ( n1->X(),n1->Y(),n1->Z()),  p2 (n2->X(),n2->Y(),n2->Z());
846           gp_Vec p1p2( p1, p2 );
847
848           const SMDS_MeshElement* face1 = side1->getCornerFace( n1 );
849           gp_XYZ p1Op = SMESH_TNodeXYZ( oppositeNode( face1, face1->GetNodeIndex(n1)));
850           gp_Vec side1Dir( p1, p1Op );
851           gp_Ax2 pln( p1, p1p2, side1Dir ); // plane with normal p1p2 and X dir side1Dir
852           _DUMP_("  Select adjacent for "<< side1._side << " - side dir ("
853                  << side1Dir.X() << ", " << side1Dir.Y() << ", " << side1Dir.Z() << ")" );
854
855           map < double , _BlockSide* > angleOfSide;
856           for (sideIt = sidesOnEdge.begin(); sideIt != sidesOnEdge.end(); ++sideIt )
857           {
858             _BlockSide* sideI = *sideIt;
859             const SMDS_MeshElement* faceI = sideI->getCornerFace( n1 );
860             gp_XYZ p1Op = SMESH_TNodeXYZ( oppositeNode( faceI, faceI->GetNodeIndex(n1)));
861             gp_Vec sideIDir( p1, p1Op );
862             // compute angle of (sideIDir projection to pln) and (X dir of pln)
863             gp_Vec2d sideIDirProj( sideIDir * pln.XDirection(), sideIDir * pln.YDirection());
864             double angle = sideIDirProj.Angle( gp::DX2d() );
865             if ( angle < 0 ) angle += 2. * M_PI; // angle [0-2*PI]
866             angleOfSide.insert( make_pair( angle, sideI ));
867             _DUMP_("  "<< sideI << " - side dir ("
868                    << sideIDir.X() << ", " << sideIDir.Y() << ", " << sideIDir.Z() << ")"
869                    << " angle " << angle);
870           }
871
872           gp_XYZ gc(0,0,0); // gravity center of already loaded block sides
873           for (int i = 0; i < NB_BLOCK_SIDES; ++i )
874             if ( block._side[ i ] )
875               gc += block._side[ i ]._side->getGC();
876           gc /= nbLoadedSides;
877
878           gp_Vec gcDir( p1, gc );
879           gp_Vec2d gcDirProj( gcDir * pln.XDirection(), gcDir * pln.YDirection());
880           double gcAngle = gcDirProj.Angle( gp::DX2d() );
881           foundSide = gcAngle < 0 ? angleOfSide.rbegin()->second : angleOfSide.begin()->second;
882         }
883       }
884       _DUMP_("  selected "<< foundSide );
885     }
886
887     // Orient the found side correctly
888
889     // corners of found side corresponding to nodes n1 and n2
890     bool xMax1, yMax1, xMax2, yMax2;
891     if ( !getEdgeEnds( sharedSideEdge2, xMax1, yMax1, xMax2, yMax2 ))
892       return error(SMESH_Comment("Internal error at ")<<__FILE__<<":"<<__LINE__),
893         _OrientedBlockSide(0);
894
895     for ( int ori = 0; ori < _OrientedIndexer::MAX_ORI+1; ++ori )
896     {
897       _OrientedBlockSide orientedSide( foundSide, ori );
898       const SMDS_MeshNode* n12 = orientedSide.cornerNode( xMax1, yMax1);
899       const SMDS_MeshNode* n22 = orientedSide.cornerNode( xMax2, yMax2);
900       if ( n1 == n12 && n2 == n22 )
901         return orientedSide;
902     }
903     error(SMESH_Comment("Failed to orient a block side found by edge ")<<sharedSideEdge1
904           << " of side " << startBlockSide
905           << " of block " << _blocks.size());
906     return 0;
907   }
908
909   //================================================================================
910   /*!
911    * \brief: Fill rows (which are actually columns,if !alongN1N2) of nodes starting
912    * from the given quadrangle until another block corner encounters.
913    *  n1 and n2 are at bottom of quad, n1 is at block corner.
914    */
915   //================================================================================
916
917   bool _Skin::fillRowsUntilCorner(const SMDS_MeshElement*       quad,
918                                   const SMDS_MeshNode*          n1,
919                                   const SMDS_MeshNode*          n2,
920                                   vector<const SMDS_MeshNode*>& row1,
921                                   vector<const SMDS_MeshNode*>& row2,
922                                   const bool                    alongN1N2 )
923   {
924     const SMDS_MeshNode* corner1 = n1;
925
926     // Store nodes of quad in the rows and find new n1 and n2 to get
927     // the next face so that new n2 is on block edge
928     int i1 = quad->GetNodeIndex( n1 );
929     int i2 = quad->GetNodeIndex( n2 );
930     row1.clear(); row2.clear();
931     row1.push_back( n1 );
932     if ( alongN1N2 )
933     {
934       row1.push_back( n2 );
935       row2.push_back( oppositeNode( quad, i2 ));
936       row2.push_back( n1 = oppositeNode( quad, i1 ));
937     }
938     else
939     {
940       row2.push_back( n2 );
941       row1.push_back( n2 = oppositeNode( quad, i2 ));
942       row2.push_back( n1 = oppositeNode( quad, i1 ));
943     }
944
945     if ( isCornerNode( row1[1] ))
946       return true;
947
948     // Find the rest nodes
949     TIDSortedElemSet emptySet, avoidSet;
950     while ( !isCornerNode( n2 ) )
951     {
952       avoidSet.clear(); avoidSet.insert( quad );
953       quad = SMESH_MeshAlgos::FindFaceInSet( n1, n2, emptySet, avoidSet, &i1, &i2 );
954       if ( !isQuadrangle( quad ))
955         return BAD_MESH_ERR;
956
957       row1.push_back( n2 = oppositeNode( quad, i1 ));
958       row2.push_back( n1 = oppositeNode( quad, i2 ));
959     }
960     return n1 != corner1;
961   }
962
963   //================================================================================
964   /*!
965    * \brief Return a corner face by a corner node
966    */
967   //================================================================================
968
969   const SMDS_MeshElement* _BlockSide::getCornerFace(const SMDS_MeshNode* cornerNode) const
970   {
971     int x, y, isXMax, isYMax, found = 0;
972     for ( isXMax = 0; isXMax < 2; ++isXMax )
973     {
974       for ( isYMax = 0; isYMax < 2; ++isYMax )
975       {
976         x = isXMax ? _index._xSize-1 : 0;
977         y = isYMax ? _index._ySize-1 : 0;
978         found = ( getNode(x,y) == cornerNode );
979         if ( found ) break;
980       }
981       if ( found ) break;
982     }
983     if ( !found ) return 0;
984     int dx = isXMax ? -1 : +1;
985     int dy = isYMax ? -1 : +1;
986     const SMDS_MeshNode* n1 = getNode(x,y);
987     const SMDS_MeshNode* n2 = getNode(x+dx,y);
988     const SMDS_MeshNode* n3 = getNode(x,y+dy);
989     const SMDS_MeshNode* n4 = getNode(x+dx,y+dy);
990     return SMDS_Mesh::FindFace(n1, n2, n3, n4 );
991   }
992
993   //================================================================================
994   /*!
995    * \brief Checks own validity
996    */
997   //================================================================================
998
999   bool _Block::isValid() const
1000   {
1001     bool ok = ( nbSides() == 6 );
1002
1003     // check only corners depending on side selection
1004     EBoxSides adjacent[4] = { B_BOTTOM, B_RIGHT, B_TOP, B_LEFT };
1005     EQuadEdge edgeAdj [4] = { Q_TOP,    Q_RIGHT, Q_TOP, Q_RIGHT };
1006     EQuadEdge edgeBack[4] = { Q_BOTTOM, Q_RIGHT, Q_TOP, Q_LEFT };
1007
1008     for ( int i=0; ok && i < NB_QUAD_SIDES; ++i )
1009     { 
1010       SMESH_OrientedLink eBack = _side[ B_BACK      ].edge( edgeBack[i] );
1011       SMESH_OrientedLink eAdja = _side[ adjacent[i] ].edge( edgeAdj[i] );
1012       ok = ( eBack == eAdja );
1013     }
1014     return ok;
1015   }
1016
1017 } // namespace
1018
1019
1020
1021
1022
1023 HEXA_NS::Hexa* _block2Hexa( const _Block& block,
1024                             HEXA_NS::Document* doc,
1025                             std::map<HEXA_NS::Vertex*, SMDS_MeshNode*> vertexNode )
1026 {
1027     const SMDS_MeshNode* n000 = block.getSide(B_BOTTOM).cornerNode( 0, 0 );
1028     const SMDS_MeshNode* n100 = block.getSide(B_BOTTOM).cornerNode( 1, 0 );
1029     const SMDS_MeshNode* n010 = block.getSide(B_BOTTOM).cornerNode( 0, 1 );
1030     const SMDS_MeshNode* n110 = block.getSide(B_BOTTOM).cornerNode( 1, 1 );
1031     const SMDS_MeshNode* n001 = block.getSide(B_TOP).cornerNode( 0, 0 );
1032     const SMDS_MeshNode* n101 = block.getSide(B_TOP).cornerNode( 1, 0 );
1033     const SMDS_MeshNode* n011 = block.getSide(B_TOP).cornerNode( 0, 1 );
1034     const SMDS_MeshNode* n111 = block.getSide(B_TOP).cornerNode( 1, 1 );
1035
1036     list<const SMDS_MeshNode* > nodeFromBlock;
1037     nodeFromBlock.push_back(n000);
1038     nodeFromBlock.push_back(n100);
1039     nodeFromBlock.push_back(n010);
1040     nodeFromBlock.push_back(n110);
1041     nodeFromBlock.push_back(n001);
1042     nodeFromBlock.push_back(n101);
1043     nodeFromBlock.push_back(n011);
1044     nodeFromBlock.push_back(n111);
1045     nodeFromBlock.sort();
1046
1047
1048     HEXA_NS::Hexa* hexa = NULL;
1049     int nHexa = doc->countUsedHexa();
1050     for (int j=0; j <nHexa; ++j ){
1051       hexa = doc->getUsedHexa(j);
1052       list<const SMDS_MeshNode* > nodeFromHexa;
1053       int nVx = hexa->countVertex();
1054       for ( int i=0; i <nVx; ++i ){
1055         HEXA_NS::Vertex* v = hexa->getVertex(i);
1056         const SMDS_MeshNode* n = vertexNode[v];
1057         nodeFromHexa.push_back(n);
1058     }
1059       nodeFromHexa.sort();
1060
1061       if ( nodeFromBlock == nodeFromHexa ){
1062 //         std::cout << "OK block match hexa "<< hexa <<" id = "<<hexa->getId()<<std::endl;
1063         return hexa;
1064       } /*else {
1065         std::cout << "KO block does not match hexa "<< hexa <<" id = "<<hexa->getId()<<std::endl;
1066       }*/
1067     }
1068     return NULL;
1069 }
1070
1071
1072
1073
1074
1075
1076
1077
1078 //=======================================================================
1079 //function : SMESH_HexaFromSkin_3D
1080 //purpose  : 
1081 //=======================================================================
1082
1083 // SMESH_HexaFromSkin_3D::SMESH_HexaFromSkin_3D(int hypId, int studyId, SMESH_Gen* gen)
1084 //   :SMESH_3D_Algo(hypId, studyId, gen)
1085 // {
1086 //   MESSAGE("SMESH_HexaFromSkin_3D::SMESH_HexaFromSkin_3D");
1087 //   _name = "HexaFromSkin_3D";
1088 // }
1089
1090 SMESH_HexaFromSkin_3D::SMESH_HexaFromSkin_3D(int hypId, int studyId, SMESH_Gen* gen, HEXA_NS::Document* doc)
1091   :SMESH_3D_Algo(hypId, studyId, gen),
1092   _doc( doc )
1093 {
1094   if(MYDEBUG) MESSAGE("SMESH_HexaFromSkin_3D::SMESH_HexaFromSkin_3D");
1095   _name = "HexaFromSkin_3D";
1096 }
1097
1098
1099 SMESH_HexaFromSkin_3D::~SMESH_HexaFromSkin_3D()
1100 {
1101   if(MYDEBUG) MESSAGE("SMESH_HexaFromSkin_3D::~SMESH_HexaFromSkin_3D");
1102 }
1103
1104 //================================================================================
1105 /*!
1106  * \brief Main method, which generates hexaheda
1107  */
1108 //================================================================================
1109
1110 bool SMESH_HexaFromSkin_3D::Compute(SMESH_Mesh & aMesh, SMESH_MesherHelper* aHelper)
1111 {
1112   _Skin skin;
1113   int nbBlocks = skin.findBlocks(aMesh);
1114   if ( nbBlocks == 0 )
1115     return error( skin.error());
1116
1117   vector< vector< const SMDS_MeshNode* > > columns;
1118   int x, xSize, y, ySize, z, zSize;
1119   _Indexer colIndex;
1120
1121   for ( int i = 0; i < nbBlocks; ++i )
1122   {
1123     const _Block& block = skin.getBlock( i );
1124
1125     // ------------------------------------------
1126     // Fill columns of nodes with existing nodes
1127     // ------------------------------------------
1128
1129     xSize = block.getSide(B_BOTTOM).getHoriSize();
1130     ySize = block.getSide(B_BOTTOM).getVertSize();
1131     zSize = block.getSide(B_FRONT ).getVertSize();
1132     int X = xSize - 1, Y = ySize - 1, Z = zSize - 1;
1133     colIndex = _Indexer( xSize, ySize );
1134     columns.resize( colIndex.size() );
1135
1136     // fill node columns by front and back box sides
1137     for ( x = 0; x < xSize; ++x ) {
1138       vector< const SMDS_MeshNode* >& column0 = columns[ colIndex( x, 0 )];
1139       vector< const SMDS_MeshNode* >& column1 = columns[ colIndex( x, Y )];
1140       column0.resize( zSize );
1141       column1.resize( zSize );
1142       for ( z = 0; z < zSize; ++z ) {
1143         column0[ z ] = block.getSide(B_FRONT).node( x, z );
1144         column1[ z ] = block.getSide(B_BACK) .node( x, z );
1145       }
1146     }
1147     // fill node columns by left and right box sides
1148     for ( y = 1; y < ySize-1; ++y ) {
1149       vector< const SMDS_MeshNode* >& column0 = columns[ colIndex( 0, y )];
1150       vector< const SMDS_MeshNode* >& column1 = columns[ colIndex( X, y )];
1151       column0.resize( zSize );
1152       column1.resize( zSize );
1153       for ( z = 0; z < zSize; ++z ) {
1154         column0[ z ] = block.getSide(B_LEFT) .node( y, z );
1155         column1[ z ] = block.getSide(B_RIGHT).node( y, z );
1156       }
1157     }
1158     // get nodes from top and bottom box sides
1159     for ( x = 1; x < xSize-1; ++x ) {
1160       for ( y = 1; y < ySize-1; ++y ) {
1161         vector< const SMDS_MeshNode* >& column = columns[ colIndex( x, y )];
1162         column.resize( zSize );
1163         column.front() = block.getSide(B_BOTTOM).node( x, y );
1164         column.back()  = block.getSide(B_TOP)   .node( x, y );
1165       }
1166     }
1167
1168     // ----------------------------
1169     // Add internal nodes of a box
1170     // ----------------------------
1171     // projection points of internal nodes on box sub-shapes by which
1172     // coordinates of internal nodes are computed
1173     vector<gp_XYZ> pointOnShape( SMESH_Block::ID_Shell );
1174
1175     // projections on vertices are constant
1176     pointOnShape[ SMESH_Block::ID_V000 ] = block.getSide(B_BOTTOM).xyz( 0, 0 );
1177     pointOnShape[ SMESH_Block::ID_V100 ] = block.getSide(B_BOTTOM).xyz( X, 0 );
1178     pointOnShape[ SMESH_Block::ID_V010 ] = block.getSide(B_BOTTOM).xyz( 0, Y );
1179     pointOnShape[ SMESH_Block::ID_V110 ] = block.getSide(B_BOTTOM).xyz( X, Y );
1180     pointOnShape[ SMESH_Block::ID_V001 ] = block.getSide(B_TOP).xyz( 0, 0 );
1181     pointOnShape[ SMESH_Block::ID_V101 ] = block.getSide(B_TOP).xyz( X, 0 );
1182     pointOnShape[ SMESH_Block::ID_V011 ] = block.getSide(B_TOP).xyz( 0, Y );
1183     pointOnShape[ SMESH_Block::ID_V111 ] = block.getSide(B_TOP).xyz( X, Y );
1184
1185     for ( x = 1; x < xSize-1; ++x )
1186     {
1187       gp_XYZ params; // normalized parameters of internal node within a unit box
1188       params.SetCoord( 1, x / double(X) );
1189       for ( y = 1; y < ySize-1; ++y )
1190       {
1191         params.SetCoord( 2, y / double(Y) );
1192         // column to fill during z loop
1193         vector< const SMDS_MeshNode* >& column = columns[ colIndex( x, y )];
1194         // projections on horizontal edges
1195         pointOnShape[ SMESH_Block::ID_Ex00 ] = block.getSide(B_BOTTOM).xyz( x, 0 );
1196         pointOnShape[ SMESH_Block::ID_Ex10 ] = block.getSide(B_BOTTOM).xyz( x, Y );
1197         pointOnShape[ SMESH_Block::ID_E0y0 ] = block.getSide(B_BOTTOM).xyz( 0, y );
1198         pointOnShape[ SMESH_Block::ID_E1y0 ] = block.getSide(B_BOTTOM).xyz( X, y );
1199         pointOnShape[ SMESH_Block::ID_Ex01 ] = block.getSide(B_TOP).xyz( x, 0 );
1200         pointOnShape[ SMESH_Block::ID_Ex11 ] = block.getSide(B_TOP).xyz( x, Y );
1201         pointOnShape[ SMESH_Block::ID_E0y1 ] = block.getSide(B_TOP).xyz( 0, y );
1202         pointOnShape[ SMESH_Block::ID_E1y1 ] = block.getSide(B_TOP).xyz( X, y );
1203         // projections on horizontal sides
1204         pointOnShape[ SMESH_Block::ID_Fxy0 ] = block.getSide(B_BOTTOM).xyz( x, y );
1205         pointOnShape[ SMESH_Block::ID_Fxy1 ] = block.getSide(B_TOP)   .xyz( x, y );
1206         for ( z = 1; z < zSize-1; ++z ) // z loop
1207         {
1208           params.SetCoord( 3, z / double(Z) );
1209           // projections on vertical edges
1210           pointOnShape[ SMESH_Block::ID_E00z ] = block.getSide(B_FRONT).xyz( 0, z );    
1211           pointOnShape[ SMESH_Block::ID_E10z ] = block.getSide(B_FRONT).xyz( X, z );    
1212           pointOnShape[ SMESH_Block::ID_E01z ] = block.getSide(B_BACK).xyz( 0, z );    
1213           pointOnShape[ SMESH_Block::ID_E11z ] = block.getSide(B_BACK).xyz( X, z );
1214           // projections on vertical sides
1215           pointOnShape[ SMESH_Block::ID_Fx0z ] = block.getSide(B_FRONT).xyz( x, z );    
1216           pointOnShape[ SMESH_Block::ID_Fx1z ] = block.getSide(B_BACK) .xyz( x, z );    
1217           pointOnShape[ SMESH_Block::ID_F0yz ] = block.getSide(B_LEFT) .xyz( y, z );    
1218           pointOnShape[ SMESH_Block::ID_F1yz ] = block.getSide(B_RIGHT).xyz( y, z );
1219
1220           // compute internal node coordinates
1221           gp_XYZ coords;
1222           SMESH_Block::ShellPoint( params, pointOnShape, coords );
1223           column[ z ] = aHelper->AddNode( coords.X(), coords.Y(), coords.Z() );
1224
1225 #ifdef DEB_GRID
1226           // debug
1227           //cout << "----------------------------------------------------------------------"<<endl;
1228           //for ( int id = SMESH_Block::ID_V000; id < SMESH_Block::ID_Shell; ++id)
1229           //{
1230           //  gp_XYZ p = pointOnShape[ id ];
1231           //  SMESH_Block::DumpShapeID( id,cout)<<" ( "<<p.X()<<", "<<p.Y()<<", "<<p.Z()<<" )"<<endl;
1232           //}
1233           //cout << "Params: ( "<< params.X()<<", "<<params.Y()<<", "<<params.Z()<<" )"<<endl;
1234           //cout << "coords: ( "<< coords.X()<<", "<<coords.Y()<<", "<<coords.Z()<<" )"<<endl;
1235 #endif
1236         }
1237       }
1238     }
1239     // ----------------
1240     // Add hexahedrons
1241     // ----------------
1242
1243     // find out orientation by a least distorted hexahedron (issue 0020855);
1244     // the last is defined by evaluating sum of face normals of 8 corner hexahedrons
1245     double badness = numeric_limits<double>::max();
1246     bool isForw = true;
1247     for ( int xMax = 0; xMax < 2; ++xMax )
1248       for ( int yMax = 0; yMax < 2; ++yMax )
1249         for ( int zMax = 0; zMax < 2; ++zMax )
1250         {
1251           x = xMax ? xSize-1 : 1;
1252           y = yMax ? ySize-1 : 1;
1253           z = zMax ? zSize-1 : 1;
1254           vector< const SMDS_MeshNode* >& col00 = columns[ colIndex( x-1, y-1 )];
1255           vector< const SMDS_MeshNode* >& col10 = columns[ colIndex( x  , y-1 )];
1256           vector< const SMDS_MeshNode* >& col01 = columns[ colIndex( x-1, y   )];
1257           vector< const SMDS_MeshNode* >& col11 = columns[ colIndex( x  , y )];
1258           
1259           const SMDS_MeshNode* n000 = col00[z-1];
1260           const SMDS_MeshNode* n100 = col10[z-1];
1261           const SMDS_MeshNode* n010 = col01[z-1];
1262           const SMDS_MeshNode* n110 = col11[z-1];
1263           const SMDS_MeshNode* n001 = col00[z];
1264           const SMDS_MeshNode* n101 = col10[z];
1265           const SMDS_MeshNode* n011 = col01[z];
1266           const SMDS_MeshNode* n111 = col11[z];
1267           SMDS_VolumeOfNodes probeVolume (n000,n010,n110,n100,
1268                                           n001,n011,n111,n101);
1269           SMDS_VolumeTool volTool( &probeVolume );
1270           double Nx=0.,Ny=0.,Nz=0.;
1271           for ( int iFace = 0; iFace < volTool.NbFaces(); ++iFace )
1272           {
1273             double nx,ny,nz;
1274             volTool.GetFaceNormal( iFace, nx,ny,nz );
1275             Nx += nx;
1276             Ny += ny;
1277             Nz += nz;
1278           }
1279           double quality = Nx*Nx + Ny*Ny + Nz*Nz;
1280           if ( quality < badness )
1281           {
1282             badness = quality;
1283             isForw = volTool.IsForward();
1284           }
1285         }
1286
1287     // add elements
1288     for ( x = 0; x < xSize-1; ++x ) {
1289       for ( y = 0; y < ySize-1; ++y ) {
1290         vector< const SMDS_MeshNode* >& col00 = columns[ colIndex( x, y )];
1291         vector< const SMDS_MeshNode* >& col10 = columns[ colIndex( x+1, y )];
1292         vector< const SMDS_MeshNode* >& col01 = columns[ colIndex( x, y+1 )];
1293         vector< const SMDS_MeshNode* >& col11 = columns[ colIndex( x+1, y+1 )];
1294         // bottom face normal of a hexa mush point outside the volume
1295         if ( isForw )
1296           for ( z = 0; z < zSize-1; ++z )
1297             aHelper->AddVolume(col00[z],   col01[z],   col11[z],   col10[z],
1298                                col00[z+1], col01[z+1], col11[z+1], col10[z+1]);
1299         else
1300           for ( z = 0; z < zSize-1; ++z )
1301             aHelper->AddVolume(col00[z],   col10[z],   col11[z],   col01[z],
1302                                col00[z+1], col10[z+1], col11[z+1], col01[z+1]);
1303       }
1304     }
1305   } // loop on blocks
1306
1307   return true;
1308 }
1309
1310
1311 //================================================================================
1312 /*!
1313  * \brief Main method, which generates hexaheda
1314  */
1315 //================================================================================
1316 bool SMESH_HexaFromSkin_3D::Compute( SMESH_Mesh & aMesh, SMESH_MesherHelper* aHelper,
1317       std::map<HEXA_NS::Hexa*, SMESH_HexaBlocks::SMESHVolumes>& volumesOnHexa,
1318       std::map<HEXA_NS::Vertex*, SMDS_MeshNode*> vertexNode )
1319           {
1320   if(MYDEBUG) MESSAGE("SMESH_HexaFromSkin_3D::Compute BEGIN");
1321   _Skin skin;
1322   int nbBlocks = skin.findBlocks(aMesh);
1323   if ( nbBlocks == 0 )
1324     return error( skin.error());
1325
1326   vector< vector< const SMDS_MeshNode* > > columns;
1327   int x, xSize, y, ySize, z, zSize;
1328   _Indexer colIndex;
1329
1330   for ( int i = 0; i < nbBlocks; ++i )
1331   {
1332     const _Block& block = skin.getBlock( i );
1333
1334     // ------------------------------------------
1335     // Fill columns of nodes with existing nodes
1336     // ------------------------------------------
1337
1338     xSize = block.getSide(B_BOTTOM).getHoriSize();
1339     ySize = block.getSide(B_BOTTOM).getVertSize();
1340     zSize = block.getSide(B_FRONT ).getVertSize();
1341     int X = xSize - 1, Y = ySize - 1, Z = zSize - 1;
1342     colIndex = _Indexer( xSize, ySize );
1343     columns.resize( colIndex.size() );
1344
1345     // fill node columns by front and back box sides
1346     for ( x = 0; x < xSize; ++x ) {
1347       vector< const SMDS_MeshNode* >& column0 = columns[ colIndex( x, 0 )];
1348       vector< const SMDS_MeshNode* >& column1 = columns[ colIndex( x, Y )];
1349       column0.resize( zSize );
1350       column1.resize( zSize );
1351       for ( z = 0; z < zSize; ++z ) {
1352         column0[ z ] = block.getSide(B_FRONT).node( x, z );
1353         column1[ z ] = block.getSide(B_BACK) .node( x, z );
1354           }
1355     }
1356     // fill node columns by left and right box sides
1357     for ( y = 1; y < ySize-1; ++y ) {
1358       vector< const SMDS_MeshNode* >& column0 = columns[ colIndex( 0, y )];
1359       vector< const SMDS_MeshNode* >& column1 = columns[ colIndex( X, y )];
1360       column0.resize( zSize );
1361       column1.resize( zSize );
1362       for ( z = 0; z < zSize; ++z ) {
1363         column0[ z ] = block.getSide(B_LEFT) .node( y, z );
1364         column1[ z ] = block.getSide(B_RIGHT).node( y, z );
1365       }
1366     }
1367     // get nodes from top and bottom box sides
1368     for ( x = 1; x < xSize-1; ++x ) {
1369       for ( y = 1; y < ySize-1; ++y ) {
1370         vector< const SMDS_MeshNode* >& column = columns[ colIndex( x, y )];
1371         column.resize( zSize );
1372         column.front() = block.getSide(B_BOTTOM).node( x, y );
1373         column.back()  = block.getSide(B_TOP)   .node( x, y );
1374       }
1375     }
1376
1377     // ----------------------------
1378     // Add internal nodes of a box
1379     // ----------------------------
1380     // projection points of internal nodes on box subshapes by which
1381     // coordinates of internal nodes are computed
1382     vector<gp_XYZ> pointOnShape( SMESH_Block::ID_Shell );
1383
1384     // projections on vertices are constant
1385     pointOnShape[ SMESH_Block::ID_V000 ] = block.getSide(B_BOTTOM).xyz( 0, 0 );
1386     pointOnShape[ SMESH_Block::ID_V100 ] = block.getSide(B_BOTTOM).xyz( X, 0 );
1387     pointOnShape[ SMESH_Block::ID_V010 ] = block.getSide(B_BOTTOM).xyz( 0, Y );
1388     pointOnShape[ SMESH_Block::ID_V110 ] = block.getSide(B_BOTTOM).xyz( X, Y );
1389     pointOnShape[ SMESH_Block::ID_V001 ] = block.getSide(B_TOP).xyz( 0, 0 );
1390     pointOnShape[ SMESH_Block::ID_V101 ] = block.getSide(B_TOP).xyz( X, 0 );
1391     pointOnShape[ SMESH_Block::ID_V011 ] = block.getSide(B_TOP).xyz( 0, Y );
1392     pointOnShape[ SMESH_Block::ID_V111 ] = block.getSide(B_TOP).xyz( X, Y );
1393
1394     for ( x = 1; x < xSize-1; ++x )
1395           {
1396       gp_XYZ params; // normalized parameters of internal node within a unit box
1397       params.SetCoord( 1, x / double(X) );
1398       for ( y = 1; y < ySize-1; ++y )
1399       {
1400         params.SetCoord( 2, y / double(Y) );
1401         // column to fill during z loop
1402         vector< const SMDS_MeshNode* >& column = columns[ colIndex( x, y )];
1403         // projections on horizontal edges
1404         pointOnShape[ SMESH_Block::ID_Ex00 ] = block.getSide(B_BOTTOM).xyz( x, 0 );
1405         pointOnShape[ SMESH_Block::ID_Ex10 ] = block.getSide(B_BOTTOM).xyz( x, Y );
1406         pointOnShape[ SMESH_Block::ID_E0y0 ] = block.getSide(B_BOTTOM).xyz( 0, y );
1407         pointOnShape[ SMESH_Block::ID_E1y0 ] = block.getSide(B_BOTTOM).xyz( X, y );
1408         pointOnShape[ SMESH_Block::ID_Ex01 ] = block.getSide(B_TOP).xyz( x, 0 );
1409         pointOnShape[ SMESH_Block::ID_Ex11 ] = block.getSide(B_TOP).xyz( x, Y );
1410         pointOnShape[ SMESH_Block::ID_E0y1 ] = block.getSide(B_TOP).xyz( 0, y );
1411         pointOnShape[ SMESH_Block::ID_E1y1 ] = block.getSide(B_TOP).xyz( X, y );
1412         // projections on horizontal sides
1413         pointOnShape[ SMESH_Block::ID_Fxy0 ] = block.getSide(B_BOTTOM).xyz( x, y );
1414         pointOnShape[ SMESH_Block::ID_Fxy1 ] = block.getSide(B_TOP)   .xyz( x, y );
1415         for ( z = 1; z < zSize-1; ++z ) // z loop
1416         {
1417           params.SetCoord( 3, z / double(Z) );
1418           // projections on vertical edges
1419           pointOnShape[ SMESH_Block::ID_E00z ] = block.getSide(B_FRONT).xyz( 0, z );    
1420           pointOnShape[ SMESH_Block::ID_E10z ] = block.getSide(B_FRONT).xyz( X, z );    
1421           pointOnShape[ SMESH_Block::ID_E01z ] = block.getSide(B_BACK).xyz( 0, z );    
1422           pointOnShape[ SMESH_Block::ID_E11z ] = block.getSide(B_BACK).xyz( X, z );
1423           // projections on vertical sides
1424           pointOnShape[ SMESH_Block::ID_Fx0z ] = block.getSide(B_FRONT).xyz( x, z );    
1425           pointOnShape[ SMESH_Block::ID_Fx1z ] = block.getSide(B_BACK) .xyz( x, z );    
1426           pointOnShape[ SMESH_Block::ID_F0yz ] = block.getSide(B_LEFT) .xyz( y, z );    
1427           pointOnShape[ SMESH_Block::ID_F1yz ] = block.getSide(B_RIGHT).xyz( y, z );
1428
1429           // compute internal node coordinates
1430           gp_XYZ coords;
1431           SMESH_Block::ShellPoint( params, pointOnShape, coords );
1432           column[ z ] = aHelper->AddNode( coords.X(), coords.Y(), coords.Z() );
1433
1434 #ifdef DEB_GRID
1435           // debug
1436           //cout << "----------------------------------------------------------------------"<<endl;
1437           //for ( int id = SMESH_Block::ID_V000; id < SMESH_Block::ID_Shell; ++id)
1438           //{
1439           //  gp_XYZ p = pointOnShape[ id ];
1440           //  SMESH_Block::DumpShapeID( id,cout)<<" ( "<<p.X()<<", "<<p.Y()<<", "<<p.Z()<<" )"<<endl;
1441           //}
1442           //cout << "Params: ( "<< params.X()<<", "<<params.Y()<<", "<<params.Z()<<" )"<<endl;
1443           //cout << "coords: ( "<< coords.X()<<", "<<coords.Y()<<", "<<coords.Z()<<" )"<<endl;
1444 #endif
1445           }
1446         }
1447     }
1448     // ----------------
1449     // Add hexahedrons
1450     // ----------------
1451
1452     // find out orientation
1453     const SMDS_MeshNode* n000 = block.getSide(B_BOTTOM).cornerNode( 0, 0 );
1454     const SMDS_MeshNode* n100 = block.getSide(B_BOTTOM).cornerNode( 1, 0 );
1455     const SMDS_MeshNode* n010 = block.getSide(B_BOTTOM).cornerNode( 0, 1 );
1456     const SMDS_MeshNode* n110 = block.getSide(B_BOTTOM).cornerNode( 1, 1 );
1457     const SMDS_MeshNode* n001 = block.getSide(B_TOP).cornerNode( 0, 0 );
1458     const SMDS_MeshNode* n101 = block.getSide(B_TOP).cornerNode( 1, 0 );
1459     const SMDS_MeshNode* n011 = block.getSide(B_TOP).cornerNode( 0, 1 );
1460     const SMDS_MeshNode* n111 = block.getSide(B_TOP).cornerNode( 1, 1 );
1461     SMDS_VolumeOfNodes probeVolume (n000,n010,n110,n100,
1462                                     n001,n011,n111,n101);
1463     bool isForw = SMDS_VolumeTool( &probeVolume ).IsForward();
1464
1465     // add elements
1466     SMESH_HexaBlocks::SMESHVolumes volumesOnBlock; //Groups creation
1467
1468     for ( x = 0; x < xSize-1; ++x ) {
1469       for ( y = 0; y < ySize-1; ++y ) {
1470         vector< const SMDS_MeshNode* >& col00 = columns[ colIndex( x, y )];
1471         vector< const SMDS_MeshNode* >& col10 = columns[ colIndex( x+1, y )];
1472         vector< const SMDS_MeshNode* >& col01 = columns[ colIndex( x, y+1 )];
1473         vector< const SMDS_MeshNode* >& col11 = columns[ colIndex( x+1, y+1 )];
1474         // bottom face normal of a hexa mush point outside the volume
1475         if ( isForw )
1476           for ( z = 0; z < zSize-1; ++z ){
1477             SMDS_MeshVolume* newVolume = 
1478             aHelper->AddVolume(col00[z],   col01[z],   col11[z],   col10[z],
1479                                col00[z+1], col01[z+1], col11[z+1], col10[z+1]);
1480             volumesOnBlock.push_back( newVolume );
1481           }
1482         else
1483           for ( z = 0; z < zSize-1; ++z ){
1484             SMDS_MeshVolume* newVolume = 
1485             aHelper->AddVolume(col00[z],   col10[z],   col11[z],   col01[z],
1486                                col00[z+1], col10[z+1], col11[z+1], col01[z+1]);
1487             volumesOnBlock.push_back( newVolume );
1488       }
1489     }
1490     }
1491 //     std::cout << "block i = " << i << std::endl;
1492     HEXA_NS::Hexa* currentHexa = _block2Hexa( block, _doc, vertexNode );
1493     if ( currentHexa != NULL ){
1494 //       std::cout<<"===== found ->"<<currentHexa<<" for block "<<i<<std::endl;
1495       if ( volumesOnHexa.count(currentHexa)==0 ) {
1496 //         std::cout<<"added!! "<<std::endl;
1497         volumesOnHexa[currentHexa] = volumesOnBlock;
1498       } else {
1499 //         std::cout<<"already !! "<<std::endl;
1500       }
1501     } else {
1502 //       std::cout<<"===== not found ->"<<currentHexa<<" for block "<<i<<std::endl;
1503     }
1504
1505   } // loop on blocks
1506
1507   if(MYDEBUG) MESSAGE("SMESH_HexaFromSkin_3D::Compute END");
1508   return true;
1509 }
1510
1511 //================================================================================
1512 /*!
1513  * \brief Evaluate nb of hexa
1514  */
1515 //================================================================================
1516
1517 bool SMESH_HexaFromSkin_3D::Evaluate(SMESH_Mesh &         aMesh,
1518                                           const TopoDS_Shape & aShape,
1519                                           MapShapeNbElems&     aResMap)
1520 {
1521   _Skin skin;
1522   int nbBlocks = skin.findBlocks(aMesh);
1523   if ( nbBlocks == 0 )
1524     return error( skin.error());
1525
1526   bool secondOrder = aMesh.NbFaces( ORDER_QUADRATIC );
1527
1528   int entity = secondOrder ? SMDSEntity_Quad_Hexa : SMDSEntity_Hexa;
1529   vector<int>& nbByType = aResMap[ aMesh.GetSubMesh( aShape )];
1530   if ( entity >= nbByType.size() )
1531     nbByType.resize( SMDSEntity_Last, 0 );
1532
1533   for ( int i = 0; i < nbBlocks; ++i )
1534   {
1535     const _Block& block = skin.getBlock( i );
1536
1537     int nbX = block.getSide(B_BOTTOM).getHoriSize();
1538     int nbY = block.getSide(B_BOTTOM).getVertSize();
1539     int nbZ = block.getSide(B_FRONT ).getVertSize();
1540
1541     int nbHexa  = (nbX-1) * (nbY-1) * (nbZ-1);
1542     int nbNodes = (nbX-2) * (nbY-2) * (nbZ-2);
1543     if ( secondOrder )
1544       nbNodes +=
1545         (nbX-2) * (nbY-2) * (nbZ-1) +
1546         (nbX-2) * (nbY-1) * (nbZ-2) +
1547         (nbX-1) * (nbY-2) * (nbZ-2);
1548
1549
1550     nbByType[ entity ] += nbHexa;
1551     nbByType[ SMDSEntity_Node ] += nbNodes;
1552   }
1553
1554   return true;
1555 }
1556
1557 //================================================================================
1558 /*!
1559  * \brief Abstract method must be defined but does nothing
1560  */
1561 //================================================================================
1562
1563 bool SMESH_HexaFromSkin_3D::CheckHypothesis(SMESH_Mesh&, const TopoDS_Shape&,
1564                                                  Hypothesis_Status& aStatus)
1565 {
1566   aStatus = SMESH_Hypothesis::HYP_OK;
1567   return true;
1568 }
1569
1570 //================================================================================
1571 /*!
1572  * \brief Abstract method must be defined but just reports an error as this
1573  *  algo is not intended to work with shapes
1574  */
1575 //================================================================================
1576
1577 bool SMESH_HexaFromSkin_3D::Compute(SMESH_Mesh&, const TopoDS_Shape&)
1578 {
1579   return error("Algorithm can't work with geometrical shapes");
1580 }
1581
1582