Salome HOME
Merge remote branch 'origin/V7_4_BR'
[modules/smesh.git] / src / StdMeshers / StdMeshers_ViscousLayers.cxx
1 // Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
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      : StdMeshers_ViscousLayers.cxx
21 // Created   : Wed Dec  1 15:15:34 2010
22 // Author    : Edward AGAPOV (eap)
23
24 #include "StdMeshers_ViscousLayers.hxx"
25
26 #include "SMDS_EdgePosition.hxx"
27 #include "SMDS_FaceOfNodes.hxx"
28 #include "SMDS_FacePosition.hxx"
29 #include "SMDS_MeshNode.hxx"
30 #include "SMDS_SetIterator.hxx"
31 #include "SMESHDS_Group.hxx"
32 #include "SMESHDS_Hypothesis.hxx"
33 #include "SMESH_Algo.hxx"
34 #include "SMESH_ComputeError.hxx"
35 #include "SMESH_ControlsDef.hxx"
36 #include "SMESH_Gen.hxx"
37 #include "SMESH_Group.hxx"
38 #include "SMESH_HypoFilter.hxx"
39 #include "SMESH_Mesh.hxx"
40 #include "SMESH_MeshAlgos.hxx"
41 #include "SMESH_MesherHelper.hxx"
42 #include "SMESH_ProxyMesh.hxx"
43 #include "SMESH_subMesh.hxx"
44 #include "SMESH_subMeshEventListener.hxx"
45 #include "StdMeshers_FaceSide.hxx"
46
47 #include <BRepAdaptor_Curve2d.hxx>
48 #include <BRepAdaptor_Surface.hxx>
49 #include <BRepLProp_SLProps.hxx>
50 #include <BRep_Tool.hxx>
51 #include <Bnd_B2d.hxx>
52 #include <Bnd_B3d.hxx>
53 #include <ElCLib.hxx>
54 #include <GCPnts_AbscissaPoint.hxx>
55 #include <Geom2d_Circle.hxx>
56 #include <Geom2d_Line.hxx>
57 #include <Geom2d_TrimmedCurve.hxx>
58 #include <GeomAdaptor_Curve.hxx>
59 #include <GeomLib.hxx>
60 #include <Geom_Circle.hxx>
61 #include <Geom_Curve.hxx>
62 #include <Geom_Line.hxx>
63 #include <Geom_TrimmedCurve.hxx>
64 #include <Precision.hxx>
65 #include <Standard_ErrorHandler.hxx>
66 #include <Standard_Failure.hxx>
67 #include <TColStd_Array1OfReal.hxx>
68 #include <TopExp.hxx>
69 #include <TopExp_Explorer.hxx>
70 #include <TopTools_IndexedMapOfShape.hxx>
71 #include <TopTools_ListOfShape.hxx>
72 #include <TopTools_MapOfShape.hxx>
73 #include <TopoDS.hxx>
74 #include <TopoDS_Edge.hxx>
75 #include <TopoDS_Face.hxx>
76 #include <TopoDS_Vertex.hxx>
77 #include <gp_Ax1.hxx>
78 #include <gp_Vec.hxx>
79 #include <gp_XY.hxx>
80
81 #include <list>
82 #include <string>
83 #include <cmath>
84 #include <limits>
85
86 #define __myDEBUG
87
88 using namespace std;
89
90 //================================================================================
91 namespace VISCOUS_3D
92 {
93   typedef int TGeomID;
94
95   enum UIndex { U_TGT = 1, U_SRC, LEN_TGT };
96
97   const double theMinSmoothCosin = 0.1;
98   const double theSmoothThickToElemSizeRatio = 0.3;
99
100   bool needSmoothing( double cosin, double tgtThick, double elemSize )
101   {
102     return cosin * tgtThick > theSmoothThickToElemSizeRatio * elemSize;
103   }
104
105   /*!
106    * \brief SMESH_ProxyMesh computed by _ViscousBuilder for a SOLID.
107    * It is stored in a SMESH_subMesh of the SOLID as SMESH_subMeshEventListenerData
108    */
109   struct _MeshOfSolid : public SMESH_ProxyMesh,
110                         public SMESH_subMeshEventListenerData
111   {
112     bool _n2nMapComputed;
113
114     _MeshOfSolid( SMESH_Mesh* mesh)
115       :SMESH_subMeshEventListenerData( /*isDeletable=*/true),_n2nMapComputed(false)
116     {
117       SMESH_ProxyMesh::setMesh( *mesh );
118     }
119
120     // returns submesh for a geom face
121     SMESH_ProxyMesh::SubMesh* getFaceSubM(const TopoDS_Face& F, bool create=false)
122     {
123       TGeomID i = SMESH_ProxyMesh::shapeIndex(F);
124       return create ? SMESH_ProxyMesh::getProxySubMesh(i) : findProxySubMesh(i);
125     }
126     void setNode2Node(const SMDS_MeshNode*                 srcNode,
127                       const SMDS_MeshNode*                 proxyNode,
128                       const SMESH_ProxyMesh::SubMesh* subMesh)
129     {
130       SMESH_ProxyMesh::setNode2Node( srcNode,proxyNode,subMesh);
131     }
132   };
133   //--------------------------------------------------------------------------------
134   /*!
135    * \brief Listener of events of 3D sub-meshes computed with viscous layers.
136    * It is used to clear an inferior dim sub-meshes modified by viscous layers
137    */
138   class _ShrinkShapeListener : SMESH_subMeshEventListener
139   {
140     _ShrinkShapeListener()
141       : SMESH_subMeshEventListener(/*isDeletable=*/false,
142                                    "StdMeshers_ViscousLayers::_ShrinkShapeListener") {}
143   public:
144     static SMESH_subMeshEventListener* Get() { static _ShrinkShapeListener l; return &l; }
145     virtual void ProcessEvent(const int                       event,
146                               const int                       eventType,
147                               SMESH_subMesh*                  solidSM,
148                               SMESH_subMeshEventListenerData* data,
149                               const SMESH_Hypothesis*         hyp)
150     {
151       if ( SMESH_subMesh::COMPUTE_EVENT == eventType && solidSM->IsEmpty() && data )
152       {
153         SMESH_subMeshEventListener::ProcessEvent(event,eventType,solidSM,data,hyp);
154       }
155     }
156   };
157   //--------------------------------------------------------------------------------
158   /*!
159    * \brief Listener of events of 3D sub-meshes computed with viscous layers.
160    * It is used to store data computed by _ViscousBuilder for a sub-mesh and to
161    * delete the data as soon as it has been used
162    */
163   class _ViscousListener : SMESH_subMeshEventListener
164   {
165     _ViscousListener():
166       SMESH_subMeshEventListener(/*isDeletable=*/false,
167                                  "StdMeshers_ViscousLayers::_ViscousListener") {}
168     static SMESH_subMeshEventListener* Get() { static _ViscousListener l; return &l; }
169   public:
170     virtual void ProcessEvent(const int                       event,
171                               const int                       eventType,
172                               SMESH_subMesh*                  subMesh,
173                               SMESH_subMeshEventListenerData* data,
174                               const SMESH_Hypothesis*         hyp)
175     {
176       if ( SMESH_subMesh::COMPUTE_EVENT == eventType )
177       {
178         // delete SMESH_ProxyMesh containing temporary faces
179         subMesh->DeleteEventListener( this );
180       }
181     }
182     // Finds or creates proxy mesh of the solid
183     static _MeshOfSolid* GetSolidMesh(SMESH_Mesh*         mesh,
184                                       const TopoDS_Shape& solid,
185                                       bool                toCreate=false)
186     {
187       if ( !mesh ) return 0;
188       SMESH_subMesh* sm = mesh->GetSubMesh(solid);
189       _MeshOfSolid* data = (_MeshOfSolid*) sm->GetEventListenerData( Get() );
190       if ( !data && toCreate )
191       {
192         data = new _MeshOfSolid(mesh);
193         data->mySubMeshes.push_back( sm ); // to find SOLID by _MeshOfSolid
194         sm->SetEventListener( Get(), data, sm );
195       }
196       return data;
197     }
198     // Removes proxy mesh of the solid
199     static void RemoveSolidMesh(SMESH_Mesh* mesh, const TopoDS_Shape& solid)
200     {
201       mesh->GetSubMesh(solid)->DeleteEventListener( _ViscousListener::Get() );
202     }
203   };
204   
205   //================================================================================
206   /*!
207    * \brief sets a sub-mesh event listener to clear sub-meshes of sub-shapes of
208    * the main shape when sub-mesh of the main shape is cleared,
209    * for example to clear sub-meshes of FACEs when sub-mesh of a SOLID
210    * is cleared
211    */
212   //================================================================================
213
214   void ToClearSubWithMain( SMESH_subMesh* sub, const TopoDS_Shape& main)
215   {
216     SMESH_subMesh* mainSM = sub->GetFather()->GetSubMesh( main );
217     SMESH_subMeshEventListenerData* data =
218       mainSM->GetEventListenerData( _ShrinkShapeListener::Get());
219     if ( data )
220     {
221       if ( find( data->mySubMeshes.begin(), data->mySubMeshes.end(), sub ) ==
222            data->mySubMeshes.end())
223         data->mySubMeshes.push_back( sub );
224     }
225     else
226     {
227       data = SMESH_subMeshEventListenerData::MakeData( /*dependent=*/sub );
228       sub->SetEventListener( _ShrinkShapeListener::Get(), data, /*whereToListenTo=*/mainSM );
229     }
230   }
231   //--------------------------------------------------------------------------------
232   /*!
233    * \brief Simplex (triangle or tetrahedron) based on 1 (tria) or 2 (tet) nodes of
234    * _LayerEdge and 2 nodes of the mesh surface beening smoothed.
235    * The class is used to check validity of face or volumes around a smoothed node;
236    * it stores only 2 nodes as the other nodes are stored by _LayerEdge.
237    */
238   struct _Simplex
239   {
240     const SMDS_MeshNode *_nPrev, *_nNext; // nodes on a smoothed mesh surface
241     const SMDS_MeshNode *_nOpp; // in 2D case, a node opposite to a smoothed node in QUAD
242     _Simplex(const SMDS_MeshNode* nPrev=0,
243              const SMDS_MeshNode* nNext=0,
244              const SMDS_MeshNode* nOpp=0)
245       : _nPrev(nPrev), _nNext(nNext), _nOpp(nOpp) {}
246     bool IsForward(const SMDS_MeshNode* nSrc, const gp_XYZ* pntTgt) const
247     {
248       const double M[3][3] =
249         {{ _nNext->X() - nSrc->X(), _nNext->Y() - nSrc->Y(), _nNext->Z() - nSrc->Z() },
250          { pntTgt->X() - nSrc->X(), pntTgt->Y() - nSrc->Y(), pntTgt->Z() - nSrc->Z() },
251          { _nPrev->X() - nSrc->X(), _nPrev->Y() - nSrc->Y(), _nPrev->Z() - nSrc->Z() }};
252       double determinant = ( + M[0][0]*M[1][1]*M[2][2]
253                              + M[0][1]*M[1][2]*M[2][0]
254                              + M[0][2]*M[1][0]*M[2][1]
255                              - M[0][0]*M[1][2]*M[2][1]
256                              - M[0][1]*M[1][0]*M[2][2]
257                              - M[0][2]*M[1][1]*M[2][0]);
258       return determinant > 1e-100;
259     }
260     bool IsForward(const gp_XY&         tgtUV,
261                    const SMDS_MeshNode* smoothedNode,
262                    const TopoDS_Face&   face,
263                    SMESH_MesherHelper&  helper,
264                    const double         refSign) const
265     {
266       gp_XY prevUV = helper.GetNodeUV( face, _nPrev, smoothedNode );
267       gp_XY nextUV = helper.GetNodeUV( face, _nNext, smoothedNode );
268       gp_Vec2d v1( tgtUV, prevUV ), v2( tgtUV, nextUV );
269       double d = v1 ^ v2;
270       return d*refSign > 1e-100;
271     }
272     bool IsNeighbour(const _Simplex& other) const
273     {
274       return _nPrev == other._nNext || _nNext == other._nPrev;
275     }
276   };
277   //--------------------------------------------------------------------------------
278   /*!
279    * Structure used to take into account surface curvature while smoothing
280    */
281   struct _Curvature
282   {
283     double _r; // radius
284     double _k; // factor to correct node smoothed position
285     double _h2lenRatio; // avgNormProj / (2*avgDist)
286   public:
287     static _Curvature* New( double avgNormProj, double avgDist )
288     {
289       _Curvature* c = 0;
290       if ( fabs( avgNormProj / avgDist ) > 1./200 )
291       {
292         c = new _Curvature;
293         c->_r = avgDist * avgDist / avgNormProj;
294         c->_k = avgDist * avgDist / c->_r / c->_r;
295         //c->_k = avgNormProj / c->_r;
296         c->_k *= ( c->_r < 0 ? 1/1.1 : 1.1 ); // not to be too restrictive
297         c->_h2lenRatio = avgNormProj / ( avgDist + avgDist );
298       }
299       return c;
300     }
301     double lenDelta(double len) const { return _k * ( _r + len ); }
302     double lenDeltaByDist(double dist) const { return dist * _h2lenRatio; }
303   };
304   struct _2NearEdges;
305   //--------------------------------------------------------------------------------
306   /*!
307    * \brief Edge normal to surface, connecting a node on solid surface (_nodes[0])
308    * and a node of the most internal layer (_nodes.back())
309    */
310   struct _LayerEdge
311   {
312     vector< const SMDS_MeshNode*> _nodes;
313
314     gp_XYZ              _normal; // to solid surface
315     vector<gp_XYZ>      _pos; // points computed during inflation
316     double              _len; // length achived with the last inflation step
317     double              _cosin; // of angle (_normal ^ surface)
318     double              _lenFactor; // to compute _len taking _cosin into account
319
320     // face or edge w/o layer along or near which _LayerEdge is inflated
321     TopoDS_Shape        _sWOL;
322     // simplices connected to the source node (_nodes[0]);
323     // used for smoothing and quality check of _LayerEdge's based on the FACE
324     vector<_Simplex>    _simplices;
325     // data for smoothing of _LayerEdge's based on the EDGE
326     _2NearEdges*        _2neibors;
327
328     _Curvature*         _curvature;
329     // TODO:: detele _Curvature, _plnNorm
330
331     void SetNewLength( double len, SMESH_MesherHelper& helper );
332     bool SetNewLength2d( Handle(Geom_Surface)& surface,
333                          const TopoDS_Face&    F,
334                          SMESH_MesherHelper&   helper );
335     void SetDataByNeighbors( const SMDS_MeshNode* n1,
336                              const SMDS_MeshNode* n2,
337                              SMESH_MesherHelper&  helper);
338     void InvalidateStep( int curStep, bool restoreLength=false );
339     bool Smooth(int& badNb);
340     bool SmoothOnEdge(Handle(Geom_Surface)& surface,
341                       const TopoDS_Face&    F,
342                       SMESH_MesherHelper&   helper);
343     bool FindIntersection( SMESH_ElementSearcher&   searcher,
344                            double &                 distance,
345                            const double&            epsilon,
346                            const SMDS_MeshElement** face = 0);
347     bool SegTriaInter( const gp_Ax1&        lastSegment,
348                        const SMDS_MeshNode* n0,
349                        const SMDS_MeshNode* n1,
350                        const SMDS_MeshNode* n2,
351                        double&              dist,
352                        const double&        epsilon) const;
353     gp_Ax1 LastSegment(double& segLen) const;
354     gp_XY  LastUV( const TopoDS_Face& F ) const;
355     bool   IsOnEdge() const { return _2neibors; }
356     gp_XYZ Copy( _LayerEdge& other, SMESH_MesherHelper& helper );
357     void   SetCosin( double cosin );
358     int    NbSteps() const { return _pos.size() - 1; } // nb inlation steps
359   };
360   struct _LayerEdgeCmp
361   {
362     bool operator () (const _LayerEdge* e1, const _LayerEdge* e2) const
363     {
364       const bool cmpNodes = ( e1 && e2 && e1->_nodes.size() && e2->_nodes.size() );
365       return cmpNodes ? ( e1->_nodes[0]->GetID() < e2->_nodes[0]->GetID()) : ( e1 < e2 );
366     }
367   };
368   struct _LayerEdge;
369   //--------------------------------------------------------------------------------
370   /*!
371    * Structure used to smooth a _LayerEdge based on an EDGE.
372    */
373   struct _2NearEdges
374   {
375     double               _wgt  [2]; // weights of _nodes
376     _LayerEdge*          _edges[2];
377
378      // normal to plane passing through _LayerEdge._normal and tangent of EDGE
379     gp_XYZ*              _plnNorm;
380
381     _2NearEdges() { _edges[0]=_edges[1]=0; _plnNorm = 0; }
382     const SMDS_MeshNode* tgtNode(bool is2nd) {
383       return _edges[is2nd] ? _edges[is2nd]->_nodes.back() : 0;
384     }
385     const SMDS_MeshNode* srcNode(bool is2nd) {
386       return _edges[is2nd] ? _edges[is2nd]->_nodes[0] : 0;
387     }
388     void reverse() {
389       std::swap( _wgt  [0], _wgt  [1] );
390       std::swap( _edges[0], _edges[1] );
391     }
392   };
393   //--------------------------------------------------------------------------------
394   /*!
395    * \brief Convex FACE whose radius of curvature is less than the thickness of 
396    *        layers. It is used to detect distortion of prisms based on a convex
397    *        FACE and to update normals to enable further increasing the thickness
398    */
399   struct _ConvexFace
400   {
401     TopoDS_Face                     _face;
402
403     // edges whose _simplices are used to detect prism destorsion
404     vector< _LayerEdge* >           _simplexTestEdges;
405
406     // map a sub-shape to it's index in _SolidData::_endEdgeOnShape vector
407     map< TGeomID, int >             _subIdToEdgeEnd;
408
409     bool                            _normalsFixed;
410
411     bool GetCenterOfCurvature( _LayerEdge*         ledge,
412                                BRepLProp_SLProps&  surfProp,
413                                SMESH_MesherHelper& helper,
414                                gp_Pnt &            center ) const;
415     bool CheckPrisms() const;
416   };
417
418   //--------------------------------------------------------------------------------
419   /*!
420    * \brief Layers parameters got by averaging several hypotheses
421    */
422   struct AverageHyp
423   {
424     AverageHyp( const StdMeshers_ViscousLayers* hyp = 0 )
425       :_nbLayers(0), _nbHyps(0), _thickness(0), _stretchFactor(0)
426     {
427       Add( hyp );
428     }
429     void Add( const StdMeshers_ViscousLayers* hyp )
430     {
431       if ( hyp )
432       {
433         _nbHyps++;
434         _nbLayers       = hyp->GetNumberLayers();
435         //_thickness     += hyp->GetTotalThickness();
436         _thickness     = Max( _thickness, hyp->GetTotalThickness() );
437         _stretchFactor += hyp->GetStretchFactor();
438       }
439     }
440     double GetTotalThickness() const { return _thickness; /*_nbHyps ? _thickness / _nbHyps : 0;*/ }
441     double GetStretchFactor()  const { return _nbHyps ? _stretchFactor / _nbHyps : 0; }
442     int    GetNumberLayers()   const { return _nbLayers; }
443   private:
444     int     _nbLayers, _nbHyps;
445     double  _thickness, _stretchFactor;
446   };
447
448   //--------------------------------------------------------------------------------
449
450   typedef map< const SMDS_MeshNode*, _LayerEdge*, TIDCompare > TNode2Edge;
451   
452   //--------------------------------------------------------------------------------
453   /*!
454    * \brief Data of a SOLID
455    */
456   struct _SolidData
457   {
458     typedef const StdMeshers_ViscousLayers* THyp;
459     TopoDS_Shape                    _solid;
460     TGeomID                         _index; // SOLID id
461     _MeshOfSolid*                   _proxyMesh;
462     list< THyp >                    _hyps;
463     list< TopoDS_Shape >            _hypShapes;
464     map< TGeomID, THyp >            _face2hyp; // filled if _hyps.size() > 1
465     set< TGeomID >                  _reversedFaceIds;
466     set< TGeomID >                  _ignoreFaceIds; // WOL FACEs and FACEs of other SOLIDs
467
468     double                          _stepSize, _stepSizeCoeff, _geomSize;
469     const SMDS_MeshNode*            _stepSizeNodes[2];
470
471     TNode2Edge                      _n2eMap; // nodes and _LayerEdge's based on them
472
473     // map to find _n2eMap of another _SolidData by a shrink shape shared by two _SolidData's
474     map< TGeomID, TNode2Edge* >     _s2neMap;
475     // edges of _n2eMap. We keep same data in two containers because
476     // iteration over the map is 5 times longer than over the vector
477     vector< _LayerEdge* >           _edges;
478
479     // key:   an id of shape (EDGE or VERTEX) shared by a FACE with
480     //        layers and a FACE w/o layers
481     // value: the shape (FACE or EDGE) to shrink mesh on.
482     //       _LayerEdge's basing on nodes on key shape are inflated along the value shape
483     map< TGeomID, TopoDS_Shape >     _shrinkShape2Shape;
484
485     // Convex FACEs whose radius of curvature is less than the thickness of layers
486     map< TGeomID, _ConvexFace >      _convexFaces;
487
488     // shapes (EDGEs and VERTEXes) srink from which is forbiden due to collisions with
489     // the adjacent SOLID
490     set< TGeomID >                   _noShrinkShapes;
491
492     // <EDGE to smooth on> to <it's curve> -- for analytic smooth
493     map< TGeomID,Handle(Geom_Curve)> _edge2curve;
494
495     // end indices in _edges of _LayerEdge on each shape, first go shapes to smooth
496     vector< int >                    _endEdgeOnShape;
497     int                              _nbShapesToSmooth;
498
499     // data of averaged StdMeshers_ViscousLayers parameters for each shape with _LayerEdge's
500     vector< AverageHyp >             _hypOnShape;
501     double                           _maxThickness; // of all _hyps
502     double                           _minThickness; // of all _hyps
503
504     double                           _epsilon; // precision for SegTriaInter()
505
506     _SolidData(const TopoDS_Shape& s=TopoDS_Shape(),
507                _MeshOfSolid*       m=0)
508       :_solid(s), _proxyMesh(m) {}
509     ~_SolidData();
510
511     Handle(Geom_Curve) CurveForSmooth( const TopoDS_Edge&    E,
512                                        const int             iFrom,
513                                        const int             iTo,
514                                        Handle(Geom_Surface)& surface,
515                                        const TopoDS_Face&    F,
516                                        SMESH_MesherHelper&   helper);
517
518     void SortOnEdge( const TopoDS_Edge&  E,
519                      const int           iFrom,
520                      const int           iTo,
521                      SMESH_MesherHelper& helper);
522
523     _ConvexFace* GetConvexFace( const TGeomID faceID )
524     {
525       map< TGeomID, _ConvexFace >::iterator id2face = _convexFaces.find( faceID );
526       return id2face == _convexFaces.end() ? 0 : & id2face->second;
527     }
528     void GetEdgesOnShape( size_t end, int &  iBeg, int &  iEnd )
529     {
530       iBeg = end > 0 ? _endEdgeOnShape[ end-1 ] : 0;
531       iEnd = _endEdgeOnShape[ end ];
532     }
533
534     bool GetShapeEdges(const TGeomID shapeID, size_t& iEdgeEnd, int* iBeg=0, int* iEnd=0 ) const;
535
536     void AddShapesToSmooth( const set< TGeomID >& shapeIDs );
537   };
538   //--------------------------------------------------------------------------------
539   /*!
540    * \brief Container of centers of curvature at nodes on an EDGE bounding _ConvexFace
541    */
542   struct _CentralCurveOnEdge
543   {
544     bool                  _isDegenerated;
545     vector< gp_Pnt >      _curvaCenters;
546     vector< _LayerEdge* > _ledges;
547     vector< gp_XYZ >      _normals; // new normal for each of _ledges
548     vector< double >      _segLength2;
549
550     TopoDS_Edge           _edge;
551     TopoDS_Face           _adjFace;
552     bool                  _adjFaceToSmooth;
553
554     void Append( const gp_Pnt& center, _LayerEdge* ledge )
555     {
556       if ( _curvaCenters.size() > 0 )
557         _segLength2.push_back( center.SquareDistance( _curvaCenters.back() ));
558       _curvaCenters.push_back( center );
559       _ledges.push_back( ledge );
560       _normals.push_back( ledge->_normal );
561     }
562     bool FindNewNormal( const gp_Pnt& center, gp_XYZ& newNormal );
563     void SetShapes( const TopoDS_Edge&  edge,
564                     const _ConvexFace&  convFace,
565                     const _SolidData&   data,
566                     SMESH_MesherHelper& helper);
567   };
568   //--------------------------------------------------------------------------------
569   /*!
570    * \brief Data of node on a shrinked FACE
571    */
572   struct _SmoothNode
573   {
574     const SMDS_MeshNode*         _node;
575     vector<_Simplex>             _simplices; // for quality check
576
577     enum SmoothType { LAPLACIAN, CENTROIDAL, ANGULAR, TFI };
578
579     bool Smooth(int&                  badNb,
580                 Handle(Geom_Surface)& surface,
581                 SMESH_MesherHelper&   helper,
582                 const double          refSign,
583                 SmoothType            how,
584                 bool                  set3D);
585
586     gp_XY computeAngularPos(vector<gp_XY>& uv,
587                             const gp_XY&   uvToFix,
588                             const double   refSign );
589   };
590   //--------------------------------------------------------------------------------
591   /*!
592    * \brief Builder of viscous layers
593    */
594   class _ViscousBuilder
595   {
596   public:
597     _ViscousBuilder();
598     // does it's job
599     SMESH_ComputeErrorPtr Compute(SMESH_Mesh&         mesh,
600                                   const TopoDS_Shape& shape);
601     // check validity of hypotheses
602     SMESH_ComputeErrorPtr CheckHypotheses( SMESH_Mesh&         mesh,
603                                            const TopoDS_Shape& shape );
604
605     // restore event listeners used to clear an inferior dim sub-mesh modified by viscous layers
606     void RestoreListeners();
607
608     // computes SMESH_ProxyMesh::SubMesh::_n2n;
609     bool MakeN2NMap( _MeshOfSolid* pm );
610
611   private:
612
613     bool findSolidsWithLayers();
614     bool findFacesWithLayers(const bool onlyWith=false);
615     void getIgnoreFaces(const TopoDS_Shape&             solid,
616                         const StdMeshers_ViscousLayers* hyp,
617                         const TopoDS_Shape&             hypShape,
618                         set<TGeomID>&                   ignoreFaces);
619     bool makeLayer(_SolidData& data);
620     bool setEdgeData(_LayerEdge& edge, const set<TGeomID>& subIds,
621                      SMESH_MesherHelper& helper, _SolidData& data);
622     gp_XYZ getFaceNormal(const SMDS_MeshNode* n,
623                          const TopoDS_Face&   face,
624                          SMESH_MesherHelper&  helper,
625                          bool&                isOK,
626                          bool                 shiftInside=false);
627     gp_XYZ getWeigthedNormal( const SMDS_MeshNode*         n,
628                               std::pair< TGeomID, gp_XYZ > fId2Normal[],
629                               const int                    nbFaces );
630     bool findNeiborsOnEdge(const _LayerEdge*     edge,
631                            const SMDS_MeshNode*& n1,
632                            const SMDS_MeshNode*& n2,
633                            _SolidData&           data);
634     void getSimplices( const SMDS_MeshNode* node, vector<_Simplex>& simplices,
635                        const set<TGeomID>& ingnoreShapes,
636                        const _SolidData*   dataToCheckOri = 0,
637                        const bool          toSort = false);
638     void findSimplexTestEdges( _SolidData&                    data,
639                                vector< vector<_LayerEdge*> >& edgesByGeom);
640     void computeGeomSize( _SolidData& data );
641     bool sortEdges( _SolidData&                    data,
642                     vector< vector<_LayerEdge*> >& edgesByGeom);
643     void limitStepSizeByCurvature( _SolidData&  data );
644     void limitStepSize( _SolidData&             data,
645                         const SMDS_MeshElement* face,
646                         const _LayerEdge*       maxCosinEdge );
647     void limitStepSize( _SolidData& data, const double minSize);
648     bool inflate(_SolidData& data);
649     bool smoothAndCheck(_SolidData& data, const int nbSteps, double & distToIntersection);
650     bool smoothAnalyticEdge( _SolidData&           data,
651                              const int             iFrom,
652                              const int             iTo,
653                              Handle(Geom_Surface)& surface,
654                              const TopoDS_Face&    F,
655                              SMESH_MesherHelper&   helper);
656     bool updateNormals( _SolidData& data, SMESH_MesherHelper& helper, int stepNb );
657     bool updateNormalsOfConvexFaces( _SolidData&         data,
658                                      SMESH_MesherHelper& helper,
659                                      int                 stepNb );
660     bool refine(_SolidData& data);
661     bool shrink();
662     bool prepareEdgeToShrink( _LayerEdge& edge, const TopoDS_Face& F,
663                               SMESH_MesherHelper& helper,
664                               const SMESHDS_SubMesh* faceSubMesh );
665     void restoreNoShrink( _LayerEdge& edge ) const;
666     void fixBadFaces(const TopoDS_Face&          F,
667                      SMESH_MesherHelper&         helper,
668                      const bool                  is2D,
669                      const int                   step,
670                      set<const SMDS_MeshNode*> * involvedNodes=NULL);
671     bool addBoundaryElements();
672
673     bool error( const string& text, int solidID=-1 );
674     SMESHDS_Mesh* getMeshDS() const { return _mesh->GetMeshDS(); }
675
676     // debug
677     void makeGroupOfLE();
678
679     SMESH_Mesh*           _mesh;
680     SMESH_ComputeErrorPtr _error;
681
682     vector< _SolidData >  _sdVec;
683     int                   _tmpFaceID;
684   };
685   //--------------------------------------------------------------------------------
686   /*!
687    * \brief Shrinker of nodes on the EDGE
688    */
689   class _Shrinker1D
690   {
691     vector<double>                _initU;
692     vector<double>                _normPar;
693     vector<const SMDS_MeshNode*>  _nodes;
694     const _LayerEdge*             _edges[2];
695     bool                          _done;
696   public:
697     void AddEdge( const _LayerEdge* e, SMESH_MesherHelper& helper );
698     void Compute(bool set3D, SMESH_MesherHelper& helper);
699     void RestoreParams();
700     void SwapSrcTgtNodes(SMESHDS_Mesh* mesh);
701   };
702   //--------------------------------------------------------------------------------
703   /*!
704    * \brief Class of temporary mesh face.
705    * We can't use SMDS_FaceOfNodes since it's impossible to set it's ID which is
706    * needed because SMESH_ElementSearcher internaly uses set of elements sorted by ID
707    */
708   struct _TmpMeshFace : public SMDS_MeshElement
709   {
710     vector<const SMDS_MeshNode* > _nn;
711     _TmpMeshFace( const vector<const SMDS_MeshNode*>& nodes, int id, int faceID=-1):
712       SMDS_MeshElement(id), _nn(nodes) { setShapeId(faceID); }
713     virtual const SMDS_MeshNode* GetNode(const int ind) const { return _nn[ind]; }
714     virtual SMDSAbs_ElementType  GetType() const              { return SMDSAbs_Face; }
715     virtual vtkIdType GetVtkType() const                      { return -1; }
716     virtual SMDSAbs_EntityType   GetEntityType() const        { return SMDSEntity_Last; }
717     virtual SMDSAbs_GeometryType GetGeomType() const
718     { return _nn.size() == 3 ? SMDSGeom_TRIANGLE : SMDSGeom_QUADRANGLE; }
719     virtual SMDS_ElemIteratorPtr elementsIterator(SMDSAbs_ElementType) const
720     { return SMDS_ElemIteratorPtr( new SMDS_NodeVectorElemIterator( _nn.begin(), _nn.end()));}
721   };
722   //--------------------------------------------------------------------------------
723   /*!
724    * \brief Class of temporary mesh face storing _LayerEdge it's based on
725    */
726   struct _TmpMeshFaceOnEdge : public _TmpMeshFace
727   {
728     _LayerEdge *_le1, *_le2;
729     _TmpMeshFaceOnEdge( _LayerEdge* le1, _LayerEdge* le2, int ID ):
730       _TmpMeshFace( vector<const SMDS_MeshNode*>(4), ID ), _le1(le1), _le2(le2)
731     {
732       _nn[0]=_le1->_nodes[0];
733       _nn[1]=_le1->_nodes.back();
734       _nn[2]=_le2->_nodes.back();
735       _nn[3]=_le2->_nodes[0];
736     }
737   };
738   //--------------------------------------------------------------------------------
739   /*!
740    * \brief Retriever of node coordinates either directly of from a surface by node UV.
741    * \warning Location of a surface is ignored
742    */
743   struct _NodeCoordHelper
744   {
745     SMESH_MesherHelper&        _helper;
746     const TopoDS_Face&         _face;
747     Handle(Geom_Surface)       _surface;
748     gp_XYZ (_NodeCoordHelper::* _fun)(const SMDS_MeshNode* n) const;
749
750     _NodeCoordHelper(const TopoDS_Face& F, SMESH_MesherHelper& helper, bool is2D)
751       : _helper( helper ), _face( F )
752     {
753       if ( is2D )
754       {
755         TopLoc_Location loc;
756         _surface = BRep_Tool::Surface( _face, loc );
757       }
758       if ( _surface.IsNull() )
759         _fun = & _NodeCoordHelper::direct;
760       else
761         _fun = & _NodeCoordHelper::byUV;
762     }
763     gp_XYZ operator()(const SMDS_MeshNode* n) const { return (this->*_fun)( n ); }
764
765   private:
766     gp_XYZ direct(const SMDS_MeshNode* n) const
767     {
768       return SMESH_TNodeXYZ( n );
769     }
770     gp_XYZ byUV  (const SMDS_MeshNode* n) const
771     {
772       gp_XY uv = _helper.GetNodeUV( _face, n );
773       return _surface->Value( uv.X(), uv.Y() ).XYZ();
774     }
775   };
776
777 } // namespace VISCOUS_3D
778
779
780
781 //================================================================================
782 // StdMeshers_ViscousLayers hypothesis
783 //
784 StdMeshers_ViscousLayers::StdMeshers_ViscousLayers(int hypId, int studyId, SMESH_Gen* gen)
785   :SMESH_Hypothesis(hypId, studyId, gen),
786    _isToIgnoreShapes(1), _nbLayers(1), _thickness(1), _stretchFactor(1)
787 {
788   _name = StdMeshers_ViscousLayers::GetHypType();
789   _param_algo_dim = -3; // auxiliary hyp used by 3D algos
790 } // --------------------------------------------------------------------------------
791 void StdMeshers_ViscousLayers::SetBndShapes(const std::vector<int>& faceIds, bool toIgnore)
792 {
793   if ( faceIds != _shapeIds )
794     _shapeIds = faceIds, NotifySubMeshesHypothesisModification();
795   if ( _isToIgnoreShapes != toIgnore )
796     _isToIgnoreShapes = toIgnore, NotifySubMeshesHypothesisModification();
797 } // --------------------------------------------------------------------------------
798 void StdMeshers_ViscousLayers::SetTotalThickness(double thickness)
799 {
800   if ( thickness != _thickness )
801     _thickness = thickness, NotifySubMeshesHypothesisModification();
802 } // --------------------------------------------------------------------------------
803 void StdMeshers_ViscousLayers::SetNumberLayers(int nb)
804 {
805   if ( _nbLayers != nb )
806     _nbLayers = nb, NotifySubMeshesHypothesisModification();
807 } // --------------------------------------------------------------------------------
808 void StdMeshers_ViscousLayers::SetStretchFactor(double factor)
809 {
810   if ( _stretchFactor != factor )
811     _stretchFactor = factor, NotifySubMeshesHypothesisModification();
812 } // --------------------------------------------------------------------------------
813 SMESH_ProxyMesh::Ptr
814 StdMeshers_ViscousLayers::Compute(SMESH_Mesh&         theMesh,
815                                   const TopoDS_Shape& theShape,
816                                   const bool          toMakeN2NMap) const
817 {
818   using namespace VISCOUS_3D;
819   _ViscousBuilder bulder;
820   SMESH_ComputeErrorPtr err = bulder.Compute( theMesh, theShape );
821   if ( err && !err->IsOK() )
822     return SMESH_ProxyMesh::Ptr();
823
824   vector<SMESH_ProxyMesh::Ptr> components;
825   TopExp_Explorer exp( theShape, TopAbs_SOLID );
826   for ( ; exp.More(); exp.Next() )
827   {
828     if ( _MeshOfSolid* pm =
829          _ViscousListener::GetSolidMesh( &theMesh, exp.Current(), /*toCreate=*/false))
830     {
831       if ( toMakeN2NMap && !pm->_n2nMapComputed )
832         if ( !bulder.MakeN2NMap( pm ))
833           return SMESH_ProxyMesh::Ptr();
834       components.push_back( SMESH_ProxyMesh::Ptr( pm ));
835       pm->myIsDeletable = false; // it will de deleted by boost::shared_ptr
836     }
837     _ViscousListener::RemoveSolidMesh ( &theMesh, exp.Current() );
838   }
839   switch ( components.size() )
840   {
841   case 0: break;
842
843   case 1: return components[0];
844
845   default: return SMESH_ProxyMesh::Ptr( new SMESH_ProxyMesh( components ));
846   }
847   return SMESH_ProxyMesh::Ptr();
848 } // --------------------------------------------------------------------------------
849 std::ostream & StdMeshers_ViscousLayers::SaveTo(std::ostream & save)
850 {
851   save << " " << _nbLayers
852        << " " << _thickness
853        << " " << _stretchFactor
854        << " " << _shapeIds.size();
855   for ( size_t i = 0; i < _shapeIds.size(); ++i )
856     save << " " << _shapeIds[i];
857   save << " " << !_isToIgnoreShapes; // negate to keep the behavior in old studies.
858   return save;
859 } // --------------------------------------------------------------------------------
860 std::istream & StdMeshers_ViscousLayers::LoadFrom(std::istream & load)
861 {
862   int nbFaces, faceID, shapeToTreat;
863   load >> _nbLayers >> _thickness >> _stretchFactor >> nbFaces;
864   while ( _shapeIds.size() < nbFaces && load >> faceID )
865     _shapeIds.push_back( faceID );
866   if ( load >> shapeToTreat )
867     _isToIgnoreShapes = !shapeToTreat;
868   else
869     _isToIgnoreShapes = true; // old behavior
870   return load;
871 } // --------------------------------------------------------------------------------
872 bool StdMeshers_ViscousLayers::SetParametersByMesh(const SMESH_Mesh*   theMesh,
873                                                    const TopoDS_Shape& theShape)
874 {
875   // TODO
876   return false;
877 } // --------------------------------------------------------------------------------
878 SMESH_ComputeErrorPtr
879 StdMeshers_ViscousLayers::CheckHypothesis(SMESH_Mesh&                          theMesh,
880                                           const TopoDS_Shape&                  theShape,
881                                           SMESH_Hypothesis::Hypothesis_Status& theStatus)
882 {
883   VISCOUS_3D::_ViscousBuilder bulder;
884   SMESH_ComputeErrorPtr err = bulder.CheckHypotheses( theMesh, theShape );
885   if ( err && !err->IsOK() )
886     theStatus = SMESH_Hypothesis::HYP_INCOMPAT_HYPS;
887   else
888     theStatus = SMESH_Hypothesis::HYP_OK;
889
890   return err;
891 }
892 // --------------------------------------------------------------------------------
893 bool StdMeshers_ViscousLayers::IsShapeWithLayers(int shapeIndex) const
894 {
895   bool isIn =
896     ( std::find( _shapeIds.begin(), _shapeIds.end(), shapeIndex ) != _shapeIds.end() );
897   return IsToIgnoreShapes() ? !isIn : isIn;
898 }
899 // END StdMeshers_ViscousLayers hypothesis
900 //================================================================================
901
902 namespace
903 {
904   gp_XYZ getEdgeDir( const TopoDS_Edge& E, const TopoDS_Vertex& fromV )
905   {
906     gp_Vec dir;
907     double f,l;
908     Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l );
909     gp_Pnt p = BRep_Tool::Pnt( fromV );
910     double distF = p.SquareDistance( c->Value( f ));
911     double distL = p.SquareDistance( c->Value( l ));
912     c->D1(( distF < distL ? f : l), p, dir );
913     if ( distL < distF ) dir.Reverse();
914     return dir.XYZ();
915   }
916   //--------------------------------------------------------------------------------
917   gp_XYZ getEdgeDir( const TopoDS_Edge& E, const SMDS_MeshNode* atNode,
918                      SMESH_MesherHelper& helper)
919   {
920     gp_Vec dir;
921     double f,l; gp_Pnt p;
922     Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l );
923     if ( c.IsNull() ) return gp_XYZ( 1e100, 1e100, 1e100 );
924     double u = helper.GetNodeU( E, atNode );
925     c->D1( u, p, dir );
926     return dir.XYZ();
927   }
928   //--------------------------------------------------------------------------------
929   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Vertex& fromV,
930                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper, bool& ok,
931                      double* cosin=0);
932   //--------------------------------------------------------------------------------
933   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Edge& fromE,
934                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper, bool& ok)
935   {
936     double f,l;
937     Handle(Geom_Curve) c = BRep_Tool::Curve( fromE, f, l );
938     if ( c.IsNull() )
939     {
940       TopoDS_Vertex v = helper.IthVertex( 0, fromE );
941       return getFaceDir( F, v, node, helper, ok );
942     }
943     gp_XY uv = helper.GetNodeUV( F, node, 0, &ok );
944     Handle(Geom_Surface) surface = BRep_Tool::Surface( F );
945     gp_Pnt p; gp_Vec du, dv, norm;
946     surface->D1( uv.X(),uv.Y(), p, du,dv );
947     norm = du ^ dv;
948
949     double u = helper.GetNodeU( fromE, node, 0, &ok );
950     c->D1( u, p, du );
951     TopAbs_Orientation o = helper.GetSubShapeOri( F.Oriented(TopAbs_FORWARD), fromE);
952     if ( o == TopAbs_REVERSED )
953       du.Reverse();
954
955     gp_Vec dir = norm ^ du;
956
957     if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX &&
958          helper.IsClosedEdge( fromE ))
959     {
960       if ( fabs(u-f) < fabs(u-l)) c->D1( l, p, dv );
961       else                        c->D1( f, p, dv );
962       if ( o == TopAbs_REVERSED )
963         dv.Reverse();
964       gp_Vec dir2 = norm ^ dv;
965       dir = dir.Normalized() + dir2.Normalized();
966     }
967     return dir.XYZ();
968   }
969   //--------------------------------------------------------------------------------
970   gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Vertex& fromV,
971                      const SMDS_MeshNode* node, SMESH_MesherHelper& helper,
972                      bool& ok, double* cosin)
973   {
974     TopoDS_Face faceFrw = F;
975     faceFrw.Orientation( TopAbs_FORWARD );
976     double f,l; TopLoc_Location loc;
977     TopoDS_Edge edges[2]; // sharing a vertex
978     int nbEdges = 0;
979     {
980       TopoDS_Vertex VV[2];
981       TopExp_Explorer exp( faceFrw, TopAbs_EDGE );
982       for ( ; exp.More() && nbEdges < 2; exp.Next() )
983       {
984         const TopoDS_Edge& e = TopoDS::Edge( exp.Current() );
985         if ( SMESH_Algo::isDegenerated( e )) continue;
986         TopExp::Vertices( e, VV[0], VV[1], /*CumOri=*/true );
987         if ( VV[1].IsSame( fromV )) {
988           nbEdges += edges[ 0 ].IsNull();
989           edges[ 0 ] = e;
990         }
991         else if ( VV[0].IsSame( fromV )) {
992           nbEdges += edges[ 1 ].IsNull();
993           edges[ 1 ] = e;
994         }
995       }
996     }
997     gp_XYZ dir(0,0,0), edgeDir[2];
998     if ( nbEdges == 2 )
999     {
1000       // get dirs of edges going fromV
1001       ok = true;
1002       for ( size_t i = 0; i < nbEdges && ok; ++i )
1003       {
1004         edgeDir[i] = getEdgeDir( edges[i], fromV );
1005         double size2 = edgeDir[i].SquareModulus();
1006         if (( ok = size2 > numeric_limits<double>::min() ))
1007           edgeDir[i] /= sqrt( size2 );
1008       }
1009       if ( !ok ) return dir;
1010
1011       // get angle between the 2 edges
1012       gp_Vec faceNormal;
1013       double angle = helper.GetAngle( edges[0], edges[1], faceFrw, fromV, &faceNormal );
1014       if ( Abs( angle ) < 5 * M_PI/180 )
1015       {
1016         dir = ( faceNormal.XYZ() ^ edgeDir[0].Reversed()) + ( faceNormal.XYZ() ^ edgeDir[1] );
1017       }
1018       else
1019       {
1020         dir = edgeDir[0] + edgeDir[1];
1021         if ( angle < 0 )
1022           dir.Reverse();
1023       }
1024       if ( cosin ) {
1025         double angle = gp_Vec( edgeDir[0] ).Angle( dir );
1026         *cosin = Cos( angle );
1027       }
1028     }
1029     else if ( nbEdges == 1 )
1030     {
1031       dir = getFaceDir( faceFrw, edges[ edges[0].IsNull() ], node, helper, ok );
1032       if ( cosin ) *cosin = 1.;
1033     }
1034     else
1035     {
1036       ok = false;
1037     }
1038
1039     return dir;
1040   }
1041   //================================================================================
1042   /*!
1043    * \brief Returns true if a FACE is bound by a concave EDGE
1044    */
1045   //================================================================================
1046
1047   bool isConcave( const TopoDS_Face& F, SMESH_MesherHelper& helper )
1048   {
1049     // if ( helper.Count( F, TopAbs_WIRE, /*useMap=*/false) > 1 )
1050     //   return true;
1051     gp_Vec2d drv1, drv2;
1052     gp_Pnt2d p;
1053     TopExp_Explorer eExp( F.Oriented( TopAbs_FORWARD ), TopAbs_EDGE );
1054     for ( ; eExp.More(); eExp.Next() )
1055     {
1056       const TopoDS_Edge& E = TopoDS::Edge( eExp.Current() );
1057       if ( SMESH_Algo::isDegenerated( E )) continue;
1058       // check if 2D curve is concave
1059       BRepAdaptor_Curve2d curve( E, F );
1060       const int nbIntervals = curve.NbIntervals( GeomAbs_C2 );
1061       TColStd_Array1OfReal intervals(1, nbIntervals + 1 );
1062       curve.Intervals( intervals, GeomAbs_C2 );
1063       bool isConvex = true;
1064       for ( int i = 1; i <= nbIntervals && isConvex; ++i )
1065       {
1066         double u1 = intervals( i );
1067         double u2 = intervals( i+1 );
1068         curve.D2( 0.5*( u1+u2 ), p, drv1, drv2 );
1069         double cross = drv2 ^ drv1;
1070         if ( E.Orientation() == TopAbs_REVERSED )
1071           cross = -cross;
1072         isConvex = ( cross > 0.1 ); //-1e-9 );
1073       }
1074       if ( !isConvex )
1075       {
1076         //cout << "Concave FACE " << helper.GetMeshDS()->ShapeToIndex( F ) << endl;
1077         return true;
1078       }
1079     }
1080     // check angles at VERTEXes
1081     TError error;
1082     TSideVector wires = StdMeshers_FaceSide::GetFaceWires( F, *helper.GetMesh(), 0, error );
1083     for ( size_t iW = 0; iW < wires.size(); ++iW )
1084     {
1085       const int nbEdges = wires[iW]->NbEdges();
1086       if ( nbEdges < 2 && SMESH_Algo::isDegenerated( wires[iW]->Edge(0)))
1087         continue;
1088       for ( int iE1 = 0; iE1 < nbEdges; ++iE1 )
1089       {
1090         if ( SMESH_Algo::isDegenerated( wires[iW]->Edge( iE1 ))) continue;
1091         int iE2 = ( iE1 + 1 ) % nbEdges;
1092         while ( SMESH_Algo::isDegenerated( wires[iW]->Edge( iE2 )))
1093           iE2 = ( iE2 + 1 ) % nbEdges;
1094         double angle = helper.GetAngle( wires[iW]->Edge( iE1 ),
1095                                         wires[iW]->Edge( iE2 ), F,
1096                                         wires[iW]->FirstVertex( iE2 ));
1097         if ( angle < -5. * M_PI / 180. )
1098           return true;
1099       }
1100     }
1101     return false;
1102   }
1103   //================================================================================
1104   /*!
1105    * \brief Computes mimimal distance of face in-FACE nodes from an EDGE
1106    *  \param [in] face - the mesh face to treat
1107    *  \param [in] nodeOnEdge - a node on the EDGE
1108    *  \param [out] faceSize - the computed distance
1109    *  \return bool - true if faceSize computed
1110    */
1111   //================================================================================
1112
1113   bool getDistFromEdge( const SMDS_MeshElement* face,
1114                         const SMDS_MeshNode*    nodeOnEdge,
1115                         double &                faceSize )
1116   {
1117     faceSize = Precision::Infinite();
1118     bool done = false;
1119
1120     int nbN  = face->NbCornerNodes();
1121     int iOnE = face->GetNodeIndex( nodeOnEdge );
1122     int iNext[2] = { SMESH_MesherHelper::WrapIndex( iOnE+1, nbN ),
1123                      SMESH_MesherHelper::WrapIndex( iOnE-1, nbN ) };
1124     const SMDS_MeshNode* nNext[2] = { face->GetNode( iNext[0] ),
1125                                       face->GetNode( iNext[1] ) };
1126     gp_XYZ segVec, segEnd = SMESH_TNodeXYZ( nodeOnEdge ); // segment on EDGE
1127     double segLen = -1.;
1128     // look for two neighbor not in-FACE nodes of face
1129     for ( int i = 0; i < 2; ++i )
1130     {
1131       if ( nNext[i]->GetPosition()->GetDim() != 2 &&
1132            nNext[i]->GetID() < nodeOnEdge->GetID() )
1133       {
1134         // look for an in-FACE node
1135         for ( int iN = 0; iN < nbN; ++iN )
1136         {
1137           if ( iN == iOnE || iN == iNext[i] )
1138             continue;
1139           SMESH_TNodeXYZ pInFace = face->GetNode( iN );
1140           gp_XYZ v = pInFace - segEnd;
1141           if ( segLen < 0 )
1142           {
1143             segVec = SMESH_TNodeXYZ( nNext[i] ) - segEnd;
1144             segLen = segVec.Modulus();
1145           }
1146           double distToSeg = v.Crossed( segVec ).Modulus() / segLen;
1147           faceSize = Min( faceSize, distToSeg );
1148           done = true;
1149         }
1150         segLen = -1;
1151       }
1152     }
1153     return done;
1154   }
1155
1156   //--------------------------------------------------------------------------------
1157   // DEBUG. Dump intermediate node positions into a python script
1158   // HOWTO use: run python commands written in a console to see
1159   //  construction steps of viscous layers
1160 #ifdef __myDEBUG
1161   ofstream* py;
1162   int       theNbFunc;
1163   struct PyDump {
1164     PyDump() {
1165       const char* fname = "/tmp/viscous.py";
1166       cout << "execfile('"<<fname<<"')"<<endl;
1167       py = new ofstream(fname);
1168       *py << "import SMESH" << endl
1169           << "from salome.smesh import smeshBuilder" << endl
1170           << "smesh  = smeshBuilder.New(salome.myStudy)" << endl
1171           << "meshSO = smesh.GetCurrentStudy().FindObjectID('0:1:2:3')" << endl
1172           << "mesh   = smesh.Mesh( meshSO.GetObject() )"<<endl;
1173       theNbFunc = 0;
1174     }
1175     void Finish() {
1176       if (py) {
1177         *py << "mesh.GroupOnFilter(SMESH.VOLUME,'Viscous Prisms',"
1178           "smesh.GetFilter(SMESH.VOLUME,SMESH.FT_ElemGeomType,'=',SMESH.Geom_PENTA))"<<endl;
1179         *py << "mesh.GroupOnFilter(SMESH.VOLUME,'Neg Volumes',"
1180           "smesh.GetFilter(SMESH.VOLUME,SMESH.FT_Volume3D,'<',0))"<<endl;
1181       }
1182       delete py; py=0;
1183     }
1184     ~PyDump() { Finish(); cout << "NB FUNCTIONS: " << theNbFunc << endl; }
1185   };
1186 #define dumpFunction(f) { _dumpFunction(f, __LINE__);}
1187 #define dumpMove(n)     { _dumpMove(n, __LINE__);}
1188 #define dumpCmd(txt)    { _dumpCmd(txt, __LINE__);}
1189   void _dumpFunction(const string& fun, int ln)
1190   { if (py) *py<< "def "<<fun<<"(): # "<< ln <<endl; cout<<fun<<"()"<<endl; ++theNbFunc; }
1191   void _dumpMove(const SMDS_MeshNode* n, int ln)
1192   { if (py) *py<< "  mesh.MoveNode( "<<n->GetID()<< ", "<< n->X()
1193                << ", "<<n->Y()<<", "<< n->Z()<< ")\t\t # "<< ln <<endl; }
1194   void _dumpCmd(const string& txt, int ln)
1195   { if (py) *py<< "  "<<txt<<" # "<< ln <<endl; }
1196   void dumpFunctionEnd()
1197   { if (py) *py<< "  return"<< endl; }
1198   void dumpChangeNodes( const SMDS_MeshElement* f )
1199   { if (py) { *py<< "  mesh.ChangeElemNodes( " << f->GetID()<<", [";
1200       for ( int i=1; i < f->NbNodes(); ++i ) *py << f->GetNode(i-1)->GetID()<<", ";
1201       *py << f->GetNode( f->NbNodes()-1 )->GetID() << " ])"<< endl; }}
1202 #define debugMsg( txt ) { cout << txt << " (line: " << __LINE__ << ")" << endl; }
1203 #else
1204   struct PyDump { void Finish() {} };
1205 #define dumpFunction(f) f
1206 #define dumpMove(n)
1207 #define dumpCmd(txt)
1208 #define dumpFunctionEnd()
1209 #define dumpChangeNodes(f)
1210 #define debugMsg( txt ) {}
1211 #endif
1212 }
1213
1214 using namespace VISCOUS_3D;
1215
1216 //================================================================================
1217 /*!
1218  * \brief Constructor of _ViscousBuilder
1219  */
1220 //================================================================================
1221
1222 _ViscousBuilder::_ViscousBuilder()
1223 {
1224   _error = SMESH_ComputeError::New(COMPERR_OK);
1225   _tmpFaceID = 0;
1226 }
1227
1228 //================================================================================
1229 /*!
1230  * \brief Stores error description and returns false
1231  */
1232 //================================================================================
1233
1234 bool _ViscousBuilder::error(const string& text, int solidId )
1235 {
1236   const string prefix = string("Viscous layers builder: ");
1237   _error->myName    = COMPERR_ALGO_FAILED;
1238   _error->myComment = prefix + text;
1239   if ( _mesh )
1240   {
1241     SMESH_subMesh* sm = _mesh->GetSubMeshContaining( solidId );
1242     if ( !sm && !_sdVec.empty() )
1243       sm = _mesh->GetSubMeshContaining( solidId = _sdVec[0]._index );
1244     if ( sm && sm->GetSubShape().ShapeType() == TopAbs_SOLID )
1245     {
1246       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1247       if ( smError && smError->myAlgo )
1248         _error->myAlgo = smError->myAlgo;
1249       smError = _error;
1250       sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1251     }
1252     // set KO to all solids
1253     for ( size_t i = 0; i < _sdVec.size(); ++i )
1254     {
1255       if ( _sdVec[i]._index == solidId )
1256         continue;
1257       sm = _mesh->GetSubMesh( _sdVec[i]._solid );
1258       if ( !sm->IsEmpty() )
1259         continue;
1260       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
1261       if ( !smError || smError->IsOK() )
1262       {
1263         smError = SMESH_ComputeError::New( COMPERR_ALGO_FAILED, prefix + "failed");
1264         sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
1265       }
1266     }
1267   }
1268   makeGroupOfLE(); // debug
1269
1270   return false;
1271 }
1272
1273 //================================================================================
1274 /*!
1275  * \brief At study restoration, restore event listeners used to clear an inferior
1276  *  dim sub-mesh modified by viscous layers
1277  */
1278 //================================================================================
1279
1280 void _ViscousBuilder::RestoreListeners()
1281 {
1282   // TODO
1283 }
1284
1285 //================================================================================
1286 /*!
1287  * \brief computes SMESH_ProxyMesh::SubMesh::_n2n
1288  */
1289 //================================================================================
1290
1291 bool _ViscousBuilder::MakeN2NMap( _MeshOfSolid* pm )
1292 {
1293   SMESH_subMesh* solidSM = pm->mySubMeshes.front();
1294   TopExp_Explorer fExp( solidSM->GetSubShape(), TopAbs_FACE );
1295   for ( ; fExp.More(); fExp.Next() )
1296   {
1297     SMESHDS_SubMesh* srcSmDS = pm->GetMeshDS()->MeshElements( fExp.Current() );
1298     const SMESH_ProxyMesh::SubMesh* prxSmDS = pm->GetProxySubMesh( fExp.Current() );
1299
1300     if ( !srcSmDS || !prxSmDS || !srcSmDS->NbElements() || !prxSmDS->NbElements() )
1301       continue;
1302     if ( srcSmDS->GetElements()->next() == prxSmDS->GetElements()->next())
1303       continue;
1304
1305     if ( srcSmDS->NbElements() != prxSmDS->NbElements() )
1306       return error( "Different nb elements in a source and a proxy sub-mesh", solidSM->GetId());
1307
1308     SMDS_ElemIteratorPtr srcIt = srcSmDS->GetElements();
1309     SMDS_ElemIteratorPtr prxIt = prxSmDS->GetElements();
1310     while( prxIt->more() )
1311     {
1312       const SMDS_MeshElement* fSrc = srcIt->next();
1313       const SMDS_MeshElement* fPrx = prxIt->next();
1314       if ( fSrc->NbNodes() != fPrx->NbNodes())
1315         return error( "Different elements in a source and a proxy sub-mesh", solidSM->GetId());
1316       for ( int i = 0 ; i < fPrx->NbNodes(); ++i )
1317         pm->setNode2Node( fSrc->GetNode(i), fPrx->GetNode(i), prxSmDS );
1318     }
1319   }
1320   pm->_n2nMapComputed = true;
1321   return true;
1322 }
1323
1324 //================================================================================
1325 /*!
1326  * \brief Does its job
1327  */
1328 //================================================================================
1329
1330 SMESH_ComputeErrorPtr _ViscousBuilder::Compute(SMESH_Mesh&         theMesh,
1331                                                const TopoDS_Shape& theShape)
1332 {
1333   // TODO: set priority of solids during Gen::Compute()
1334
1335   _mesh = & theMesh;
1336
1337   // check if proxy mesh already computed
1338   TopExp_Explorer exp( theShape, TopAbs_SOLID );
1339   if ( !exp.More() )
1340     return error("No SOLID's in theShape"), _error;
1341
1342   if ( _ViscousListener::GetSolidMesh( _mesh, exp.Current(), /*toCreate=*/false))
1343     return SMESH_ComputeErrorPtr(); // everything already computed
1344
1345   PyDump debugDump;
1346
1347   // TODO: ignore already computed SOLIDs 
1348   if ( !findSolidsWithLayers())
1349     return _error;
1350
1351   if ( !findFacesWithLayers() )
1352     return _error;
1353
1354   for ( size_t i = 0; i < _sdVec.size(); ++i )
1355   {
1356     if ( ! makeLayer(_sdVec[i]) )
1357       return _error;
1358
1359     if ( _sdVec[i]._edges.size() == 0 )
1360       continue;
1361     
1362     if ( ! inflate(_sdVec[i]) )
1363       return _error;
1364
1365     if ( ! refine(_sdVec[i]) )
1366       return _error;
1367   }
1368   if ( !shrink() )
1369     return _error;
1370
1371   addBoundaryElements();
1372
1373   makeGroupOfLE(); // debug
1374   debugDump.Finish();
1375
1376   return _error;
1377 }
1378
1379 //================================================================================
1380 /*!
1381  * \brief Check validity of hypotheses
1382  */
1383 //================================================================================
1384
1385 SMESH_ComputeErrorPtr _ViscousBuilder::CheckHypotheses( SMESH_Mesh&         mesh,
1386                                                         const TopoDS_Shape& shape )
1387 {
1388   _mesh = & mesh;
1389
1390   if ( _ViscousListener::GetSolidMesh( _mesh, shape, /*toCreate=*/false))
1391     return SMESH_ComputeErrorPtr(); // everything already computed
1392
1393
1394   findSolidsWithLayers();
1395   bool ok = findFacesWithLayers();
1396
1397   // remove _MeshOfSolid's of _SolidData's
1398   for ( size_t i = 0; i < _sdVec.size(); ++i )
1399     _ViscousListener::RemoveSolidMesh( _mesh, _sdVec[i]._solid );
1400
1401   if ( !ok )
1402     return _error;
1403
1404   return SMESH_ComputeErrorPtr();
1405 }
1406
1407 //================================================================================
1408 /*!
1409  * \brief Finds SOLIDs to compute using viscous layers. Fills _sdVec
1410  */
1411 //================================================================================
1412
1413 bool _ViscousBuilder::findSolidsWithLayers()
1414 {
1415   // get all solids
1416   TopTools_IndexedMapOfShape allSolids;
1417   TopExp::MapShapes( _mesh->GetShapeToMesh(), TopAbs_SOLID, allSolids );
1418   _sdVec.reserve( allSolids.Extent());
1419
1420   SMESH_Gen* gen = _mesh->GetGen();
1421   SMESH_HypoFilter filter;
1422   for ( int i = 1; i <= allSolids.Extent(); ++i )
1423   {
1424     // find StdMeshers_ViscousLayers hyp assigned to the i-th solid
1425     SMESH_Algo* algo = gen->GetAlgo( *_mesh, allSolids(i) );
1426     if ( !algo ) continue;
1427     // TODO: check if algo is hidden
1428     const list <const SMESHDS_Hypothesis *> & allHyps =
1429       algo->GetUsedHypothesis(*_mesh, allSolids(i), /*ignoreAuxiliary=*/false);
1430     _SolidData* soData = 0;
1431     list< const SMESHDS_Hypothesis *>::const_iterator hyp = allHyps.begin();
1432     const StdMeshers_ViscousLayers* viscHyp = 0;
1433     for ( ; hyp != allHyps.end(); ++hyp )
1434       if ( viscHyp = dynamic_cast<const StdMeshers_ViscousLayers*>( *hyp ))
1435       {
1436         TopoDS_Shape hypShape;
1437         filter.Init( filter.Is( viscHyp ));
1438         _mesh->GetHypothesis( allSolids(i), filter, true, &hypShape );
1439
1440         if ( !soData )
1441         {
1442           _MeshOfSolid* proxyMesh = _ViscousListener::GetSolidMesh( _mesh,
1443                                                                     allSolids(i),
1444                                                                     /*toCreate=*/true);
1445           _sdVec.push_back( _SolidData( allSolids(i), proxyMesh ));
1446           soData = & _sdVec.back();
1447           soData->_index = getMeshDS()->ShapeToIndex( allSolids(i));
1448         }
1449         soData->_hyps.push_back( viscHyp );
1450         soData->_hypShapes.push_back( hypShape );
1451       }
1452   }
1453   if ( _sdVec.empty() )
1454     return error
1455       ( SMESH_Comment(StdMeshers_ViscousLayers::GetHypType()) << " hypothesis not found",0);
1456
1457   return true;
1458 }
1459
1460 //================================================================================
1461 /*!
1462  * \brief 
1463  */
1464 //================================================================================
1465
1466 bool _ViscousBuilder::findFacesWithLayers(const bool onlyWith)
1467 {
1468   SMESH_MesherHelper helper( *_mesh );
1469   TopExp_Explorer exp;
1470   TopTools_IndexedMapOfShape solids;
1471
1472   // collect all faces to ignore defined by hyp
1473   for ( size_t i = 0; i < _sdVec.size(); ++i )
1474   {
1475     solids.Add( _sdVec[i]._solid );
1476
1477     // get faces to ignore defined by each hyp
1478     typedef const StdMeshers_ViscousLayers* THyp;
1479     typedef std::pair< set<TGeomID>, THyp > TFacesOfHyp;
1480     list< TFacesOfHyp > ignoreFacesOfHyps;
1481     list< THyp >::iterator              hyp = _sdVec[i]._hyps.begin();
1482     list< TopoDS_Shape >::iterator hypShape = _sdVec[i]._hypShapes.begin();
1483     for ( ; hyp != _sdVec[i]._hyps.end(); ++hyp, ++hypShape )
1484     {
1485       ignoreFacesOfHyps.push_back( TFacesOfHyp( set<TGeomID>(), *hyp ));
1486       getIgnoreFaces( _sdVec[i]._solid, *hyp, *hypShape, ignoreFacesOfHyps.back().first );
1487     }
1488
1489     // fill _SolidData::_face2hyp and check compatibility of hypotheses
1490     const int nbHyps = _sdVec[i]._hyps.size();
1491     if ( nbHyps > 1 )
1492     {
1493       // check if two hypotheses define different parameters for the same FACE
1494       list< TFacesOfHyp >::iterator igFacesOfHyp;
1495       for ( exp.Init( _sdVec[i]._solid, TopAbs_FACE ); exp.More(); exp.Next() )
1496       {
1497         const TGeomID faceID = getMeshDS()->ShapeToIndex( exp.Current() );
1498         THyp hyp = 0;
1499         igFacesOfHyp = ignoreFacesOfHyps.begin();
1500         for ( ; igFacesOfHyp != ignoreFacesOfHyps.end(); ++igFacesOfHyp )
1501           if ( ! igFacesOfHyp->first.count( faceID ))
1502           {
1503             if ( hyp )
1504               return error(SMESH_Comment("Several hypotheses define "
1505                                          "Viscous Layers on the face #") << faceID );
1506             hyp = igFacesOfHyp->second;
1507           }
1508         if ( hyp )
1509           _sdVec[i]._face2hyp.insert( make_pair( faceID, hyp ));
1510         else
1511           _sdVec[i]._ignoreFaceIds.insert( faceID );
1512       }
1513
1514       // check if two hypotheses define different number of viscous layers for
1515       // adjacent faces of a solid
1516       set< int > nbLayersSet;
1517       igFacesOfHyp = ignoreFacesOfHyps.begin();
1518       for ( ; igFacesOfHyp != ignoreFacesOfHyps.end(); ++igFacesOfHyp )
1519       {
1520         nbLayersSet.insert( igFacesOfHyp->second->GetNumberLayers() );
1521       }
1522       if ( nbLayersSet.size() > 1 )
1523       {
1524         for ( exp.Init( _sdVec[i]._solid, TopAbs_EDGE ); exp.More(); exp.Next() )
1525         {
1526           PShapeIteratorPtr fIt = helper.GetAncestors( exp.Current(), *_mesh, TopAbs_FACE );
1527           THyp hyp1 = 0, hyp2 = 0;
1528           while( const TopoDS_Shape* face = fIt->next() )
1529           {
1530             const TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
1531             map< TGeomID, THyp >::iterator f2h = _sdVec[i]._face2hyp.find( faceID );
1532             if ( f2h != _sdVec[i]._face2hyp.end() )
1533             {
1534               ( hyp1 ? hyp2 : hyp1 ) = f2h->second;
1535             }
1536           }
1537           if ( hyp1 && hyp2 &&
1538                hyp1->GetNumberLayers() != hyp2->GetNumberLayers() )
1539           {
1540             return error("Two hypotheses define different number of "
1541                          "viscous layers on adjacent faces");
1542           }
1543         }
1544       }
1545     } // if ( nbHyps > 1 )
1546     else
1547     {
1548       _sdVec[i]._ignoreFaceIds.swap( ignoreFacesOfHyps.back().first );
1549     }
1550
1551     // fill _SolidData::_reversedFaceIds
1552     {
1553       exp.Init( _sdVec[i]._solid.Oriented( TopAbs_FORWARD ), TopAbs_FACE );
1554       for ( ; exp.More(); exp.Next() )
1555       {
1556         const TopoDS_Face& face = TopoDS::Face( exp.Current() );
1557         const TGeomID faceID = getMeshDS()->ShapeToIndex( face );
1558         if ( //!sdVec[i]._ignoreFaceIds.count( faceID ) && ???????
1559              helper.NbAncestors( face, *_mesh, TopAbs_SOLID ) > 1 &&
1560              helper.IsReversedSubMesh( face ))
1561         {
1562           _sdVec[i]._reversedFaceIds.insert( faceID );
1563         }
1564       }
1565     }
1566   } // loop on _sdVec
1567
1568   if ( onlyWith ) // is called to check hypotheses compatibility only
1569     return true;
1570
1571   // Find faces to shrink mesh on (solution 2 in issue 0020832);
1572   TopTools_IndexedMapOfShape shapes;
1573   for ( size_t i = 0; i < _sdVec.size(); ++i )
1574   {
1575     shapes.Clear();
1576     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_EDGE, shapes);
1577     for ( int iE = 1; iE <= shapes.Extent(); ++iE )
1578     {
1579       const TopoDS_Shape& edge = shapes(iE);
1580       // find 2 faces sharing an edge
1581       TopoDS_Shape FF[2];
1582       PShapeIteratorPtr fIt = helper.GetAncestors(edge, *_mesh, TopAbs_FACE);
1583       while ( fIt->more())
1584       {
1585         const TopoDS_Shape* f = fIt->next();
1586         if ( helper.IsSubShape( *f, _sdVec[i]._solid))
1587           FF[ int( !FF[0].IsNull()) ] = *f;
1588       }
1589       if( FF[1].IsNull() ) continue; // seam edge can be shared by 1 FACE only
1590       // check presence of layers on them
1591       int ignore[2];
1592       for ( int j = 0; j < 2; ++j )
1593         ignore[j] = _sdVec[i]._ignoreFaceIds.count ( getMeshDS()->ShapeToIndex( FF[j] ));
1594       if ( ignore[0] == ignore[1] )
1595         continue; // nothing interesting
1596       TopoDS_Shape fWOL = FF[ ignore[0] ? 0 : 1 ];
1597       // check presence of layers on fWOL within an adjacent SOLID
1598       bool collision = false;
1599       PShapeIteratorPtr sIt = helper.GetAncestors( fWOL, *_mesh, TopAbs_SOLID );
1600       while ( const TopoDS_Shape* solid = sIt->next() )
1601         if ( !solid->IsSame( _sdVec[i]._solid ))
1602         {
1603           int iSolid = solids.FindIndex( *solid );
1604           int  iFace = getMeshDS()->ShapeToIndex( fWOL );
1605           if ( iSolid > 0 && !_sdVec[ iSolid-1 ]._ignoreFaceIds.count( iFace ))
1606           {
1607             //_sdVec[i]._noShrinkShapes.insert( iFace );
1608             //fWOL.Nullify();
1609             collision = true;
1610           }
1611         }
1612       // add edge to maps
1613       if ( !fWOL.IsNull())
1614       {
1615         TGeomID edgeInd = getMeshDS()->ShapeToIndex( edge );
1616         _sdVec[i]._shrinkShape2Shape.insert( make_pair( edgeInd, fWOL ));
1617         if ( collision )
1618         {
1619           // _shrinkShape2Shape will be used to temporary inflate _LayerEdge's based
1620           // on the edge but shrink won't be performed
1621           _sdVec[i]._noShrinkShapes.insert( edgeInd );
1622         }
1623       }
1624     }
1625   }
1626   // Exclude from _shrinkShape2Shape FACE's that can't be shrinked since
1627   // the algo of the SOLID sharing the FACE does not support it
1628   set< string > notSupportAlgos; notSupportAlgos.insert("Hexa_3D");
1629   for ( size_t i = 0; i < _sdVec.size(); ++i )
1630   {
1631     map< TGeomID, TopoDS_Shape >::iterator e2f = _sdVec[i]._shrinkShape2Shape.begin();
1632     for ( ; e2f != _sdVec[i]._shrinkShape2Shape.end(); ++e2f )
1633     {
1634       const TopoDS_Shape& fWOL = e2f->second;
1635       const TGeomID     edgeID = e2f->first;
1636       bool notShrinkFace = false;
1637       PShapeIteratorPtr soIt = helper.GetAncestors(fWOL, *_mesh, TopAbs_SOLID);
1638       while ( soIt->more() )
1639       {
1640         const TopoDS_Shape* solid = soIt->next();
1641         if ( _sdVec[i]._solid.IsSame( *solid )) continue;
1642         SMESH_Algo* algo = _mesh->GetGen()->GetAlgo( *_mesh, *solid );
1643         if ( !algo || !notSupportAlgos.count( algo->GetName() )) continue;
1644         notShrinkFace = true;
1645         size_t iSolid = 0;
1646         for ( ; iSolid < _sdVec.size(); ++iSolid )
1647         {
1648           if ( _sdVec[iSolid]._solid.IsSame( *solid ) ) {
1649             if ( _sdVec[iSolid]._shrinkShape2Shape.count( edgeID ))
1650               notShrinkFace = false;
1651             break;
1652           }
1653         }
1654         if ( notShrinkFace )
1655         {
1656           _sdVec[i]._noShrinkShapes.insert( edgeID );
1657
1658           // add VERTEXes of the edge in _noShrinkShapes
1659           TopoDS_Shape edge = getMeshDS()->IndexToShape( edgeID );
1660           for ( TopoDS_Iterator vIt( edge ); vIt.More(); vIt.Next() )
1661             _sdVec[i]._noShrinkShapes.insert( getMeshDS()->ShapeToIndex( vIt.Value() ));
1662
1663           // check if there is a collision with to-shrink-from EDGEs in iSolid
1664           if ( iSolid == _sdVec.size() )
1665             continue; // no VL in the solid
1666           shapes.Clear();
1667           TopExp::MapShapes( fWOL, TopAbs_EDGE, shapes);
1668           for ( int iE = 1; iE <= shapes.Extent(); ++iE )
1669           {
1670             const TopoDS_Edge& E = TopoDS::Edge( shapes( iE ));
1671             const TGeomID    eID = getMeshDS()->ShapeToIndex( E );
1672             if ( eID == edgeID ||
1673                  !_sdVec[iSolid]._shrinkShape2Shape.count( eID ) ||
1674                  _sdVec[i]._noShrinkShapes.count( eID ))
1675               continue;
1676             for ( int is1st = 0; is1st < 2; ++is1st )
1677             {
1678               TopoDS_Vertex V = helper.IthVertex( is1st, E );
1679               if ( _sdVec[i]._noShrinkShapes.count( getMeshDS()->ShapeToIndex( V ) ))
1680               {
1681                 // _sdVec[i]._noShrinkShapes.insert( eID );
1682                 // V = helper.IthVertex( !is1st, E );
1683                 // _sdVec[i]._noShrinkShapes.insert( getMeshDS()->ShapeToIndex( V ));
1684                 //iE = 0; // re-start the loop on EDGEs of fWOL
1685                 return error("No way to make a conformal mesh with "
1686                              "the given set of faces with layers", _sdVec[i]._index);
1687               }
1688             }
1689           }
1690         }
1691
1692       } // while ( soIt->more() )
1693     } // loop on _sdVec[i]._shrinkShape2Shape
1694   } // loop on _sdVec to fill in _SolidData::_noShrinkShapes
1695
1696   // Find the SHAPE along which to inflate _LayerEdge based on VERTEX
1697
1698   for ( size_t i = 0; i < _sdVec.size(); ++i )
1699   {
1700     shapes.Clear();
1701     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_VERTEX, shapes);
1702     for ( int iV = 1; iV <= shapes.Extent(); ++iV )
1703     {
1704       const TopoDS_Shape& vertex = shapes(iV);
1705       // find faces WOL sharing the vertex
1706       vector< TopoDS_Shape > facesWOL;
1707       int totalNbFaces = 0;
1708       PShapeIteratorPtr fIt = helper.GetAncestors(vertex, *_mesh, TopAbs_FACE);
1709       while ( fIt->more())
1710       {
1711         const TopoDS_Shape* f = fIt->next();
1712         if ( helper.IsSubShape( *f, _sdVec[i]._solid ) )
1713         {
1714           totalNbFaces++;
1715           const int fID = getMeshDS()->ShapeToIndex( *f );
1716           if ( _sdVec[i]._ignoreFaceIds.count ( fID ) /*&&
1717                !_sdVec[i]._noShrinkShapes.count( fID )*/)
1718             facesWOL.push_back( *f );
1719         }
1720       }
1721       if ( facesWOL.size() == totalNbFaces || facesWOL.empty() )
1722         continue; // no layers at this vertex or no WOL
1723       TGeomID vInd = getMeshDS()->ShapeToIndex( vertex );
1724       switch ( facesWOL.size() )
1725       {
1726       case 1:
1727       {
1728         helper.SetSubShape( facesWOL[0] );
1729         if ( helper.IsRealSeam( vInd )) // inflate along a seam edge?
1730         {
1731           TopoDS_Shape seamEdge;
1732           PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
1733           while ( eIt->more() && seamEdge.IsNull() )
1734           {
1735             const TopoDS_Shape* e = eIt->next();
1736             if ( helper.IsRealSeam( *e ) )
1737               seamEdge = *e;
1738           }
1739           if ( !seamEdge.IsNull() )
1740           {
1741             _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, seamEdge ));
1742             break;
1743           }
1744         }
1745         _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, facesWOL[0] ));
1746         break;
1747       }
1748       case 2:
1749       {
1750         // find an edge shared by 2 faces
1751         PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE);
1752         while ( eIt->more())
1753         {
1754           const TopoDS_Shape* e = eIt->next();
1755           if ( helper.IsSubShape( *e, facesWOL[0]) &&
1756                helper.IsSubShape( *e, facesWOL[1]))
1757           {
1758             _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, *e )); break;
1759           }
1760         }
1761         break;
1762       }
1763       default:
1764         return error("Not yet supported case", _sdVec[i]._index);
1765       }
1766     }
1767   }
1768
1769   // add FACEs of other SOLIDs to _ignoreFaceIds
1770   for ( size_t i = 0; i < _sdVec.size(); ++i )
1771   {
1772     shapes.Clear();
1773     TopExp::MapShapes(_sdVec[i]._solid, TopAbs_FACE, shapes);
1774
1775     for ( exp.Init( _mesh->GetShapeToMesh(), TopAbs_FACE ); exp.More(); exp.Next() )
1776     {
1777       if ( !shapes.Contains( exp.Current() ))
1778         _sdVec[i]._ignoreFaceIds.insert( getMeshDS()->ShapeToIndex( exp.Current() ));
1779     }
1780   }
1781
1782   return true;
1783 }
1784
1785 //================================================================================
1786 /*!
1787  * \brief Finds FACEs w/o layers for a given SOLID by an hypothesis
1788  */
1789 //================================================================================
1790
1791 void _ViscousBuilder::getIgnoreFaces(const TopoDS_Shape&             solid,
1792                                      const StdMeshers_ViscousLayers* hyp,
1793                                      const TopoDS_Shape&             hypShape,
1794                                      set<TGeomID>&                   ignoreFaceIds)
1795 {
1796   TopExp_Explorer exp;
1797
1798   vector<TGeomID> ids = hyp->GetBndShapes();
1799   if ( hyp->IsToIgnoreShapes() ) // FACEs to ignore are given
1800   {
1801     for ( size_t ii = 0; ii < ids.size(); ++ii )
1802     {
1803       const TopoDS_Shape& s = getMeshDS()->IndexToShape( ids[ii] );
1804       if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
1805         ignoreFaceIds.insert( ids[ii] );
1806     }
1807   }
1808   else // FACEs with layers are given
1809   {
1810     exp.Init( solid, TopAbs_FACE );
1811     for ( ; exp.More(); exp.Next() )
1812     {
1813       TGeomID faceInd = getMeshDS()->ShapeToIndex( exp.Current() );
1814       if ( find( ids.begin(), ids.end(), faceInd ) == ids.end() )
1815         ignoreFaceIds.insert( faceInd );
1816     }
1817   }
1818
1819   // ignore internal FACEs if inlets and outlets are specified
1820   if ( hyp->IsToIgnoreShapes() )
1821   {
1822     TopTools_IndexedDataMapOfShapeListOfShape solidsOfFace;
1823     TopExp::MapShapesAndAncestors( hypShape,
1824                                    TopAbs_FACE, TopAbs_SOLID, solidsOfFace);
1825
1826     for ( exp.Init( solid, TopAbs_FACE ); exp.More(); exp.Next() )
1827     {
1828       const TopoDS_Face& face = TopoDS::Face( exp.Current() );
1829       if ( SMESH_MesherHelper::NbAncestors( face, *_mesh, TopAbs_SOLID ) < 2 )
1830         continue;
1831
1832       int nbSolids = solidsOfFace.FindFromKey( face ).Extent();
1833       if ( nbSolids > 1 )
1834         ignoreFaceIds.insert( getMeshDS()->ShapeToIndex( face ));
1835     }
1836   }
1837 }
1838
1839 //================================================================================
1840 /*!
1841  * \brief Create the inner surface of the viscous layer and prepare data for infation
1842  */
1843 //================================================================================
1844
1845 bool _ViscousBuilder::makeLayer(_SolidData& data)
1846 {
1847   // get all sub-shapes to make layers on
1848   set<TGeomID> subIds, faceIds;
1849   subIds = data._noShrinkShapes;
1850   TopExp_Explorer exp( data._solid, TopAbs_FACE );
1851   for ( ; exp.More(); exp.Next() )
1852     {
1853       SMESH_subMesh* fSubM = _mesh->GetSubMesh( exp.Current() );
1854       if ( ! data._ignoreFaceIds.count( fSubM->GetId() ))
1855         faceIds.insert( fSubM->GetId() );
1856       SMESH_subMeshIteratorPtr subIt = fSubM->getDependsOnIterator(/*includeSelf=*/true);
1857       while ( subIt->more() )
1858         subIds.insert( subIt->next()->GetId() );
1859     }
1860
1861   // make a map to find new nodes on sub-shapes shared with other SOLID
1862   map< TGeomID, TNode2Edge* >::iterator s2ne;
1863   map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
1864   for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
1865   {
1866     TGeomID shapeInd = s2s->first;
1867     for ( size_t i = 0; i < _sdVec.size(); ++i )
1868     {
1869       if ( _sdVec[i]._index == data._index ) continue;
1870       map< TGeomID, TopoDS_Shape >::iterator s2s2 = _sdVec[i]._shrinkShape2Shape.find( shapeInd );
1871       if ( s2s2 != _sdVec[i]._shrinkShape2Shape.end() &&
1872            *s2s == *s2s2 && !_sdVec[i]._n2eMap.empty() )
1873       {
1874         data._s2neMap.insert( make_pair( shapeInd, &_sdVec[i]._n2eMap ));
1875         break;
1876       }
1877     }
1878   }
1879
1880   // Create temporary faces and _LayerEdge's
1881
1882   dumpFunction(SMESH_Comment("makeLayers_")<<data._index); 
1883
1884   data._stepSize = Precision::Infinite();
1885   data._stepSizeNodes[0] = 0;
1886
1887   SMESH_MesherHelper helper( *_mesh );
1888   helper.SetSubShape( data._solid );
1889   helper.SetElementsOnShape( true );
1890
1891   vector< const SMDS_MeshNode*> newNodes; // of a mesh face
1892   TNode2Edge::iterator n2e2;
1893
1894   // collect _LayerEdge's of shapes they are based on
1895   const int nbShapes = getMeshDS()->MaxShapeIndex();
1896   vector< vector<_LayerEdge*> > edgesByGeom( nbShapes+1 );
1897
1898   for ( set<TGeomID>::iterator id = faceIds.begin(); id != faceIds.end(); ++id )
1899   {
1900     SMESHDS_SubMesh* smDS = getMeshDS()->MeshElements( *id );
1901     if ( !smDS ) return error(SMESH_Comment("Not meshed face ") << *id, data._index );
1902
1903     const TopoDS_Face& F = TopoDS::Face( getMeshDS()->IndexToShape( *id ));
1904     SMESH_ProxyMesh::SubMesh* proxySub =
1905       data._proxyMesh->getFaceSubM( F, /*create=*/true);
1906
1907     SMDS_ElemIteratorPtr eIt = smDS->GetElements();
1908     while ( eIt->more() )
1909     {
1910       const SMDS_MeshElement* face = eIt->next();
1911       double          faceMaxCosin = -1;
1912       _LayerEdge*     maxCosinEdge = 0;
1913       int             nbDegenNodes = 0;
1914
1915       newNodes.resize( face->NbCornerNodes() );
1916       for ( size_t i = 0 ; i < newNodes.size(); ++i )
1917       {
1918         const SMDS_MeshNode* n = face->GetNode( i );
1919         const int      shapeID = n->getshapeId();
1920         const bool onDegenShap = helper.IsDegenShape( shapeID );
1921         const bool onDegenEdge = ( onDegenShap && n->GetPosition()->GetDim() == 1 );
1922         if ( onDegenShap )
1923         {
1924           if ( onDegenEdge )
1925           {
1926             // substitute n on a degenerated EDGE with a node on a corresponding VERTEX
1927             const TopoDS_Shape& E = getMeshDS()->IndexToShape( shapeID );
1928             TopoDS_Vertex       V = helper.IthVertex( 0, TopoDS::Edge( E ));
1929             if ( const SMDS_MeshNode* vN = SMESH_Algo::VertexNode( V, getMeshDS() )) {
1930               n = vN;
1931               nbDegenNodes++;
1932             }
1933           }
1934           else
1935           {
1936             nbDegenNodes++;
1937           }
1938         }
1939         TNode2Edge::iterator n2e = data._n2eMap.insert( make_pair( n, (_LayerEdge*)0 )).first;
1940         if ( !(*n2e).second )
1941         {
1942           // add a _LayerEdge
1943           _LayerEdge* edge = new _LayerEdge();
1944           edge->_nodes.push_back( n );
1945           n2e->second = edge;
1946           edgesByGeom[ shapeID ].push_back( edge );
1947           const bool noShrink = data._noShrinkShapes.count( shapeID );
1948
1949           SMESH_TNodeXYZ xyz( n );
1950
1951           // set edge data or find already refined _LayerEdge and get data from it
1952           if (( !noShrink                                                     ) &&
1953               ( n->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE        ) &&
1954               (( s2ne = data._s2neMap.find( shapeID )) != data._s2neMap.end() ) &&
1955               (( n2e2 = (*s2ne).second->find( n )) != s2ne->second->end()     ))
1956           {
1957             _LayerEdge* foundEdge = (*n2e2).second;
1958             gp_XYZ        lastPos = edge->Copy( *foundEdge, helper );
1959             foundEdge->_pos.push_back( lastPos );
1960             // location of the last node is modified and we restore it by foundEdge->_pos.back()
1961             const_cast< SMDS_MeshNode* >
1962               ( edge->_nodes.back() )->setXYZ( xyz.X(), xyz.Y(), xyz.Z() );
1963           }
1964           else
1965           {
1966             if ( !noShrink )
1967             {
1968               edge->_nodes.push_back( helper.AddNode( xyz.X(), xyz.Y(), xyz.Z() ));
1969             }
1970             if ( !setEdgeData( *edge, subIds, helper, data ))
1971               return false;
1972           }
1973           dumpMove(edge->_nodes.back());
1974
1975           if ( edge->_cosin > faceMaxCosin )
1976           {
1977             faceMaxCosin = edge->_cosin;
1978             maxCosinEdge = edge;
1979           }
1980         }
1981         newNodes[ i ] = n2e->second->_nodes.back();
1982
1983         if ( onDegenEdge )
1984           data._n2eMap.insert( make_pair( face->GetNode( i ), n2e->second ));
1985       }
1986       if ( newNodes.size() - nbDegenNodes < 2 )
1987         continue;
1988
1989       // create a temporary face
1990       const SMDS_MeshElement* newFace =
1991         new _TmpMeshFace( newNodes, --_tmpFaceID, face->getshapeId() );
1992       proxySub->AddElement( newFace );
1993
1994       // compute inflation step size by min size of element on a convex surface
1995       if ( faceMaxCosin > theMinSmoothCosin )
1996         limitStepSize( data, face, maxCosinEdge );
1997
1998     } // loop on 2D elements on a FACE
1999   } // loop on FACEs of a SOLID
2000
2001   data._epsilon = 1e-7;
2002   if ( data._stepSize < 1. )
2003     data._epsilon *= data._stepSize;
2004
2005   // Put _LayerEdge's into the vector data._edges
2006   if ( !sortEdges( data, edgesByGeom ))
2007     return false;
2008
2009   // limit data._stepSize depending on surface curvature and fill data._convexFaces
2010   limitStepSizeByCurvature( data ); // !!! it must be before node substitution in _Simplex
2011
2012   // Set target nodes into _Simplex and _LayerEdge's to _2NearEdges
2013   TNode2Edge::iterator n2e;
2014   const SMDS_MeshNode* nn[2];
2015   for ( size_t i = 0; i < data._edges.size(); ++i )
2016   {
2017     _LayerEdge* edge = data._edges[i];
2018     if ( edge->IsOnEdge() )
2019     {
2020       // get neighbor nodes
2021       bool hasData = ( edge->_2neibors->_edges[0] );
2022       if ( hasData ) // _LayerEdge is a copy of another one
2023       {
2024         nn[0] = edge->_2neibors->srcNode(0);
2025         nn[1] = edge->_2neibors->srcNode(1);
2026       }
2027       else if ( !findNeiborsOnEdge( edge, nn[0],nn[1], data ))
2028       {
2029         return false;
2030       }
2031       // set neighbor _LayerEdge's
2032       for ( int j = 0; j < 2; ++j )
2033       {
2034         if (( n2e = data._n2eMap.find( nn[j] )) == data._n2eMap.end() )
2035           return error("_LayerEdge not found by src node", data._index);
2036         edge->_2neibors->_edges[j] = n2e->second;
2037       }
2038       if ( !hasData )
2039         edge->SetDataByNeighbors( nn[0], nn[1], helper);
2040     }
2041
2042     for ( size_t j = 0; j < edge->_simplices.size(); ++j )
2043     {
2044       _Simplex& s = edge->_simplices[j];
2045       s._nNext = data._n2eMap[ s._nNext ]->_nodes.back();
2046       s._nPrev = data._n2eMap[ s._nPrev ]->_nodes.back();
2047     }
2048
2049     // For an _LayerEdge on a degenerated EDGE, copy some data from
2050     // a corresponding _LayerEdge on a VERTEX
2051     // (issue 52453, pb on a downloaded SampleCase2-Tet-netgen-mephisto.hdf)
2052     if ( helper.IsDegenShape( edge->_nodes[0]->getshapeId() ))
2053     {
2054       // Generally we should not get here
2055       const TopoDS_Shape& E = getMeshDS()->IndexToShape( edge->_nodes[0]->getshapeId() );
2056       if ( E.ShapeType() != TopAbs_EDGE )
2057         continue;
2058       TopoDS_Vertex V = helper.IthVertex( 0, TopoDS::Edge( E ));
2059       const SMDS_MeshNode* vN = SMESH_Algo::VertexNode( V, getMeshDS() );
2060       if (( n2e = data._n2eMap.find( vN )) == data._n2eMap.end() )
2061         continue;
2062       const _LayerEdge* vEdge = n2e->second;
2063       edge->_normal    = vEdge->_normal;
2064       edge->_lenFactor = vEdge->_lenFactor;
2065       edge->_cosin     = vEdge->_cosin;
2066     }
2067   }
2068
2069   dumpFunctionEnd();
2070   return true;
2071 }
2072
2073 //================================================================================
2074 /*!
2075  * \brief Compute inflation step size by min size of element on a convex surface
2076  */
2077 //================================================================================
2078
2079 void _ViscousBuilder::limitStepSize( _SolidData&             data,
2080                                      const SMDS_MeshElement* face,
2081                                      const _LayerEdge*       maxCosinEdge )
2082 {
2083   int iN = 0;
2084   double minSize = 10 * data._stepSize;
2085   const int nbNodes = face->NbCornerNodes();
2086   for ( int i = 0; i < nbNodes; ++i )
2087   {
2088     const SMDS_MeshNode* nextN = face->GetNode( SMESH_MesherHelper::WrapIndex( i+1, nbNodes ));
2089     const SMDS_MeshNode*  curN = face->GetNode( i );
2090     if ( nextN->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ||
2091          curN-> GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2092     {
2093       double dist = SMESH_TNodeXYZ( curN ).Distance( nextN );
2094       if ( dist < minSize )
2095         minSize = dist, iN = i;
2096     }
2097   }
2098   double newStep = 0.8 * minSize / maxCosinEdge->_lenFactor;
2099   if ( newStep < data._stepSize )
2100   {
2101     data._stepSize = newStep;
2102     data._stepSizeCoeff = 0.8 / maxCosinEdge->_lenFactor;
2103     data._stepSizeNodes[0] = face->GetNode( iN );
2104     data._stepSizeNodes[1] = face->GetNode( SMESH_MesherHelper::WrapIndex( iN+1, nbNodes ));
2105   }
2106 }
2107
2108 //================================================================================
2109 /*!
2110  * \brief Compute inflation step size by min size of element on a convex surface
2111  */
2112 //================================================================================
2113
2114 void _ViscousBuilder::limitStepSize( _SolidData& data, const double minSize )
2115 {
2116   if ( minSize < data._stepSize )
2117   {
2118     data._stepSize = minSize;
2119     if ( data._stepSizeNodes[0] )
2120     {
2121       double dist =
2122         SMESH_TNodeXYZ(data._stepSizeNodes[0]).Distance(data._stepSizeNodes[1]);
2123       data._stepSizeCoeff = data._stepSize / dist;
2124     }
2125   }
2126 }
2127
2128 //================================================================================
2129 /*!
2130  * \brief Limit data._stepSize by evaluating curvature of shapes and fill data._convexFaces
2131  */
2132 //================================================================================
2133
2134 void _ViscousBuilder::limitStepSizeByCurvature( _SolidData& data )
2135 {
2136   const int nbTestPnt = 5; // on a FACE sub-shape
2137
2138   BRepLProp_SLProps surfProp( 2, 1e-6 );
2139   SMESH_MesherHelper helper( *_mesh );
2140
2141   data._convexFaces.clear();
2142
2143   TopExp_Explorer face( data._solid, TopAbs_FACE );
2144   for ( ; face.More(); face.Next() )
2145   {
2146     const TopoDS_Face& F = TopoDS::Face( face.Current() );
2147     SMESH_subMesh *   sm = _mesh->GetSubMesh( F );
2148     const TGeomID faceID = sm->GetId();
2149     if ( data._ignoreFaceIds.count( faceID )) continue;
2150
2151     BRepAdaptor_Surface surface( F, false );
2152     surfProp.SetSurface( surface );
2153
2154     bool isTooCurved = false;
2155     int iBeg, iEnd;
2156
2157     _ConvexFace cnvFace;
2158     const double        oriFactor = ( F.Orientation() == TopAbs_REVERSED ? +1. : -1. );
2159     SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/true);
2160     while ( smIt->more() )
2161     {
2162       sm = smIt->next();
2163       const TGeomID subID = sm->GetId();
2164       // find _LayerEdge's of a sub-shape
2165       size_t edgesEnd;
2166       if ( data.GetShapeEdges( subID, edgesEnd, &iBeg, &iEnd ))
2167         cnvFace._subIdToEdgeEnd.insert( make_pair( subID, edgesEnd ));
2168       else
2169         continue;
2170       // check concavity and curvature and limit data._stepSize
2171       const double minCurvature = 0.9 / data._hypOnShape[ edgesEnd ].GetTotalThickness();
2172       int nbLEdges = iEnd - iBeg;
2173       int iStep    = Max( 1, nbLEdges / nbTestPnt );
2174       for ( ; iBeg < iEnd; iBeg += iStep )
2175       {
2176         gp_XY uv = helper.GetNodeUV( F, data._edges[ iBeg ]->_nodes[0] );
2177         surfProp.SetParameters( uv.X(), uv.Y() );
2178         if ( !surfProp.IsCurvatureDefined() )
2179           continue;
2180         if ( surfProp.MaxCurvature() * oriFactor > minCurvature )
2181         {
2182           limitStepSize( data, 0.9 / surfProp.MaxCurvature() * oriFactor );
2183           isTooCurved = true;
2184         }
2185         if ( surfProp.MinCurvature() * oriFactor > minCurvature )
2186         {
2187           limitStepSize( data, 0.9 / surfProp.MinCurvature() * oriFactor );
2188           isTooCurved = true;
2189         }
2190       }
2191     } // loop on sub-shapes of the FACE
2192
2193     if ( !isTooCurved ) continue;
2194
2195     _ConvexFace & convFace =
2196       data._convexFaces.insert( make_pair( faceID, cnvFace )).first->second;
2197
2198     convFace._face = F;
2199     convFace._normalsFixed = false;
2200
2201     // Fill _ConvexFace::_simplexTestEdges. These _LayerEdge's are used to detect
2202     // prism distortion.
2203     map< TGeomID, int >::iterator id2end = convFace._subIdToEdgeEnd.find( faceID );
2204     if ( id2end != convFace._subIdToEdgeEnd.end() )
2205     {
2206       // there are _LayerEdge's on the FACE it-self;
2207       // select _LayerEdge's near EDGEs
2208       data.GetEdgesOnShape( id2end->second, iBeg, iEnd );
2209       for ( ; iBeg < iEnd; ++iBeg )
2210       {
2211         _LayerEdge* ledge = data._edges[ iBeg ];
2212         for ( size_t j = 0; j < ledge->_simplices.size(); ++j )
2213           if ( ledge->_simplices[j]._nNext->GetPosition()->GetDim() < 2 )
2214           {
2215             convFace._simplexTestEdges.push_back( ledge );
2216             break;
2217           }
2218       }
2219     }
2220     else
2221     {
2222       // where there are no _LayerEdge's on a _ConvexFace,
2223       // as e.g. on a fillet surface with no internal nodes - issue 22580,
2224       // so that collision of viscous internal faces is not detected by check of
2225       // intersection of _LayerEdge's with the viscous internal faces.
2226
2227       set< const SMDS_MeshNode* > usedNodes;
2228
2229       // look for _LayerEdge's with null _sWOL
2230       map< TGeomID, int >::iterator id2end = convFace._subIdToEdgeEnd.begin();
2231       for ( ; id2end != convFace._subIdToEdgeEnd.end(); ++id2end )
2232       {
2233         data.GetEdgesOnShape( id2end->second, iBeg, iEnd );
2234         if ( iBeg >= iEnd || !data._edges[ iBeg ]->_sWOL.IsNull() )
2235           continue;
2236         for ( ; iBeg < iEnd; ++iBeg )
2237         {
2238           _LayerEdge* ledge = data._edges[ iBeg ];
2239           const SMDS_MeshNode* srcNode = ledge->_nodes[0];
2240           if ( !usedNodes.insert( srcNode ).second ) continue;
2241
2242           getSimplices( srcNode, ledge->_simplices, data._ignoreFaceIds, &data );
2243           for ( size_t i = 0; i < ledge->_simplices.size(); ++i )
2244           {
2245             usedNodes.insert( ledge->_simplices[i]._nPrev );
2246             usedNodes.insert( ledge->_simplices[i]._nNext );
2247           }
2248           convFace._simplexTestEdges.push_back( ledge );
2249         }
2250       }
2251     }
2252   } // loop on FACEs of data._solid
2253 }
2254
2255 //================================================================================
2256 /*!
2257  * \brief Separate shapes (and _LayerEdge's on them) to smooth from the rest ones
2258  */
2259 //================================================================================
2260
2261 bool _ViscousBuilder::sortEdges( _SolidData&                    data,
2262                                  vector< vector<_LayerEdge*> >& edgesByGeom)
2263 {
2264   // define allowed thickness
2265   computeGeomSize( data ); // compute data._geomSize
2266
2267   data._maxThickness = 0;
2268   data._minThickness = 1e100;
2269   list< const StdMeshers_ViscousLayers* >::iterator hyp = data._hyps.begin();
2270   for ( ; hyp != data._hyps.end(); ++hyp )
2271   {
2272     data._maxThickness = Max( data._maxThickness, (*hyp)->GetTotalThickness() );
2273     data._minThickness = Min( data._minThickness, (*hyp)->GetTotalThickness() );
2274   }
2275   const double tgtThick = /*Min( 0.5 * data._geomSize, */data._maxThickness;
2276
2277   // Find shapes needing smoothing; such a shape has _LayerEdge._normal on it's
2278   // boundry inclined to the shape at a sharp angle
2279
2280   list< TGeomID > shapesToSmooth;
2281   
2282   SMESH_MesherHelper helper( *_mesh );
2283   bool ok = true;
2284
2285   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS )
2286   {
2287     vector<_LayerEdge*>& eS = edgesByGeom[iS];
2288     if ( eS.empty() ) continue;
2289     const TopoDS_Shape& S = getMeshDS()->IndexToShape( iS );
2290     bool needSmooth = false;
2291     switch ( S.ShapeType() )
2292     {
2293     case TopAbs_EDGE: {
2294
2295       if ( SMESH_Algo::isDegenerated( TopoDS::Edge( S )))
2296         break;
2297       //bool isShrinkEdge = !eS[0]->_sWOL.IsNull();
2298       for ( TopoDS_Iterator vIt( S ); vIt.More() && !needSmooth; vIt.Next() )
2299       {
2300         TGeomID iV = getMeshDS()->ShapeToIndex( vIt.Value() );
2301         vector<_LayerEdge*>& eV = edgesByGeom[ iV ];
2302         if ( eV.empty() ) continue;
2303         gp_Vec eDir = getEdgeDir( TopoDS::Edge( S ), TopoDS::Vertex( vIt.Value() ));
2304         double angle = eDir.Angle( eV[0]->_normal );
2305         double cosin = Cos( angle );
2306         if ( cosin > theMinSmoothCosin )
2307         {
2308           // compare tgtThick with the length of an end segment
2309           SMDS_ElemIteratorPtr eIt = eV[0]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Edge);
2310           while ( eIt->more() )
2311           {
2312             const SMDS_MeshElement* endSeg = eIt->next();
2313             if ( endSeg->getshapeId() == iS )
2314             {
2315               double segLen =
2316                 SMESH_TNodeXYZ( endSeg->GetNode(0) ).Distance( endSeg->GetNode(1 ));
2317               needSmooth = needSmoothing( cosin, tgtThick, segLen );
2318               break;
2319             }
2320           }
2321         }
2322       }
2323       break;
2324     }
2325     case TopAbs_FACE: {
2326
2327       for ( TopExp_Explorer eExp( S, TopAbs_EDGE ); eExp.More() && !needSmooth; eExp.Next() )
2328       {
2329         TGeomID iE = getMeshDS()->ShapeToIndex( eExp.Current() );
2330         vector<_LayerEdge*>& eE = edgesByGeom[ iE ];
2331         if ( eE.empty() ) continue;
2332         // TopLoc_Location loc;
2333         // Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face( S ), loc );
2334         // bool isPlane = GeomLib_IsPlanarSurface( surface ).IsPlanar();
2335         //if ( eE[0]->_sWOL.IsNull() )
2336         {
2337           double faceSize;
2338           for ( size_t i = 0; i < eE.size() && !needSmooth; ++i )
2339             if ( eE[i]->_cosin > theMinSmoothCosin )
2340             {
2341               SMDS_ElemIteratorPtr fIt = eE[i]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
2342               while ( fIt->more() && !needSmooth )
2343               {
2344                 const SMDS_MeshElement* face = fIt->next();
2345                 if ( getDistFromEdge( face, eE[i]->_nodes[0], faceSize ))
2346                   needSmooth = needSmoothing( eE[i]->_cosin, tgtThick, faceSize );
2347               }
2348             }
2349         }
2350         // else
2351         // {
2352         //   const TopoDS_Face& F1 = TopoDS::Face( S );
2353         //   const TopoDS_Face& F2 = TopoDS::Face( eE[0]->_sWOL );
2354         //   const TopoDS_Edge& E  = TopoDS::Edge( eExp.Current() );
2355         //   for ( size_t i = 0; i < eE.size() && !needSmooth; ++i )
2356         //   {
2357         //     gp_Vec dir1 = getFaceDir( F1, E, eE[i]->_nodes[0], helper, ok );
2358         //     gp_Vec dir2 = getFaceDir( F2, E, eE[i]->_nodes[0], helper, ok );
2359         //     double angle = dir1.Angle(  );
2360         //     double cosin = cos( angle );
2361         //     needSmooth = ( cosin > theMinSmoothCosin );
2362         //   }
2363         // }
2364       }
2365       break;
2366     }
2367     case TopAbs_VERTEX:
2368       continue;
2369     default:;
2370     }
2371
2372     if ( needSmooth )
2373     {
2374       if ( S.ShapeType() == TopAbs_EDGE ) shapesToSmooth.push_front( iS );
2375       else                                shapesToSmooth.push_back ( iS );
2376     }
2377
2378   } // loop on edgesByGeom
2379
2380   data._edges.reserve( data._n2eMap.size() );
2381   data._endEdgeOnShape.clear();
2382
2383   // first we put _LayerEdge's on shapes to smooth
2384   data._nbShapesToSmooth = 0;
2385   list< TGeomID >::iterator gIt = shapesToSmooth.begin();
2386   for ( ; gIt != shapesToSmooth.end(); ++gIt )
2387   {
2388     vector<_LayerEdge*>& eVec = edgesByGeom[ *gIt ];
2389     if ( eVec.empty() ) continue;
2390     data._edges.insert( data._edges.end(), eVec.begin(), eVec.end() );
2391     data._endEdgeOnShape.push_back( data._edges.size() );
2392     data._nbShapesToSmooth++;
2393     eVec.clear();
2394   }
2395
2396   // then the rest _LayerEdge's
2397   for ( size_t iS = 0; iS < edgesByGeom.size(); ++iS )
2398   {
2399     vector<_LayerEdge*>& eVec = edgesByGeom[iS];
2400     if ( eVec.empty() ) continue;
2401     data._edges.insert( data._edges.end(), eVec.begin(), eVec.end() );
2402     data._endEdgeOnShape.push_back( data._edges.size() );
2403     //eVec.clear();
2404   }
2405
2406   // compute average StdMeshers_ViscousLayers parameters for each shape
2407
2408   data._hypOnShape.clear();
2409   if ( data._hyps.size() == 1 )
2410   {
2411     data._hypOnShape.resize( data._endEdgeOnShape.size(), AverageHyp( data._hyps.back() ));
2412   }
2413   else
2414   {
2415     data._hypOnShape.resize( data._endEdgeOnShape.size() );
2416     map< TGeomID, const StdMeshers_ViscousLayers* >::iterator f2hyp;
2417     for ( size_t i = 0; i < data._endEdgeOnShape.size(); ++i )
2418     {
2419       int       iEnd = data._endEdgeOnShape[i];
2420       _LayerEdge* LE = data._edges[ iEnd-1 ];
2421       TGeomID iShape = LE->_nodes[0]->getshapeId();
2422       const TopoDS_Shape& S = getMeshDS()->IndexToShape( iShape );
2423       if ( S.ShapeType() == TopAbs_FACE )
2424       {
2425         if (( f2hyp = data._face2hyp.find( iShape )) != data._face2hyp.end() )
2426         {
2427           data._hypOnShape[ i ].Add( f2hyp->second );
2428         }
2429       }
2430       else
2431       {
2432         PShapeIteratorPtr fIt = SMESH_MesherHelper::GetAncestors( S, *_mesh, TopAbs_FACE );
2433         while ( const TopoDS_Shape* face = fIt->next() )
2434         {
2435           TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
2436           if (( f2hyp = data._face2hyp.find( faceID )) != data._face2hyp.end() )
2437           {
2438             data._hypOnShape[ i ].Add( f2hyp->second );
2439           }
2440         }
2441       }
2442     }
2443   }
2444
2445   return ok;
2446 }
2447
2448 //================================================================================
2449 /*!
2450  * \brief Set data of _LayerEdge needed for smoothing
2451  *  \param subIds - ids of sub-shapes of a SOLID to take into account faces from
2452  */
2453 //================================================================================
2454
2455 bool _ViscousBuilder::setEdgeData(_LayerEdge&         edge,
2456                                   const set<TGeomID>& subIds,
2457                                   SMESH_MesherHelper& helper,
2458                                   _SolidData&         data)
2459 {
2460   SMESH_MeshEditor editor(_mesh);
2461
2462   const SMDS_MeshNode* node = edge._nodes[0]; // source node
2463   SMDS_TypeOfPosition posType = node->GetPosition()->GetTypeOfPosition();
2464
2465   edge._len       = 0;
2466   edge._2neibors  = 0;
2467   edge._curvature = 0;
2468
2469   // --------------------------
2470   // Compute _normal and _cosin
2471   // --------------------------
2472
2473   edge._cosin = 0;
2474   edge._normal.SetCoord(0,0,0);
2475
2476   int totalNbFaces = 0;
2477   gp_Vec geomNorm;
2478   bool normOK = true;
2479
2480   const TGeomID shapeInd = node->getshapeId();
2481   map< TGeomID, TopoDS_Shape >::const_iterator s2s = data._shrinkShape2Shape.find( shapeInd );
2482   const bool onShrinkShape ( s2s != data._shrinkShape2Shape.end() );
2483
2484   if ( onShrinkShape ) // one of faces the node is on has no layers
2485   {
2486     TopoDS_Shape vertEdge = getMeshDS()->IndexToShape( s2s->first ); // vertex or edge
2487     if ( s2s->second.ShapeType() == TopAbs_EDGE )
2488     {
2489       // inflate from VERTEX along EDGE
2490       edge._normal = getEdgeDir( TopoDS::Edge( s2s->second ), TopoDS::Vertex( vertEdge ));
2491     }
2492     else if ( vertEdge.ShapeType() == TopAbs_VERTEX )
2493     {
2494       // inflate from VERTEX along FACE
2495       edge._normal = getFaceDir( TopoDS::Face( s2s->second ), TopoDS::Vertex( vertEdge ),
2496                                  node, helper, normOK, &edge._cosin);
2497     }
2498     else
2499     {
2500       // inflate from EDGE along FACE
2501       edge._normal = getFaceDir( TopoDS::Face( s2s->second ), TopoDS::Edge( vertEdge ),
2502                                  node, helper, normOK);
2503     }
2504   }
2505   else // layers are on all faces of SOLID the node is on
2506   {
2507     // find indices of geom faces the node lies on
2508     set<TGeomID> faceIds;
2509     if  ( posType == SMDS_TOP_FACE )
2510     {
2511       faceIds.insert( node->getshapeId() );
2512     }
2513     else
2514     {
2515       SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
2516       while ( fIt->more() )
2517         faceIds.insert( editor.FindShape(fIt->next()));
2518     }
2519
2520     set<TGeomID>::iterator id = faceIds.begin();
2521     TopoDS_Face F;
2522     std::pair< TGeomID, gp_XYZ > id2Norm[20];
2523     for ( ; id != faceIds.end(); ++id )
2524     {
2525       const TopoDS_Shape& s = getMeshDS()->IndexToShape( *id );
2526       if ( s.IsNull() || s.ShapeType() != TopAbs_FACE || !subIds.count( *id ))
2527         continue;
2528       F = TopoDS::Face( s );
2529       geomNorm = getFaceNormal( node, F, helper, normOK );
2530       if ( !normOK ) continue;
2531
2532       if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
2533         geomNorm.Reverse();
2534       id2Norm[ totalNbFaces ].first  = *id;
2535       id2Norm[ totalNbFaces ].second = geomNorm.XYZ();
2536       totalNbFaces++;
2537       edge._normal += geomNorm.XYZ();
2538     }
2539     if ( totalNbFaces == 0 )
2540       return error(SMESH_Comment("Can't get normal to node ") << node->GetID(), data._index);
2541
2542     if ( normOK && edge._normal.Modulus() < 1e-3 && totalNbFaces > 1 )
2543     {
2544       // opposite normals, re-get normals at shifted positions (IPAL 52426)
2545       edge._normal.SetCoord( 0,0,0 );
2546       for ( int i = 0; i < totalNbFaces; ++i )
2547       {
2548         const TopoDS_Face& F = TopoDS::Face( getMeshDS()->IndexToShape( id2Norm[i].first ));
2549         geomNorm = getFaceNormal( node, F, helper, normOK, /*shiftInside=*/true );
2550         if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED )
2551           geomNorm.Reverse();
2552         if ( normOK )
2553           id2Norm[ i ].second = geomNorm.XYZ();
2554         edge._normal += id2Norm[ i ].second;
2555       }
2556     }
2557
2558     if ( totalNbFaces < 3 )
2559     {
2560       //edge._normal /= totalNbFaces;
2561     }
2562     else
2563     {
2564       edge._normal = getWeigthedNormal( node, id2Norm, totalNbFaces );
2565     }
2566
2567     switch ( posType )
2568     {
2569     case SMDS_TOP_FACE:
2570       edge._cosin = 0; break;
2571
2572     case SMDS_TOP_EDGE: {
2573       TopoDS_Edge E = TopoDS::Edge( helper.GetSubShapeByNode( node, getMeshDS()));
2574       gp_Vec inFaceDir = getFaceDir( F, E, node, helper, normOK );
2575       double angle = inFaceDir.Angle( edge._normal ); // [0,PI]
2576       edge._cosin = cos( angle );
2577       //cout << "Cosin on EDGE " << edge._cosin << " node " << node->GetID() << endl;
2578       break;
2579     }
2580     case SMDS_TOP_VERTEX: {
2581       TopoDS_Vertex V = TopoDS::Vertex( helper.GetSubShapeByNode( node, getMeshDS()));
2582       gp_Vec inFaceDir = getFaceDir( F, V, node, helper, normOK );
2583       double angle = inFaceDir.Angle( edge._normal ); // [0,PI]
2584       edge._cosin = cos( angle );
2585       //cout << "Cosin on VERTEX " << edge._cosin << " node " << node->GetID() << endl;
2586       break;
2587     }
2588     default:
2589       return error(SMESH_Comment("Invalid shape position of node ")<<node, data._index);
2590     }
2591   } // case _sWOL.IsNull()
2592
2593   double normSize = edge._normal.SquareModulus();
2594   if ( normSize < numeric_limits<double>::min() )
2595     return error(SMESH_Comment("Bad normal at node ")<< node->GetID(), data._index );
2596
2597   edge._normal /= sqrt( normSize );
2598
2599   // TODO: if ( !normOK ) then get normal by mesh faces
2600
2601   // Set the rest data
2602   // --------------------
2603   if ( onShrinkShape )
2604   {
2605     edge._sWOL = (*s2s).second;
2606
2607     SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edge._nodes.back() );
2608     if ( SMESHDS_SubMesh* sm = getMeshDS()->MeshElements( data._solid ))
2609       sm->RemoveNode( tgtNode , /*isNodeDeleted=*/false );
2610
2611     // set initial position which is parameters on _sWOL in this case
2612     if ( edge._sWOL.ShapeType() == TopAbs_EDGE )
2613     {
2614       double u = helper.GetNodeU( TopoDS::Edge( edge._sWOL ), node, 0, &normOK );
2615       edge._pos.push_back( gp_XYZ( u, 0, 0 ));
2616       if ( edge._nodes.size() > 1 )
2617         getMeshDS()->SetNodeOnEdge( tgtNode, TopoDS::Edge( edge._sWOL ), u );
2618     }
2619     else // TopAbs_FACE
2620     {
2621       gp_XY uv = helper.GetNodeUV( TopoDS::Face( edge._sWOL ), node, 0, &normOK );
2622       edge._pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
2623       if ( edge._nodes.size() > 1 )
2624         getMeshDS()->SetNodeOnFace( tgtNode, TopoDS::Face( edge._sWOL ), uv.X(), uv.Y() );
2625     }
2626   }
2627   else
2628   {
2629     edge._pos.push_back( SMESH_TNodeXYZ( node ));
2630
2631     if ( posType == SMDS_TOP_FACE )
2632     {
2633       getSimplices( node, edge._simplices, data._ignoreFaceIds, &data );
2634       double avgNormProj = 0, avgLen = 0;
2635       for ( size_t i = 0; i < edge._simplices.size(); ++i )
2636       {
2637         gp_XYZ vec = edge._pos.back() - SMESH_TNodeXYZ( edge._simplices[i]._nPrev );
2638         avgNormProj += edge._normal * vec;
2639         avgLen += vec.Modulus();
2640       }
2641       avgNormProj /= edge._simplices.size();
2642       avgLen /= edge._simplices.size();
2643       edge._curvature = _Curvature::New( avgNormProj, avgLen );
2644     }
2645   }
2646
2647   // Set neighbour nodes for a _LayerEdge based on EDGE
2648
2649   if ( posType == SMDS_TOP_EDGE /*||
2650        ( onShrinkShape && posType == SMDS_TOP_VERTEX && fabs( edge._cosin ) < 1e-10 )*/)
2651   {
2652     edge._2neibors = new _2NearEdges;
2653     // target node instead of source ones will be set later
2654     // if ( ! findNeiborsOnEdge( &edge,
2655     //                           edge._2neibors->_nodes[0],
2656     //                           edge._2neibors->_nodes[1],
2657     //                           data))
2658     //   return false;
2659     // edge.SetDataByNeighbors( edge._2neibors->_nodes[0],
2660     //                          edge._2neibors->_nodes[1],
2661     //                          helper);
2662   }
2663
2664   edge.SetCosin( edge._cosin ); // to update edge._lenFactor
2665
2666   return true;
2667 }
2668
2669 //================================================================================
2670 /*!
2671  * \brief Return normal to a FACE at a node
2672  *  \param [in] n - node
2673  *  \param [in] face - FACE
2674  *  \param [in] helper - helper
2675  *  \param [out] isOK - true or false
2676  *  \param [in] shiftInside - to find normal at a position shifted inside the face
2677  *  \return gp_XYZ - normal
2678  */
2679 //================================================================================
2680
2681 gp_XYZ _ViscousBuilder::getFaceNormal(const SMDS_MeshNode* node,
2682                                       const TopoDS_Face&   face,
2683                                       SMESH_MesherHelper&  helper,
2684                                       bool&                isOK,
2685                                       bool                 shiftInside)
2686 {
2687   gp_XY uv;
2688   if ( shiftInside )
2689   {
2690     // get a shifted position
2691     gp_Pnt p = SMESH_TNodeXYZ( node );
2692     gp_XYZ shift( 0,0,0 );
2693     TopoDS_Shape S = helper.GetSubShapeByNode( node, helper.GetMeshDS() );
2694     switch ( S.ShapeType() ) {
2695     case TopAbs_VERTEX:
2696     {
2697       shift = getFaceDir( face, TopoDS::Vertex( S ), node, helper, isOK );
2698       break;
2699     }
2700     case TopAbs_EDGE:
2701     {
2702       shift = getFaceDir( face, TopoDS::Edge( S ), node, helper, isOK );
2703       break;
2704     }
2705     default:
2706       isOK = false;
2707     }
2708     if ( isOK )
2709       shift.Normalize();
2710     p.Translate( shift * 1e-5 );
2711
2712     TopLoc_Location loc;
2713     GeomAPI_ProjectPointOnSurf& projector = helper.GetProjector( face, loc, 1e-7 );
2714
2715     if ( !loc.IsIdentity() ) p.Transform( loc.Transformation().Inverted() );
2716     
2717     projector.Perform( p );
2718     if ( !projector.IsDone() || projector.NbPoints() < 1 )
2719     {
2720       isOK = false;
2721       return p.XYZ();
2722     }
2723     Quantity_Parameter U,V;
2724     projector.LowerDistanceParameters(U,V);
2725     uv.SetCoord( U,V );
2726   }
2727   else
2728   {
2729     uv = helper.GetNodeUV( face, node, 0, &isOK );
2730   }
2731
2732   gp_Dir normal;
2733   isOK = false;
2734
2735   Handle(Geom_Surface) surface = BRep_Tool::Surface( face );
2736   int pointKind = GeomLib::NormEstim( surface, uv, 1e-5, normal );
2737   enum { REGULAR = 0, QUASYSINGULAR, CONICAL, IMPOSSIBLE };
2738
2739   if ( pointKind == IMPOSSIBLE &&
2740        node->GetPosition()->GetDim() == 2 ) // node inside the FACE
2741   {
2742     // probably NormEstim() failed due to a too high tolerance
2743     pointKind = GeomLib::NormEstim( surface, uv, 1e-20, normal );
2744     isOK = ( pointKind < IMPOSSIBLE );
2745   }
2746   if ( pointKind < IMPOSSIBLE )
2747   {
2748     if ( pointKind != REGULAR &&
2749          !shiftInside &&
2750          node->GetPosition()->GetDim() < 2 ) // FACE boundary
2751     {
2752       gp_XYZ normShift = getFaceNormal( node, face, helper, isOK, /*shiftInside=*/true );
2753       if ( normShift * normal.XYZ() < 0. )
2754         normal = normShift;
2755     }
2756     isOK = true;
2757   }
2758
2759   if ( !isOK ) // hard singularity, to call with shiftInside=true ?
2760   {
2761     const TGeomID faceID = helper.GetMeshDS()->ShapeToIndex( face );
2762
2763     SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
2764     while ( fIt->more() )
2765     {
2766       const SMDS_MeshElement* f = fIt->next();
2767       if ( f->getshapeId() == faceID )
2768       {
2769         isOK = SMESH_MeshAlgos::FaceNormal( f, (gp_XYZ&) normal.XYZ(), /*normalized=*/true );
2770         if ( isOK )
2771         {
2772           TopoDS_Face ff = face;
2773           ff.Orientation( TopAbs_FORWARD );
2774           if ( helper.IsReversedSubMesh( ff ))
2775             normal.Reverse();
2776           break;
2777         }
2778       }
2779     }
2780   }
2781   return normal.XYZ();
2782 }
2783
2784 //================================================================================
2785 /*!
2786  * \brief Return a normal at a node weighted with angles taken by FACEs
2787  *  \param [in] n - the node
2788  *  \param [in] fId2Normal - FACE ids and normals
2789  *  \param [in] nbFaces - nb of FACEs meeting at the node
2790  *  \return gp_XYZ - computed normal
2791  */
2792 //================================================================================
2793
2794 gp_XYZ _ViscousBuilder::getWeigthedNormal( const SMDS_MeshNode*         n,
2795                                            std::pair< TGeomID, gp_XYZ > fId2Normal[],
2796                                            const int                    nbFaces )
2797 {
2798   gp_XYZ resNorm(0,0,0);
2799   TopoDS_Shape V = SMESH_MesherHelper::GetSubShapeByNode( n, getMeshDS() );
2800   if ( V.ShapeType() != TopAbs_VERTEX )
2801   {
2802     for ( int i = 0; i < nbFaces; ++i )
2803       resNorm += fId2Normal[i].second / nbFaces ;
2804     return resNorm;
2805   }
2806
2807   double angles[30];
2808   for ( int i = 0; i < nbFaces; ++i )
2809   {
2810     const TopoDS_Face& F = TopoDS::Face( getMeshDS()->IndexToShape( fId2Normal[i].first ));
2811
2812     // look for two EDGEs shared by F and other FACEs within fId2Normal
2813     TopoDS_Edge ee[2];
2814     int nbE = 0;
2815     PShapeIteratorPtr eIt = SMESH_MesherHelper::GetAncestors( V, *_mesh, TopAbs_EDGE );
2816     while ( const TopoDS_Shape* E = eIt->next() )
2817     {
2818       if ( !SMESH_MesherHelper::IsSubShape( *E, F ))
2819         continue;
2820       bool isSharedEdge = false;
2821       for ( int j = 0; j < nbFaces && !isSharedEdge; ++j )
2822       {
2823         if ( i == j ) continue;
2824         const TopoDS_Shape& otherF = getMeshDS()->IndexToShape( fId2Normal[j].first );
2825         isSharedEdge = SMESH_MesherHelper::IsSubShape( *E, otherF );
2826       }
2827       if ( !isSharedEdge )
2828         continue;
2829       ee[ nbE ] = TopoDS::Edge( *E );
2830       ee[ nbE ].Orientation( SMESH_MesherHelper::GetSubShapeOri( F, *E ));
2831       if ( ++nbE == 2 )
2832         break;
2833     }
2834
2835     // get an angle between the two EDGEs
2836     angles[i] = 0;
2837     if ( nbE < 1 ) continue;
2838     if ( nbE == 1 )
2839     {
2840       ee[ 1 ] == ee[ 0 ];
2841     }
2842     else
2843     {
2844       if ( !V.IsSame( SMESH_MesherHelper::IthVertex( 0, ee[ 1 ] )))
2845         std::swap( ee[0], ee[1] );
2846     }
2847     angles[i] = SMESH_MesherHelper::GetAngle( ee[0], ee[1], F, TopoDS::Vertex( V ));
2848   }
2849
2850   // compute a weighted normal
2851   double sumAngle = 0;
2852   for ( int i = 0; i < nbFaces; ++i )
2853   {
2854     angles[i] = ( angles[i] > 2*M_PI )  ?  0  :  M_PI - angles[i];
2855     sumAngle += angles[i];
2856   }
2857   for ( int i = 0; i < nbFaces; ++i )
2858     resNorm += angles[i] / sumAngle * fId2Normal[i].second;
2859
2860   return resNorm;
2861 }
2862
2863 //================================================================================
2864 /*!
2865  * \brief Find 2 neigbor nodes of a node on EDGE
2866  */
2867 //================================================================================
2868
2869 bool _ViscousBuilder::findNeiborsOnEdge(const _LayerEdge*     edge,
2870                                         const SMDS_MeshNode*& n1,
2871                                         const SMDS_MeshNode*& n2,
2872                                         _SolidData&           data)
2873 {
2874   const SMDS_MeshNode* node = edge->_nodes[0];
2875   const int        shapeInd = node->getshapeId();
2876   SMESHDS_SubMesh*   edgeSM = 0;
2877   if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_EDGE )
2878   {
2879     edgeSM = getMeshDS()->MeshElements( shapeInd );
2880     if ( !edgeSM || edgeSM->NbElements() == 0 )
2881       return error(SMESH_Comment("Not meshed EDGE ") << shapeInd, data._index);
2882   }
2883   int iN = 0;
2884   n2 = 0;
2885   SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Edge);
2886   while ( eIt->more() && !n2 )
2887   {
2888     const SMDS_MeshElement* e = eIt->next();
2889     const SMDS_MeshNode*   nNeibor = e->GetNode( 0 );
2890     if ( nNeibor == node ) nNeibor = e->GetNode( 1 );
2891     if ( edgeSM )
2892     {
2893       if (!edgeSM->Contains(e)) continue;
2894     }
2895     else
2896     {
2897       TopoDS_Shape s = SMESH_MesherHelper::GetSubShapeByNode(nNeibor, getMeshDS() );
2898       if ( !SMESH_MesherHelper::IsSubShape( s, edge->_sWOL )) continue;
2899     }
2900     ( iN++ ? n2 : n1 ) = nNeibor;
2901   }
2902   if ( !n2 )
2903     return error(SMESH_Comment("Wrongly meshed EDGE ") << shapeInd, data._index);
2904   return true;
2905 }
2906
2907 //================================================================================
2908 /*!
2909  * \brief Set _curvature and _2neibors->_plnNorm by 2 neigbor nodes residing the same EDGE
2910  */
2911 //================================================================================
2912
2913 void _LayerEdge::SetDataByNeighbors( const SMDS_MeshNode* n1,
2914                                      const SMDS_MeshNode* n2,
2915                                      SMESH_MesherHelper&  helper)
2916 {
2917   if ( _nodes[0]->GetPosition()->GetTypeOfPosition() != SMDS_TOP_EDGE )
2918     return;
2919
2920   gp_XYZ pos = SMESH_TNodeXYZ( _nodes[0] );
2921   gp_XYZ vec1 = pos - SMESH_TNodeXYZ( n1 );
2922   gp_XYZ vec2 = pos - SMESH_TNodeXYZ( n2 );
2923
2924   // Set _curvature
2925
2926   double      sumLen = vec1.Modulus() + vec2.Modulus();
2927   _2neibors->_wgt[0] = 1 - vec1.Modulus() / sumLen;
2928   _2neibors->_wgt[1] = 1 - vec2.Modulus() / sumLen;
2929   double avgNormProj = 0.5 * ( _normal * vec1 + _normal * vec2 );
2930   double      avgLen = 0.5 * ( vec1.Modulus() + vec2.Modulus() );
2931   if ( _curvature ) delete _curvature;
2932   _curvature = _Curvature::New( avgNormProj, avgLen );
2933   // if ( _curvature )
2934   //   debugMsg( _nodes[0]->GetID()
2935   //             << " CURV r,k: " << _curvature->_r<<","<<_curvature->_k
2936   //             << " proj = "<<avgNormProj<< " len = " << avgLen << "| lenDelta(0) = "
2937   //             << _curvature->lenDelta(0) );
2938
2939   // Set _plnNorm
2940
2941   if ( _sWOL.IsNull() )
2942   {
2943     TopoDS_Shape S = helper.GetSubShapeByNode( _nodes[0], helper.GetMeshDS() );
2944     TopoDS_Edge  E = TopoDS::Edge( S );
2945     // if ( SMESH_Algo::isDegenerated( E ))
2946     //   return;
2947     gp_XYZ dirE    = getEdgeDir( E, _nodes[0], helper );
2948     gp_XYZ plnNorm = dirE ^ _normal;
2949     double proj0   = plnNorm * vec1;
2950     double proj1   = plnNorm * vec2;
2951     if ( fabs( proj0 ) > 1e-10 || fabs( proj1 ) > 1e-10 )
2952     {
2953       if ( _2neibors->_plnNorm ) delete _2neibors->_plnNorm;
2954       _2neibors->_plnNorm = new gp_XYZ( plnNorm.Normalized() );
2955     }
2956   }
2957 }
2958
2959 //================================================================================
2960 /*!
2961  * \brief Copy data from a _LayerEdge of other SOLID and based on the same node;
2962  * this and other _LayerEdge's are inflated along a FACE or an EDGE
2963  */
2964 //================================================================================
2965
2966 gp_XYZ _LayerEdge::Copy( _LayerEdge& other, SMESH_MesherHelper& helper )
2967 {
2968   _nodes     = other._nodes;
2969   _normal    = other._normal;
2970   _len       = 0;
2971   _lenFactor = other._lenFactor;
2972   _cosin     = other._cosin;
2973   _sWOL      = other._sWOL;
2974   _2neibors  = other._2neibors;
2975   _curvature = 0; std::swap( _curvature, other._curvature );
2976   _2neibors  = 0; std::swap( _2neibors,  other._2neibors );
2977
2978   gp_XYZ lastPos( 0,0,0 );
2979   if ( _sWOL.ShapeType() == TopAbs_EDGE )
2980   {
2981     double u = helper.GetNodeU( TopoDS::Edge( _sWOL ), _nodes[0] );
2982     _pos.push_back( gp_XYZ( u, 0, 0));
2983
2984     u = helper.GetNodeU( TopoDS::Edge( _sWOL ), _nodes.back() );
2985     lastPos.SetX( u );
2986   }
2987   else // TopAbs_FACE
2988   {
2989     gp_XY uv = helper.GetNodeUV( TopoDS::Face( _sWOL ), _nodes[0]);
2990     _pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
2991
2992     uv = helper.GetNodeUV( TopoDS::Face( _sWOL ), _nodes.back() );
2993     lastPos.SetX( uv.X() );
2994     lastPos.SetY( uv.Y() );
2995   }
2996   return lastPos;
2997 }
2998
2999 //================================================================================
3000 /*!
3001  * \brief Set _cosin and _lenFactor
3002  */
3003 //================================================================================
3004
3005 void _LayerEdge::SetCosin( double cosin )
3006 {
3007   _cosin = cosin;
3008   cosin = Abs( _cosin );
3009   _lenFactor = ( /*0.1 < cosin &&*/ cosin < 1-1e-12 ) ?  1./sqrt(1-cosin*cosin) : 1.0;
3010 }
3011
3012 //================================================================================
3013 /*!
3014  * \brief Fills a vector<_Simplex > 
3015  */
3016 //================================================================================
3017
3018 void _ViscousBuilder::getSimplices( const SMDS_MeshNode* node,
3019                                     vector<_Simplex>&    simplices,
3020                                     const set<TGeomID>&  ingnoreShapes,
3021                                     const _SolidData*    dataToCheckOri,
3022                                     const bool           toSort)
3023 {
3024   simplices.clear();
3025   SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
3026   while ( fIt->more() )
3027   {
3028     const SMDS_MeshElement* f = fIt->next();
3029     const TGeomID    shapeInd = f->getshapeId();
3030     if ( ingnoreShapes.count( shapeInd )) continue;
3031     const int nbNodes = f->NbCornerNodes();
3032     const int  srcInd = f->GetNodeIndex( node );
3033     const SMDS_MeshNode* nPrev = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd-1, nbNodes ));
3034     const SMDS_MeshNode* nNext = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+1, nbNodes ));
3035     const SMDS_MeshNode* nOpp  = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+2, nbNodes ));
3036     if ( dataToCheckOri && dataToCheckOri->_reversedFaceIds.count( shapeInd ))
3037       std::swap( nPrev, nNext );
3038     simplices.push_back( _Simplex( nPrev, nNext, nOpp ));
3039   }
3040
3041   if ( toSort )
3042   {
3043     vector<_Simplex> sortedSimplices( simplices.size() );
3044     sortedSimplices[0] = simplices[0];
3045     int nbFound = 0;
3046     for ( size_t i = 1; i < simplices.size(); ++i )
3047     {
3048       for ( size_t j = 1; j < simplices.size(); ++j )
3049         if ( sortedSimplices[i-1]._nNext == simplices[j]._nPrev )
3050         {
3051           sortedSimplices[i] = simplices[j];
3052           nbFound++;
3053           break;
3054         }
3055     }
3056     if ( nbFound == simplices.size() - 1 )
3057       simplices.swap( sortedSimplices );
3058   }
3059 }
3060
3061 //================================================================================
3062 /*!
3063  * \brief DEBUG. Create groups contating temorary data of _LayerEdge's
3064  */
3065 //================================================================================
3066
3067 void _ViscousBuilder::makeGroupOfLE()
3068 {
3069 #ifdef _DEBUG_
3070   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
3071   {
3072     if ( _sdVec[i]._edges.empty() ) continue;
3073
3074     dumpFunction( SMESH_Comment("make_LayerEdge_") << i );
3075     for ( size_t j = 0 ; j < _sdVec[i]._edges.size(); ++j )
3076     {
3077       _LayerEdge* le = _sdVec[i]._edges[j];
3078       for ( size_t iN = 1; iN < le->_nodes.size(); ++iN )
3079         dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <<le->_nodes[iN-1]->GetID()
3080                 << ", " << le->_nodes[iN]->GetID() <<"])");
3081     }
3082     dumpFunctionEnd();
3083
3084     dumpFunction( SMESH_Comment("makeNormals") << i );
3085     for ( size_t j = 0 ; j < _sdVec[i]._edges.size(); ++j )
3086     {
3087       _LayerEdge& edge = *_sdVec[i]._edges[j];
3088       SMESH_TNodeXYZ nXYZ( edge._nodes[0] );
3089       nXYZ += edge._normal * _sdVec[i]._stepSize;
3090       dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <<edge._nodes[0]->GetID()
3091               << ", mesh.AddNode( " << nXYZ.X()<<","<< nXYZ.Y()<<","<< nXYZ.Z()<<")])");
3092     }
3093     dumpFunctionEnd();
3094
3095     dumpFunction( SMESH_Comment("makeTmpFaces_") << i );
3096     TopExp_Explorer fExp( _sdVec[i]._solid, TopAbs_FACE );
3097     for ( ; fExp.More(); fExp.Next() )
3098     {
3099       if (const SMESHDS_SubMesh* sm = _sdVec[i]._proxyMesh->GetProxySubMesh( fExp.Current()))
3100       {
3101         SMDS_ElemIteratorPtr fIt = sm->GetElements();
3102         while ( fIt->more())
3103         {
3104           const SMDS_MeshElement* e = fIt->next();
3105           SMESH_Comment cmd("mesh.AddFace([");
3106           for ( int j=0; j < e->NbCornerNodes(); ++j )
3107             cmd << e->GetNode(j)->GetID() << (j+1<e->NbCornerNodes() ? ",": "])");
3108           dumpCmd( cmd );
3109         }
3110       }
3111     }
3112     dumpFunctionEnd();
3113   }
3114 #endif
3115 }
3116
3117 //================================================================================
3118 /*!
3119  * \brief Find maximal _LayerEdge length (layer thickness) limited by geometry
3120  */
3121 //================================================================================
3122
3123 void _ViscousBuilder::computeGeomSize( _SolidData& data )
3124 {
3125   data._geomSize = Precision::Infinite();
3126   double intersecDist;
3127   auto_ptr<SMESH_ElementSearcher> searcher
3128     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
3129                                            data._proxyMesh->GetFaces( data._solid )) );
3130
3131   TNode2Edge::iterator n2e = data._n2eMap.begin(), n2eEnd = data._n2eMap.end();
3132   for ( ; n2e != n2eEnd; ++n2e )
3133   {
3134     _LayerEdge* edge = n2e->second;
3135     if ( edge->IsOnEdge() ) continue;
3136     edge->FindIntersection( *searcher, intersecDist, data._epsilon );
3137     if ( data._geomSize > intersecDist && intersecDist > 0 )
3138       data._geomSize = intersecDist;
3139   }
3140 }
3141
3142 //================================================================================
3143 /*!
3144  * \brief Increase length of _LayerEdge's to reach the required thickness of layers
3145  */
3146 //================================================================================
3147
3148 bool _ViscousBuilder::inflate(_SolidData& data)
3149 {
3150   SMESH_MesherHelper helper( *_mesh );
3151
3152   // Limit inflation step size by geometry size found by itersecting
3153   // normals of _LayerEdge's with mesh faces
3154   if ( data._stepSize > 0.3 * data._geomSize )
3155     limitStepSize( data, 0.3 * data._geomSize );
3156
3157   const double tgtThick = data._maxThickness;
3158   if ( data._stepSize > data._minThickness )
3159     limitStepSize( data, data._minThickness );
3160
3161   if ( data._stepSize < 1. )
3162     data._epsilon = data._stepSize * 1e-7;
3163
3164   debugMsg( "-- geomSize = " << data._geomSize << ", stepSize = " << data._stepSize );
3165
3166   double avgThick = 0, curThick = 0, distToIntersection = Precision::Infinite();
3167   int nbSteps = 0, nbRepeats = 0;
3168   int iBeg, iEnd, iS;
3169   while ( avgThick < 0.99 )
3170   {
3171     // new target length
3172     curThick += data._stepSize;
3173     if ( curThick > tgtThick )
3174     {
3175       curThick = tgtThick + tgtThick*( 1.-avgThick ) * nbRepeats;
3176       nbRepeats++;
3177     }
3178
3179     // Elongate _LayerEdge's
3180     dumpFunction(SMESH_Comment("inflate")<<data._index<<"_step"<<nbSteps); // debug
3181     for ( iBeg = 0, iS = 0; iS < data._endEdgeOnShape.size(); ++iS )
3182     {
3183       const double shapeCurThick = Min( curThick, data._hypOnShape[ iS ].GetTotalThickness() );
3184       for ( iEnd = data._endEdgeOnShape[ iS ]; iBeg < iEnd; ++iBeg )
3185       {
3186         data._edges[iBeg]->SetNewLength( shapeCurThick, helper );
3187       }
3188     }
3189     dumpFunctionEnd();
3190
3191     if ( !updateNormals( data, helper, nbSteps ))
3192       return false;
3193
3194     // Improve and check quality
3195     if ( !smoothAndCheck( data, nbSteps, distToIntersection ))
3196     {
3197       if ( nbSteps > 0 )
3198       {
3199         dumpFunction(SMESH_Comment("invalidate")<<data._index<<"_step"<<nbSteps); // debug
3200         for ( size_t i = 0; i < data._edges.size(); ++i )
3201         {
3202           data._edges[i]->InvalidateStep( nbSteps+1 );
3203         }
3204         dumpFunctionEnd();
3205       }
3206       break; // no more inflating possible
3207     }
3208     nbSteps++;
3209
3210     // Evaluate achieved thickness
3211     avgThick = 0;
3212     for ( iBeg = 0, iS = 0; iS < data._endEdgeOnShape.size(); ++iS )
3213     {
3214       const double shapeTgtThick = data._hypOnShape[ iS ].GetTotalThickness();
3215       for ( iEnd = data._endEdgeOnShape[ iS ]; iBeg < iEnd; ++iBeg )
3216       {
3217         avgThick += Min( 1., data._edges[iBeg]->_len / shapeTgtThick );
3218       }
3219     }
3220     avgThick /= data._edges.size();
3221     debugMsg( "-- Thickness " << curThick << " ("<< avgThick*100 << "%) reached" );
3222
3223     if ( distToIntersection < tgtThick*avgThick*1.5 )
3224     {
3225       debugMsg( "-- Stop inflation since "
3226                 << " distToIntersection( "<<distToIntersection<<" ) < avgThick( "
3227                 << tgtThick*avgThick << " ) * 1.5" );
3228       break;
3229     }
3230     // new step size
3231     limitStepSize( data, 0.25 * distToIntersection );
3232     if ( data._stepSizeNodes[0] )
3233       data._stepSize = data._stepSizeCoeff *
3234         SMESH_TNodeXYZ(data._stepSizeNodes[0]).Distance(data._stepSizeNodes[1]);
3235
3236   } // while ( avgThick < 0.99 )
3237
3238   if (nbSteps == 0 )
3239     return error("failed at the very first inflation step", data._index);
3240
3241   if ( avgThick < 0.99 )
3242     if ( SMESH_subMesh* sm = _mesh->GetSubMeshContaining( data._index ))
3243     {
3244       SMESH_ComputeErrorPtr& smError = sm->GetComputeError();
3245       if ( !smError || smError->IsOK() )
3246         smError.reset
3247           ( new SMESH_ComputeError (COMPERR_WARNING,
3248                                     SMESH_Comment("Thickness ") << tgtThick <<
3249                                     " of viscous layers not reached,"
3250                                     " average reached thickness is " << avgThick*tgtThick));
3251     }
3252
3253
3254   // Restore position of src nodes moved by infaltion on _noShrinkShapes
3255   dumpFunction(SMESH_Comment("restoNoShrink_So")<<data._index); // debug
3256   for ( iEnd = iS = 0; iS < data._endEdgeOnShape.size(); ++iS )
3257   {
3258     iBeg = iEnd;
3259     iEnd = data._endEdgeOnShape[ iS ];
3260     if ( data._edges[ iBeg ]->_nodes.size() == 1 )
3261       for ( ; iBeg < iEnd; ++iBeg )
3262       {
3263         restoreNoShrink( *data._edges[ iBeg ] );
3264       }
3265   }
3266   dumpFunctionEnd();
3267
3268   return true;
3269 }
3270
3271 //================================================================================
3272 /*!
3273  * \brief Improve quality of layer inner surface and check intersection
3274  */
3275 //================================================================================
3276
3277 bool _ViscousBuilder::smoothAndCheck(_SolidData& data,
3278                                      const int   nbSteps,
3279                                      double &    distToIntersection)
3280 {
3281   if ( data._nbShapesToSmooth == 0 )
3282     return true; // no shapes needing smoothing
3283
3284   bool moved, improved;
3285
3286   SMESH_MesherHelper helper(*_mesh);
3287   Handle(Geom_Surface) surface;
3288   TopoDS_Face F;
3289
3290   int iBeg, iEnd = 0;
3291   for ( int iS = 0; iS < data._nbShapesToSmooth; ++iS )
3292   {
3293     iBeg = iEnd;
3294     iEnd = data._endEdgeOnShape[ iS ];
3295
3296     // bool toSmooth = false;
3297     // for ( int i = iBeg; i < iEnd; ++i )
3298     //   toSmooth = data._edges[ iBeg ]->NbSteps() >= nbSteps+1;
3299     // if ( !toSmooth )
3300     // {
3301     //   if ( iS+1 == data._nbShapesToSmooth )
3302     //     data._nbShapesToSmooth--;
3303     //   continue; // target length reached some steps before
3304     // }
3305
3306     if ( !data._edges[ iBeg ]->_sWOL.IsNull() &&
3307          data._edges[ iBeg ]->_sWOL.ShapeType() == TopAbs_FACE )
3308     {
3309       if ( !F.IsSame( data._edges[ iBeg ]->_sWOL )) {
3310         F = TopoDS::Face( data._edges[ iBeg ]->_sWOL );
3311         helper.SetSubShape( F );
3312         surface = BRep_Tool::Surface( F );
3313       }
3314     }
3315     else
3316     {
3317       F.Nullify(); surface.Nullify();
3318     }
3319     TGeomID sInd = data._edges[ iBeg ]->_nodes[0]->getshapeId();
3320
3321     if ( data._edges[ iBeg ]->IsOnEdge() )
3322     { 
3323       dumpFunction(SMESH_Comment("smooth")<<data._index << "_Ed"<<sInd <<"_InfStep"<<nbSteps);
3324
3325       // try a simple solution on an analytic EDGE
3326       if ( !smoothAnalyticEdge( data, iBeg, iEnd, surface, F, helper ))
3327       {
3328         // smooth on EDGE's
3329         int step = 0;
3330         do {
3331           moved = false;
3332           for ( int i = iBeg; i < iEnd; ++i )
3333           {
3334             moved |= data._edges[i]->SmoothOnEdge(surface, F, helper);
3335           }
3336           dumpCmd( SMESH_Comment("# end step ")<<step);
3337         }
3338         while ( moved && step++ < 5 );
3339       }
3340       dumpFunctionEnd();
3341     }
3342     else
3343     {
3344       // smooth on FACE's
3345       int step = 0, stepLimit = 5, badNb = 0; moved = true;
3346       while (( ++step <= stepLimit && moved ) || improved )
3347       {
3348         dumpFunction(SMESH_Comment("smooth")<<data._index<<"_Fa"<<sInd
3349                      <<"_InfStep"<<nbSteps<<"_"<<step); // debug
3350         int oldBadNb = badNb;
3351         badNb = 0;
3352         moved = false;
3353         if ( step % 2 )
3354           for ( int i = iBeg; i < iEnd; ++i ) // iterate forward
3355             moved |= data._edges[i]->Smooth(badNb);
3356         else
3357           for ( int i = iEnd-1; i >= iBeg; --i ) // iterate backward
3358             moved |= data._edges[i]->Smooth(badNb);
3359         improved = ( badNb < oldBadNb );
3360
3361         // issue 22576 -- no bad faces but still there are intersections to fix
3362         if ( improved && badNb == 0 )
3363           stepLimit = step + 3;
3364
3365         dumpFunctionEnd();
3366       }
3367       if ( badNb > 0 )
3368       {
3369 #ifdef __myDEBUG
3370         for ( int i = iBeg; i < iEnd; ++i )
3371         {
3372           _LayerEdge* edge = data._edges[i];
3373           SMESH_TNodeXYZ tgtXYZ( edge->_nodes.back() );
3374           for ( size_t j = 0; j < edge->_simplices.size(); ++j )
3375             if ( !edge->_simplices[j].IsForward( edge->_nodes[0], &tgtXYZ ))
3376             {
3377               cout << "Bad simplex ( " << edge->_nodes[0]->GetID()<< " "<< tgtXYZ._node->GetID()
3378                    << " "<< edge->_simplices[j]._nPrev->GetID()
3379                    << " "<< edge->_simplices[j]._nNext->GetID() << " )" << endl;
3380               return false;
3381             }
3382         }
3383 #endif
3384         return false;
3385       }
3386     }
3387   } // loop on shapes to smooth
3388
3389   // Check orientation of simplices of _ConvexFace::_simplexTestEdges
3390   map< TGeomID, _ConvexFace >::iterator id2face = data._convexFaces.begin();
3391   for ( ; id2face != data._convexFaces.end(); ++id2face )
3392   {
3393     _ConvexFace & convFace = (*id2face).second;
3394     if ( !convFace._simplexTestEdges.empty() &&
3395          convFace._simplexTestEdges[0]->_nodes[0]->GetPosition()->GetDim() == 2 )
3396       continue; // _simplexTestEdges are based on FACE -- already checked while smoothing
3397
3398     if ( !convFace.CheckPrisms() )
3399       return false;
3400   }
3401
3402   // Check if the last segments of _LayerEdge intersects 2D elements;
3403   // checked elements are either temporary faces or faces on surfaces w/o the layers
3404
3405   auto_ptr<SMESH_ElementSearcher> searcher
3406     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
3407                                            data._proxyMesh->GetFaces( data._solid )) );
3408
3409   distToIntersection = Precision::Infinite();
3410   double dist;
3411   const SMDS_MeshElement* intFace = 0;
3412   const SMDS_MeshElement* closestFace = 0;
3413   int iLE = 0;
3414   for ( size_t i = 0; i < data._edges.size(); ++i )
3415   {
3416     if ( !data._edges[i]->_sWOL.IsNull() )
3417       continue;
3418     if ( data._edges[i]->FindIntersection( *searcher, dist, data._epsilon, &intFace ))
3419       return false;
3420     if ( distToIntersection > dist )
3421     {
3422       // ignore intersection of a _LayerEdge based on a _ConvexFace with a face
3423       // lying on this _ConvexFace
3424       if ( _ConvexFace* convFace = data.GetConvexFace( intFace->getshapeId() ))
3425         if ( convFace->_subIdToEdgeEnd.count ( data._edges[i]->_nodes[0]->getshapeId() ))
3426           continue;
3427
3428       distToIntersection = dist;
3429       iLE = i;
3430       closestFace = intFace;
3431     }
3432   }
3433 #ifdef __myDEBUG
3434   if ( closestFace )
3435   {
3436     SMDS_MeshElement::iterator nIt = closestFace->begin_nodes();
3437     cout << "Shortest distance: _LayerEdge nodes: tgt " << data._edges[iLE]->_nodes.back()->GetID()
3438          << " src " << data._edges[iLE]->_nodes[0]->GetID()<< ", intersection with face ("
3439          << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()
3440          << ") distance = " << distToIntersection<< endl;
3441   }
3442 #endif
3443
3444   return true;
3445 }
3446
3447 //================================================================================
3448 /*!
3449  * \brief Return a curve of the EDGE to be used for smoothing and arrange
3450  *        _LayerEdge's to be in a consequent order
3451  */
3452 //================================================================================
3453
3454 Handle(Geom_Curve) _SolidData::CurveForSmooth( const TopoDS_Edge&    E,
3455                                                const int             iFrom,
3456                                                const int             iTo,
3457                                                Handle(Geom_Surface)& surface,
3458                                                const TopoDS_Face&    F,
3459                                                SMESH_MesherHelper&   helper)
3460 {
3461   TGeomID eIndex = helper.GetMeshDS()->ShapeToIndex( E );
3462
3463   map< TGeomID, Handle(Geom_Curve)>::iterator i2curve = _edge2curve.find( eIndex );
3464
3465   if ( i2curve == _edge2curve.end() )
3466   {
3467     // sort _LayerEdge's by position on the EDGE
3468     SortOnEdge( E, iFrom, iTo, helper );
3469
3470     SMESHDS_SubMesh* smDS = helper.GetMeshDS()->MeshElements( eIndex );
3471
3472     TopLoc_Location loc; double f,l;
3473
3474     Handle(Geom_Line)   line;
3475     Handle(Geom_Circle) circle;
3476     bool isLine, isCirc;
3477     if ( F.IsNull() ) // 3D case
3478     {
3479       // check if the EDGE is a line
3480       Handle(Geom_Curve) curve = BRep_Tool::Curve( E, loc, f, l);
3481       if ( curve->IsKind( STANDARD_TYPE( Geom_TrimmedCurve )))
3482         curve = Handle(Geom_TrimmedCurve)::DownCast( curve )->BasisCurve();
3483
3484       line   = Handle(Geom_Line)::DownCast( curve );
3485       circle = Handle(Geom_Circle)::DownCast( curve );
3486       isLine = (!line.IsNull());
3487       isCirc = (!circle.IsNull());
3488
3489       if ( !isLine && !isCirc ) // Check if the EDGE is close to a line
3490       {
3491         Bnd_B3d bndBox;
3492         SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
3493         while ( nIt->more() )
3494           bndBox.Add( SMESH_TNodeXYZ( nIt->next() ));
3495         gp_XYZ size = bndBox.CornerMax() - bndBox.CornerMin();
3496
3497         SMESH_TNodeXYZ p0( _edges[iFrom]->_2neibors->tgtNode(0) );
3498         SMESH_TNodeXYZ p1( _edges[iFrom]->_2neibors->tgtNode(1) );
3499         const double lineTol = 1e-2 * ( p0 - p1 ).Modulus();
3500         for ( int i = 0; i < 3 && !isLine; ++i )
3501           isLine = ( size.Coord( i+1 ) <= lineTol );
3502       }
3503       if ( !isLine && !isCirc && iTo-iFrom > 2) // Check if the EDGE is close to a circle
3504       {
3505         // TODO
3506       }
3507     }
3508     else // 2D case
3509     {
3510       // check if the EDGE is a line
3511       Handle(Geom2d_Curve) curve = BRep_Tool::CurveOnSurface( E, F, f, l);
3512       if ( curve->IsKind( STANDARD_TYPE( Geom2d_TrimmedCurve )))
3513         curve = Handle(Geom2d_TrimmedCurve)::DownCast( curve )->BasisCurve();
3514
3515       Handle(Geom2d_Line)   line2d   = Handle(Geom2d_Line)::DownCast( curve );
3516       Handle(Geom2d_Circle) circle2d = Handle(Geom2d_Circle)::DownCast( curve );
3517       isLine = (!line2d.IsNull());
3518       isCirc = (!circle2d.IsNull());
3519
3520       if ( !isLine && !isCirc) // Check if the EDGE is close to a line
3521       {
3522         Bnd_B2d bndBox;
3523         SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
3524         while ( nIt->more() )
3525           bndBox.Add( helper.GetNodeUV( F, nIt->next() ));
3526         gp_XY size = bndBox.CornerMax() - bndBox.CornerMin();
3527
3528         const double lineTol = 1e-2 * sqrt( bndBox.SquareExtent() );
3529         for ( int i = 0; i < 2 && !isLine; ++i )
3530           isLine = ( size.Coord( i+1 ) <= lineTol );
3531       }
3532       if ( !isLine && !isCirc && iTo-iFrom > 2) // Check if the EDGE is close to a circle
3533       {
3534         // TODO
3535       }
3536       if ( isLine )
3537       {
3538         line = new Geom_Line( gp::OX() ); // only type does matter
3539       }
3540       else if ( isCirc )
3541       {
3542         gp_Pnt2d p = circle2d->Location();
3543         gp_Ax2 ax( gp_Pnt( p.X(), p.Y(), 0), gp::DX());
3544         circle = new Geom_Circle( ax, 1.); // only center position does matter
3545       }
3546     }
3547
3548     Handle(Geom_Curve)& res = _edge2curve[ eIndex ];
3549     if ( isLine )
3550       res = line;
3551     else if ( isCirc )
3552       res = circle;
3553
3554     return res;
3555   }
3556   return i2curve->second;
3557 }
3558
3559 //================================================================================
3560 /*!
3561  * \brief Sort _LayerEdge's by a parameter on a given EDGE
3562  */
3563 //================================================================================
3564
3565 void _SolidData::SortOnEdge( const TopoDS_Edge&  E,
3566                              const int           iFrom,
3567                              const int           iTo,
3568                              SMESH_MesherHelper& helper)
3569 {
3570   map< double, _LayerEdge* > u2edge;
3571   for ( int i = iFrom; i < iTo; ++i )
3572     u2edge.insert( make_pair( helper.GetNodeU( E, _edges[i]->_nodes[0] ), _edges[i] ));
3573
3574   ASSERT( u2edge.size() == iTo - iFrom );
3575   map< double, _LayerEdge* >::iterator u2e = u2edge.begin();
3576   for ( int i = iFrom; i < iTo; ++i, ++u2e )
3577     _edges[i] = u2e->second;
3578
3579   // set _2neibors according to the new order
3580   for ( int i = iFrom; i < iTo-1; ++i )
3581     if ( _edges[i]->_2neibors->tgtNode(1) != _edges[i+1]->_nodes.back() )
3582       _edges[i]->_2neibors->reverse();
3583   if ( u2edge.size() > 1 &&
3584        _edges[iTo-1]->_2neibors->tgtNode(0) != _edges[iTo-2]->_nodes.back() )
3585     _edges[iTo-1]->_2neibors->reverse();
3586 }
3587
3588 //================================================================================
3589 /*!
3590  * \brief Return index corresponding to the shape in _endEdgeOnShape
3591  */
3592 //================================================================================
3593
3594 bool _SolidData::GetShapeEdges(const TGeomID shapeID,
3595                                size_t &      iEdgesEnd,
3596                                int*          iBeg,
3597                                int*          iEnd ) const
3598 {
3599   int beg = 0, end = 0;
3600   for ( iEdgesEnd = 0; iEdgesEnd < _endEdgeOnShape.size(); ++iEdgesEnd )
3601   {
3602     end = _endEdgeOnShape[ iEdgesEnd ];
3603     TGeomID sID = _edges[ beg ]->_nodes[0]->getshapeId();
3604     if ( sID == shapeID )
3605     {
3606       if ( iBeg ) *iBeg = beg;
3607       if ( iEnd ) *iEnd = end;
3608       return true;
3609     }
3610     beg = end;
3611   }
3612   return false;
3613 }
3614
3615 //================================================================================
3616 /*!
3617  * \brief Add faces for smoothing
3618  */
3619 //================================================================================
3620
3621 void _SolidData::AddShapesToSmooth( const set< TGeomID >& faceIDs )
3622 {
3623   // convert faceIDs to indices in _endEdgeOnShape
3624   set< size_t > iEnds;
3625   size_t end;
3626   set< TGeomID >::const_iterator fId = faceIDs.begin();
3627   for ( ; fId != faceIDs.end(); ++fId )
3628     if ( GetShapeEdges( *fId, end ) && end >= _nbShapesToSmooth )
3629       iEnds.insert( end );
3630
3631   set< size_t >::iterator endsIt = iEnds.begin();
3632
3633   // "add" by move of _nbShapesToSmooth
3634   int nbFacesToAdd = iEnds.size();
3635   while ( endsIt != iEnds.end() && *endsIt == _nbShapesToSmooth )
3636   {
3637     ++endsIt;
3638     ++_nbShapesToSmooth;
3639     --nbFacesToAdd;
3640   }
3641   if ( endsIt == iEnds.end() )
3642     return;
3643
3644   // Move _LayerEdge's on FACEs just after _nbShapesToSmooth
3645
3646   vector< _LayerEdge* > nonSmoothLE, smoothLE;
3647   size_t lastSmooth = *iEnds.rbegin();
3648   int iBeg, iEnd;
3649   for ( size_t i = _nbShapesToSmooth; i <= lastSmooth; ++i )
3650   {
3651     vector< _LayerEdge* > & edgesVec = iEnds.count(i) ? smoothLE : nonSmoothLE;
3652     iBeg = i ? _endEdgeOnShape[ i-1 ] : 0;
3653     iEnd = _endEdgeOnShape[ i ];
3654     edgesVec.insert( edgesVec.end(), _edges.begin() + iBeg, _edges.begin() + iEnd ); 
3655   }
3656
3657   iBeg = _nbShapesToSmooth ? _endEdgeOnShape[ _nbShapesToSmooth-1 ] : 0;
3658   std::copy( smoothLE.begin(),    smoothLE.end(),    &_edges[ iBeg ] );
3659   std::copy( nonSmoothLE.begin(), nonSmoothLE.end(), &_edges[ iBeg + smoothLE.size()]);
3660
3661   // update _endEdgeOnShape
3662   for ( size_t i = _nbShapesToSmooth; i < _endEdgeOnShape.size(); ++i )
3663   {
3664     TGeomID curShape = _edges[ iBeg ]->_nodes[0]->getshapeId();
3665     while ( ++iBeg < _edges.size() &&
3666             curShape == _edges[ iBeg ]->_nodes[0]->getshapeId() );
3667
3668     _endEdgeOnShape[ i ] = iBeg;
3669   }
3670
3671   _nbShapesToSmooth += nbFacesToAdd;
3672 }
3673
3674 //================================================================================
3675 /*!
3676  * \brief smooth _LayerEdge's on a staight EDGE or circular EDGE
3677  */
3678 //================================================================================
3679
3680 bool _ViscousBuilder::smoothAnalyticEdge( _SolidData&           data,
3681                                           const int             iFrom,
3682                                           const int             iTo,
3683                                           Handle(Geom_Surface)& surface,
3684                                           const TopoDS_Face&    F,
3685                                           SMESH_MesherHelper&   helper)
3686 {
3687   TopoDS_Shape S = helper.GetSubShapeByNode( data._edges[ iFrom ]->_nodes[0],
3688                                              helper.GetMeshDS());
3689   TopoDS_Edge E = TopoDS::Edge( S );
3690
3691   Handle(Geom_Curve) curve = data.CurveForSmooth( E, iFrom, iTo, surface, F, helper );
3692   if ( curve.IsNull() ) return false;
3693
3694   // compute a relative length of segments
3695   vector< double > len( iTo-iFrom+1 );
3696   {
3697     double curLen, prevLen = len[0] = 1.0;
3698     for ( int i = iFrom; i < iTo; ++i )
3699     {
3700       curLen = prevLen * data._edges[i]->_2neibors->_wgt[0] / data._edges[i]->_2neibors->_wgt[1];
3701       len[i-iFrom+1] = len[i-iFrom] + curLen;
3702       prevLen = curLen;
3703     }
3704   }
3705
3706   if ( curve->IsKind( STANDARD_TYPE( Geom_Line )))
3707   {
3708     if ( F.IsNull() ) // 3D
3709     {
3710       SMESH_TNodeXYZ p0( data._edges[iFrom]->_2neibors->tgtNode(0));
3711       SMESH_TNodeXYZ p1( data._edges[iTo-1]->_2neibors->tgtNode(1));
3712       for ( int i = iFrom; i < iTo; ++i )
3713       {
3714         double r = len[i-iFrom] / len.back();
3715         gp_XYZ newPos = p0 * ( 1. - r ) + p1 * r;
3716         data._edges[i]->_pos.back() = newPos;
3717         SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( data._edges[i]->_nodes.back() );
3718         tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
3719         dumpMove( tgtNode );
3720       }
3721     }
3722     else
3723     {
3724       // gp_XY uv0 = helper.GetNodeUV( F, data._edges[iFrom]->_2neibors->tgtNode(0));
3725       // gp_XY uv1 = helper.GetNodeUV( F, data._edges[iTo-1]->_2neibors->tgtNode(1));
3726       gp_XY uv0 = data._edges[iFrom]->_2neibors->_edges[0]->LastUV( F );
3727       gp_XY uv1 = data._edges[iTo-1]->_2neibors->_edges[1]->LastUV( F );
3728       if ( data._edges[iFrom]->_2neibors->tgtNode(0) ==
3729            data._edges[iTo-1]->_2neibors->tgtNode(1) ) // closed edge
3730       {
3731         int iPeriodic = helper.GetPeriodicIndex();
3732         if ( iPeriodic == 1 || iPeriodic == 2 )
3733         {
3734           uv1.SetCoord( iPeriodic, helper.GetOtherParam( uv1.Coord( iPeriodic )));
3735           if ( uv0.Coord( iPeriodic ) > uv1.Coord( iPeriodic ))
3736             std::swap( uv0, uv1 );
3737         }
3738       }
3739       const gp_XY rangeUV = uv1 - uv0;
3740       for ( int i = iFrom; i < iTo; ++i )
3741       {
3742         double r = len[i-iFrom] / len.back();
3743         gp_XY newUV = uv0 + r * rangeUV;
3744         data._edges[i]->_pos.back().SetCoord( newUV.X(), newUV.Y(), 0 );
3745
3746         gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() );
3747         SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( data._edges[i]->_nodes.back() );
3748         tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
3749         dumpMove( tgtNode );
3750
3751         SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( tgtNode->GetPosition() );
3752         pos->SetUParameter( newUV.X() );
3753         pos->SetVParameter( newUV.Y() );
3754       }
3755     }
3756     return true;
3757   }
3758
3759   if ( curve->IsKind( STANDARD_TYPE( Geom_Circle )))
3760   {
3761     Handle(Geom_Circle) circle = Handle(Geom_Circle)::DownCast( curve );
3762     gp_Pnt center3D = circle->Location();
3763
3764     if ( F.IsNull() ) // 3D
3765     {
3766       if ( data._edges[iFrom]->_2neibors->tgtNode(0) ==
3767            data._edges[iTo-1]->_2neibors->tgtNode(1) )
3768         return true; // closed EDGE - nothing to do
3769
3770       return false; // TODO ???
3771     }
3772     else // 2D
3773     {
3774       const gp_XY center( center3D.X(), center3D.Y() );
3775
3776       gp_XY uv0 = data._edges[iFrom]->_2neibors->_edges[0]->LastUV( F );
3777       gp_XY uvM = data._edges[iFrom]->LastUV( F );
3778       gp_XY uv1 = data._edges[iTo-1]->_2neibors->_edges[1]->LastUV( F );
3779       // gp_XY uv0 = helper.GetNodeUV( F, data._edges[iFrom]->_2neibors->tgtNode(0));
3780       // gp_XY uvM = helper.GetNodeUV( F, data._edges[iFrom]->_nodes.back());
3781       // gp_XY uv1 = helper.GetNodeUV( F, data._edges[iTo-1]->_2neibors->tgtNode(1));
3782       gp_Vec2d vec0( center, uv0 );
3783       gp_Vec2d vecM( center, uvM );
3784       gp_Vec2d vec1( center, uv1 );
3785       double uLast = vec0.Angle( vec1 ); // -PI - +PI
3786       double uMidl = vec0.Angle( vecM );
3787       if ( uLast * uMidl <= 0. )
3788         uLast += ( uMidl > 0 ? +2. : -2. ) * M_PI;
3789       const double radius = 0.5 * ( vec0.Magnitude() + vec1.Magnitude() );
3790
3791       gp_Ax2d   axis( center, vec0 );
3792       gp_Circ2d circ( axis, radius );
3793       for ( int i = iFrom; i < iTo; ++i )
3794       {
3795         double    newU = uLast * len[i-iFrom] / len.back();
3796         gp_Pnt2d newUV = ElCLib::Value( newU, circ );
3797         data._edges[i]->_pos.back().SetCoord( newUV.X(), newUV.Y(), 0 );
3798
3799         gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() );
3800         SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( data._edges[i]->_nodes.back() );
3801         tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
3802         dumpMove( tgtNode );
3803
3804         SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( tgtNode->GetPosition() );
3805         pos->SetUParameter( newUV.X() );
3806         pos->SetVParameter( newUV.Y() );
3807       }
3808     }
3809     return true;
3810   }
3811
3812   return false;
3813 }
3814
3815 //================================================================================
3816 /*!
3817  * \brief Modify normals of _LayerEdge's on EDGE's to avoid intersection with
3818  * _LayerEdge's on neighbor EDGE's
3819  */
3820 //================================================================================
3821
3822 bool _ViscousBuilder::updateNormals( _SolidData&         data,
3823                                      SMESH_MesherHelper& helper,
3824                                      int                 stepNb )
3825 {
3826   if ( stepNb > 0 )
3827     return updateNormalsOfConvexFaces( data, helper, stepNb );
3828
3829   // make temporary quadrangles got by extrusion of
3830   // mesh edges along _LayerEdge._normal's
3831
3832   vector< const SMDS_MeshElement* > tmpFaces;
3833   {
3834     set< SMESH_TLink > extrudedLinks; // contains target nodes
3835     vector< const SMDS_MeshNode*> nodes(4); // of a tmp mesh face
3836
3837     dumpFunction(SMESH_Comment("makeTmpFacesOnEdges")<<data._index);
3838     for ( size_t i = 0; i < data._edges.size(); ++i )
3839     {
3840       _LayerEdge* edge = data._edges[i];
3841       if ( !edge->IsOnEdge() || !edge->_sWOL.IsNull() ) continue;
3842       const SMDS_MeshNode* tgt1 = edge->_nodes.back();
3843       for ( int j = 0; j < 2; ++j ) // loop on _2NearEdges
3844       {
3845         const SMDS_MeshNode* tgt2 = edge->_2neibors->tgtNode(j);
3846         pair< set< SMESH_TLink >::iterator, bool > link_isnew =
3847           extrudedLinks.insert( SMESH_TLink( tgt1, tgt2 ));
3848         if ( !link_isnew.second )
3849         {
3850           extrudedLinks.erase( link_isnew.first );
3851           continue; // already extruded and will no more encounter
3852         }
3853         // a _LayerEdge containg tgt2
3854         _LayerEdge* neiborEdge = edge->_2neibors->_edges[j];
3855
3856         _TmpMeshFaceOnEdge* f = new _TmpMeshFaceOnEdge( edge, neiborEdge, --_tmpFaceID );
3857         tmpFaces.push_back( f );
3858
3859         dumpCmd(SMESH_Comment("mesh.AddFace([ ")
3860                 <<f->_nn[0]->GetID()<<", "<<f->_nn[1]->GetID()<<", "
3861                 <<f->_nn[2]->GetID()<<", "<<f->_nn[3]->GetID()<<" ])");
3862       }
3863     }
3864     dumpFunctionEnd();
3865   }
3866   // Check if _LayerEdge's based on EDGE's intersects tmpFaces.
3867   // Perform two loops on _LayerEdge on EDGE's:
3868   // 1) to find and fix intersection
3869   // 2) to check that no new intersection appears as result of 1)
3870
3871   SMDS_ElemIteratorPtr fIt( new SMDS_ElementVectorIterator( tmpFaces.begin(),
3872                                                             tmpFaces.end()));
3873   auto_ptr<SMESH_ElementSearcher> searcher
3874     ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(), fIt ));
3875
3876   // 1) Find intersections
3877   double dist;
3878   const SMDS_MeshElement* face;
3879   typedef map< _LayerEdge*, set< _LayerEdge*, _LayerEdgeCmp >, _LayerEdgeCmp > TLEdge2LEdgeSet;
3880   TLEdge2LEdgeSet edge2CloseEdge;
3881
3882   const double eps = data._epsilon * data._epsilon;
3883   for ( size_t i = 0; i < data._edges.size(); ++i )
3884   {
3885     _LayerEdge* edge = data._edges[i];
3886     if (( !edge->IsOnEdge() ) &&
3887         ( edge->_sWOL.IsNull() || edge->_sWOL.ShapeType() != TopAbs_FACE ))
3888       continue;
3889     if ( edge->FindIntersection( *searcher, dist, eps, &face ))
3890     {
3891       const _TmpMeshFaceOnEdge* f = (const _TmpMeshFaceOnEdge*) face;
3892       set< _LayerEdge*, _LayerEdgeCmp > & ee = edge2CloseEdge[ edge ];
3893       ee.insert( f->_le1 );
3894       ee.insert( f->_le2 );
3895       if ( f->_le1->IsOnEdge() && f->_le1->_sWOL.IsNull() ) 
3896         edge2CloseEdge[ f->_le1 ].insert( edge );
3897       if ( f->_le2->IsOnEdge() && f->_le2->_sWOL.IsNull() ) 
3898         edge2CloseEdge[ f->_le2 ].insert( edge );
3899     }
3900   }
3901
3902   // Set _LayerEdge._normal
3903
3904   if ( !edge2CloseEdge.empty() )
3905   {
3906     dumpFunction(SMESH_Comment("updateNormals")<<data._index);
3907
3908     set< TGeomID > shapesToSmooth;
3909
3910     // vector to store new _normal and _cosin for each edge in edge2CloseEdge
3911     vector< pair< _LayerEdge*, _LayerEdge > > edge2newEdge( edge2CloseEdge.size() );
3912
3913     TLEdge2LEdgeSet::iterator e2ee = edge2CloseEdge.begin();
3914     for ( size_t iE = 0; e2ee != edge2CloseEdge.end(); ++e2ee, ++iE )
3915     {
3916       _LayerEdge* edge1 = e2ee->first;
3917       _LayerEdge* edge2 = 0;
3918       set< _LayerEdge*, _LayerEdgeCmp >& ee = e2ee->second;
3919
3920       edge2newEdge[ iE ].first = NULL;
3921
3922       // find EDGEs the edges reside
3923       // TopoDS_Edge E1, E2;
3924       // TopoDS_Shape S = helper.GetSubShapeByNode( edge1->_nodes[0], getMeshDS() );
3925       // if ( S.ShapeType() != TopAbs_EDGE )
3926       //   continue; // TODO: find EDGE by VERTEX
3927       // E1 = TopoDS::Edge( S );
3928       set< _LayerEdge*, _LayerEdgeCmp >::iterator eIt = ee.begin();
3929       for ( ; !edge2 && eIt != ee.end(); ++eIt )
3930       {
3931         if ( edge1->_sWOL == (*eIt)->_sWOL )
3932           edge2 = *eIt;
3933       }
3934       if ( !edge2 ) continue;
3935
3936       edge2newEdge[ iE ].first = edge1;
3937       _LayerEdge& newEdge = edge2newEdge[ iE ].second;
3938       // while ( E2.IsNull() && eIt != ee.end())
3939       // {
3940       //   _LayerEdge* e2 = *eIt++;
3941       //   TopoDS_Shape S = helper.GetSubShapeByNode( e2->_nodes[0], getMeshDS() );
3942       //   if ( S.ShapeType() == TopAbs_EDGE )
3943       //     E2 = TopoDS::Edge( S ), edge2 = e2;
3944       // }
3945       // if ( E2.IsNull() ) continue; // TODO: find EDGE by VERTEX
3946
3947       // find 3 FACEs sharing 2 EDGEs
3948
3949       // TopoDS_Face FF1[2], FF2[2];
3950       // PShapeIteratorPtr fIt = helper.GetAncestors(E1, *_mesh, TopAbs_FACE);
3951       // while ( fIt->more() && FF1[1].IsNull() )
3952       // {
3953       //   const TopoDS_Face *F = (const TopoDS_Face*) fIt->next();
3954       //   if ( helper.IsSubShape( *F, data._solid))
3955       //     FF1[ FF1[0].IsNull() ? 0 : 1 ] = *F;
3956       // }
3957       // fIt = helper.GetAncestors(E2, *_mesh, TopAbs_FACE);
3958       // while ( fIt->more() && FF2[1].IsNull())
3959       // {
3960       //   const TopoDS_Face *F = (const TopoDS_Face*) fIt->next();
3961       //   if ( helper.IsSubShape( *F, data._solid))
3962       //     FF2[ FF2[0].IsNull() ? 0 : 1 ] = *F;
3963       // }
3964       // // exclude a FACE common to E1 and E2 (put it to FFn[1] )
3965       // if ( FF1[0].IsSame( FF2[0]) || FF1[0].IsSame( FF2[1]))
3966       //   std::swap( FF1[0], FF1[1] );
3967       // if ( FF2[0].IsSame( FF1[0]) )
3968       //   std::swap( FF2[0], FF2[1] );
3969       // if ( FF1[0].IsNull() || FF2[0].IsNull() )
3970       //   continue;
3971
3972       // get a new normal for edge1
3973       //bool ok;
3974       gp_Vec dir1 = edge1->_normal, dir2 = edge2->_normal;
3975       // if ( edge1->_cosin < 0 )
3976       //   dir1 = getFaceDir( FF1[0], E1, edge1->_nodes[0], helper, ok ).Normalized();
3977       // if ( edge2->_cosin < 0 )
3978       //   dir2 = getFaceDir( FF2[0], E2, edge2->_nodes[0], helper, ok ).Normalized();
3979
3980       double cos1 = Abs( edge1->_cosin ), cos2 = Abs( edge2->_cosin );
3981       double wgt1 = ( cos1 + 0.001 ) / ( cos1 + cos2 + 0.002 );
3982       double wgt2 = ( cos2 + 0.001 ) / ( cos1 + cos2 + 0.002 );
3983       newEdge._normal = ( wgt1 * dir1 + wgt2 * dir2 ).XYZ();
3984       newEdge._normal.Normalize();
3985
3986       // cout << edge1->_nodes[0]->GetID() << " "
3987       //      << edge2->_nodes[0]->GetID() << " NORM: "
3988       //      << newEdge._normal.X() << ", " << newEdge._normal.Y() << ", " << newEdge._normal.Z() << endl;
3989
3990       // get new cosin
3991       if ( cos1 < theMinSmoothCosin )
3992       {
3993         newEdge._cosin = edge2->_cosin;
3994       }
3995       else if ( cos2 > theMinSmoothCosin ) // both cos1 and cos2 > theMinSmoothCosin
3996       {
3997         // gp_Vec dirInFace;
3998         // if ( edge1->_cosin < 0 )
3999         //   dirInFace = dir1;
4000         // else
4001         //   dirInFace = getFaceDir( FF1[0], E1, edge1->_nodes[0], helper, ok );
4002         // double angle = dirInFace.Angle( edge1->_normal ); // [0,PI]
4003         // edge1->SetCosin( Cos( angle ));
4004         //newEdge._cosin = 0; // ???????????
4005         newEdge._cosin = ( wgt1 * cos1 + wgt2 * cos2 ) * edge1->_cosin / cos1;
4006       }
4007       else
4008       {
4009         newEdge._cosin = edge1->_cosin;
4010       }
4011
4012       // find shapes that need smoothing due to change of _normal
4013       if ( edge1->_cosin  < theMinSmoothCosin &&
4014            newEdge._cosin > theMinSmoothCosin )
4015       {
4016         if ( edge1->_sWOL.IsNull() )
4017         {
4018           SMDS_ElemIteratorPtr fIt = edge1->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
4019           while ( fIt->more() )
4020             shapesToSmooth.insert( fIt->next()->getshapeId() );
4021           //limitStepSize( data, fIt->next(), edge1->_cosin ); // too late
4022         }
4023         else // edge1 inflates along a FACE
4024         {
4025           TopoDS_Shape V = helper.GetSubShapeByNode( edge1->_nodes[0], getMeshDS() );
4026           PShapeIteratorPtr eIt = helper.GetAncestors( V, *_mesh, TopAbs_EDGE );
4027           while ( const TopoDS_Shape* E = eIt->next() )
4028           {
4029             if ( !helper.IsSubShape( *E, /*FACE=*/edge1->_sWOL ))
4030               continue;
4031             gp_Vec edgeDir = getEdgeDir( TopoDS::Edge( *E ), TopoDS::Vertex( V ));
4032             double   angle = edgeDir.Angle( newEdge._normal ); // [0,PI]
4033             if ( angle < M_PI / 2 )
4034               shapesToSmooth.insert( getMeshDS()->ShapeToIndex( *E ));
4035           }
4036         }
4037       }
4038     }
4039
4040     data.AddShapesToSmooth( shapesToSmooth );
4041
4042     // Update data of edges depending on a new _normal
4043
4044     for ( size_t iE = 0; iE < edge2newEdge.size(); ++iE )
4045     {
4046       _LayerEdge*   edge1 = edge2newEdge[ iE ].first;
4047       _LayerEdge& newEdge = edge2newEdge[ iE ].second;
4048       if ( !edge1 ) continue;
4049
4050       edge1->_normal = newEdge._normal;
4051       edge1->SetCosin( newEdge._cosin );
4052       edge1->InvalidateStep( 1 );
4053       edge1->_len = 0;
4054       edge1->SetNewLength( data._stepSize, helper );
4055       if ( edge1->IsOnEdge() )
4056       {
4057         const SMDS_MeshNode * n1 = edge1->_2neibors->srcNode(0);
4058         const SMDS_MeshNode * n2 = edge1->_2neibors->srcNode(1);
4059         edge1->SetDataByNeighbors( n1, n2, helper );
4060       }
4061
4062       // Update normals and other dependent data of not intersecting _LayerEdge's
4063       // neighboring the intersecting ones
4064
4065       if ( !edge1->_2neibors )
4066         continue;
4067       for ( int j = 0; j < 2; ++j ) // loop on 2 neighbors
4068       {
4069         _LayerEdge* neighbor = edge1->_2neibors->_edges[j];
4070         if ( edge2CloseEdge.count ( neighbor ))
4071           continue; // j-th neighbor is also intersected
4072         _LayerEdge* prevEdge = edge1;
4073         const int nbSteps = 10;
4074         for ( int step = nbSteps; step; --step ) // step from edge1 in j-th direction
4075         {
4076           if ( !neighbor->_2neibors )
4077             break; // neighbor is on VERTEX
4078           int iNext = 0;
4079           _LayerEdge* nextEdge = neighbor->_2neibors->_edges[iNext];
4080           if ( nextEdge == prevEdge )
4081             nextEdge = neighbor->_2neibors->_edges[ ++iNext ];
4082           double r = double(step-1)/nbSteps;
4083           if ( !nextEdge->_2neibors )
4084             r = 0.5;
4085
4086           gp_XYZ newNorm = prevEdge->_normal * r + nextEdge->_normal * (1-r);
4087           newNorm.Normalize();
4088
4089           neighbor->_normal = newNorm;
4090           neighbor->SetCosin( prevEdge->_cosin * r + nextEdge->_cosin * (1-r) );
4091           neighbor->SetDataByNeighbors( prevEdge->_nodes[0], nextEdge->_nodes[0], helper );
4092
4093           neighbor->InvalidateStep( 1 );
4094           neighbor->_len = 0;
4095           neighbor->SetNewLength( data._stepSize, helper );
4096
4097           // goto the next neighbor
4098           prevEdge = neighbor;
4099           neighbor = nextEdge;
4100         }
4101       }
4102     }
4103     dumpFunctionEnd();
4104   }
4105   // 2) Check absence of intersections
4106   // TODO?
4107
4108   for ( size_t i = 0 ; i < tmpFaces.size(); ++i )
4109     delete tmpFaces[i];
4110
4111   return true;
4112 }
4113
4114 //================================================================================
4115 /*!
4116  * \brief Modify normals of _LayerEdge's on _ConvexFace's
4117  */
4118 //================================================================================
4119
4120 bool _ViscousBuilder::updateNormalsOfConvexFaces( _SolidData&         data,
4121                                                   SMESH_MesherHelper& helper,
4122                                                   int                 stepNb )
4123 {
4124   SMESHDS_Mesh* meshDS = helper.GetMeshDS();
4125   bool isOK;
4126
4127   map< TGeomID, _ConvexFace >::iterator id2face = data._convexFaces.begin();
4128   for ( ; id2face != data._convexFaces.end(); ++id2face )
4129   {
4130     _ConvexFace & convFace = (*id2face).second;
4131     if ( convFace._normalsFixed )
4132       continue; // already fixed
4133     if ( convFace.CheckPrisms() )
4134       continue; // nothing to fix
4135
4136     convFace._normalsFixed = true;
4137
4138     BRepAdaptor_Surface surface ( convFace._face, false );
4139     BRepLProp_SLProps   surfProp( surface, 2, 1e-6 );
4140
4141     // check if the convex FACE is of spherical shape
4142
4143     Bnd_B3d centersBox; // bbox of centers of curvature of _LayerEdge's on VERTEXes
4144     Bnd_B3d nodesBox;
4145     gp_Pnt  center;
4146     int     iBeg, iEnd;
4147
4148     map< TGeomID, int >::iterator id2end = convFace._subIdToEdgeEnd.begin();
4149     for ( ; id2end != convFace._subIdToEdgeEnd.end(); ++id2end )
4150     {
4151       data.GetEdgesOnShape( id2end->second, iBeg, iEnd );
4152
4153       if ( meshDS->IndexToShape( id2end->first ).ShapeType() == TopAbs_VERTEX )
4154       {
4155         _LayerEdge* ledge = data._edges[ iBeg ];
4156         if ( convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
4157           centersBox.Add( center );
4158       }
4159       for ( ; iBeg < iEnd; ++iBeg )
4160         nodesBox.Add( SMESH_TNodeXYZ( data._edges[ iBeg ]->_nodes[0] ));
4161     }
4162     if ( centersBox.IsVoid() )
4163     {
4164       debugMsg( "Error: centersBox.IsVoid()" );
4165       return false;
4166     }
4167     const bool isSpherical =
4168       ( centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
4169
4170     int nbEdges = helper.Count( convFace._face, TopAbs_EDGE, /*ignoreSame=*/false );
4171     vector < _CentralCurveOnEdge > centerCurves( nbEdges );
4172
4173     if ( isSpherical )
4174     {
4175       // set _LayerEdge::_normal as average of all normals
4176
4177       // WARNING: different density of nodes on EDGEs is not taken into account that
4178       // can lead to an improper new normal
4179
4180       gp_XYZ avgNormal( 0,0,0 );
4181       nbEdges = 0;
4182       id2end = convFace._subIdToEdgeEnd.begin();
4183       for ( ; id2end != convFace._subIdToEdgeEnd.end(); ++id2end )
4184       {
4185         data.GetEdgesOnShape( id2end->second, iBeg, iEnd );
4186         // set data of _CentralCurveOnEdge
4187         const TopoDS_Shape& S = meshDS->IndexToShape( id2end->first );
4188         if ( S.ShapeType() == TopAbs_EDGE )
4189         {
4190           _CentralCurveOnEdge& ceCurve = centerCurves[ nbEdges++ ];
4191           ceCurve.SetShapes( TopoDS::Edge(S), convFace, data, helper );
4192           if ( !data._edges[ iBeg ]->_sWOL.IsNull() )
4193             ceCurve._adjFace.Nullify();
4194           else
4195             ceCurve._ledges.insert( ceCurve._ledges.end(),
4196                                     &data._edges[ iBeg ], &data._edges[ iEnd ]);
4197         }
4198         // summarize normals
4199         for ( ; iBeg < iEnd; ++iBeg )
4200           avgNormal += data._edges[ iBeg ]->_normal;
4201       }
4202       double normSize = avgNormal.SquareModulus();
4203       if ( normSize < 1e-200 )
4204       {
4205         debugMsg( "updateNormalsOfConvexFaces(): zero avgNormal" );
4206         return false;
4207       }
4208       avgNormal /= Sqrt( normSize );
4209
4210       // compute new _LayerEdge::_cosin on EDGEs
4211       double avgCosin = 0;
4212       int     nbCosin = 0;
4213       gp_Vec inFaceDir;
4214       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
4215       {
4216         _CentralCurveOnEdge& ceCurve = centerCurves[ iE ];
4217         if ( ceCurve._adjFace.IsNull() )
4218           continue;
4219         for ( size_t iLE = 0; iLE < ceCurve._ledges.size(); ++iLE )
4220         {
4221           const SMDS_MeshNode* node = ceCurve._ledges[ iLE ]->_nodes[0];
4222           inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
4223           if ( isOK )
4224           {
4225             double angle = inFaceDir.Angle( avgNormal ); // [0,PI]
4226             ceCurve._ledges[ iLE ]->_cosin = Cos( angle );
4227             avgCosin += ceCurve._ledges[ iLE ]->_cosin;
4228             nbCosin++;
4229           }
4230         }
4231       }
4232       if ( nbCosin > 0 )
4233         avgCosin /= nbCosin;
4234
4235       // set _LayerEdge::_normal = avgNormal
4236       id2end = convFace._subIdToEdgeEnd.begin();
4237       for ( ; id2end != convFace._subIdToEdgeEnd.end(); ++id2end )
4238       {
4239         data.GetEdgesOnShape( id2end->second, iBeg, iEnd );
4240         const TopoDS_Shape& S = meshDS->IndexToShape( id2end->first );
4241         if ( S.ShapeType() != TopAbs_EDGE )
4242           for ( int i = iBeg; i < iEnd; ++i )
4243             data._edges[ i ]->_cosin = avgCosin;
4244
4245         for ( ; iBeg < iEnd; ++iBeg )
4246           data._edges[ iBeg ]->_normal = avgNormal;
4247       }
4248     }
4249     else // if ( isSpherical )
4250     {
4251       // We suppose that centers of curvature at all points of the FACE
4252       // lie on some curve, let's call it "central curve". For all _LayerEdge's
4253       // having a common center of curvature we define the same new normal
4254       // as a sum of normals of _LayerEdge's on EDGEs among them.
4255
4256       // get all centers of curvature for each EDGE
4257
4258       helper.SetSubShape( convFace._face );
4259       _LayerEdge* vertexLEdges[2], **edgeLEdge, **edgeLEdgeEnd;
4260
4261       TopExp_Explorer edgeExp( convFace._face, TopAbs_EDGE );
4262       for ( int iE = 0; edgeExp.More(); edgeExp.Next(), ++iE )
4263       {
4264         const TopoDS_Edge& edge = TopoDS::Edge( edgeExp.Current() );
4265
4266         // set adjacent FACE
4267         centerCurves[ iE ].SetShapes( edge, convFace, data, helper );
4268
4269         // get _LayerEdge's of the EDGE
4270         TGeomID edgeID = meshDS->ShapeToIndex( edge );
4271         id2end = convFace._subIdToEdgeEnd.find( edgeID );
4272         if ( id2end == convFace._subIdToEdgeEnd.end() )
4273         {
4274           // no _LayerEdge's on EDGE, use _LayerEdge's on VERTEXes
4275           for ( int iV = 0; iV < 2; ++iV )
4276           {
4277             TopoDS_Vertex v = helper.IthVertex( iV, edge );
4278             TGeomID     vID = meshDS->ShapeToIndex( v );
4279             int  end = convFace._subIdToEdgeEnd[ vID ];
4280             int iBeg = end > 0 ? data._endEdgeOnShape[ end-1 ] : 0;
4281             vertexLEdges[ iV ] = data._edges[ iBeg ];
4282           }
4283           edgeLEdge    = &vertexLEdges[0];
4284           edgeLEdgeEnd = edgeLEdge + 2;
4285
4286           centerCurves[ iE ]._adjFace.Nullify();
4287         }
4288         else
4289         {
4290           data.GetEdgesOnShape( id2end->second, iBeg, iEnd );
4291           if ( id2end->second >= data._nbShapesToSmooth )
4292             data.SortOnEdge( edge, iBeg, iEnd, helper );
4293           edgeLEdge    = &data._edges[ iBeg ];
4294           edgeLEdgeEnd = edgeLEdge + iEnd - iBeg;
4295           vertexLEdges[0] = data._edges[ iBeg   ]->_2neibors->_edges[0];
4296           vertexLEdges[1] = data._edges[ iEnd-1 ]->_2neibors->_edges[1];
4297
4298           if ( ! data._edges[ iBeg ]->_sWOL.IsNull() )
4299             centerCurves[ iE ]._adjFace.Nullify();
4300         }
4301
4302         // Get curvature centers
4303
4304         centersBox.Clear();
4305
4306         if ( edgeLEdge[0]->IsOnEdge() &&
4307              convFace.GetCenterOfCurvature( vertexLEdges[0], surfProp, helper, center ))
4308         { // 1st VERTEX
4309           centerCurves[ iE ].Append( center, vertexLEdges[0] );
4310           centersBox.Add( center );
4311         }
4312         for ( ; edgeLEdge < edgeLEdgeEnd; ++edgeLEdge )
4313           if ( convFace.GetCenterOfCurvature( *edgeLEdge, surfProp, helper, center ))
4314           { // EDGE or VERTEXes
4315             centerCurves[ iE ].Append( center, *edgeLEdge );
4316             centersBox.Add( center );
4317           }
4318         if ( edgeLEdge[-1]->IsOnEdge() &&
4319              convFace.GetCenterOfCurvature( vertexLEdges[1], surfProp, helper, center ))
4320         { // 2nd VERTEX
4321           centerCurves[ iE ].Append( center, vertexLEdges[1] );
4322           centersBox.Add( center );
4323         }
4324         centerCurves[ iE ]._isDegenerated =
4325           ( centersBox.IsVoid() || centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
4326
4327       } // loop on EDGES of convFace._face to set up data of centerCurves
4328
4329       // Compute new normals for _LayerEdge's on EDGEs
4330
4331       double avgCosin = 0;
4332       int     nbCosin = 0;
4333       gp_Vec inFaceDir;
4334       for ( size_t iE1 = 0; iE1 < centerCurves.size(); ++iE1 )
4335       {
4336         _CentralCurveOnEdge& ceCurve = centerCurves[ iE1 ];
4337         if ( ceCurve._isDegenerated )
4338           continue;
4339         const vector< gp_Pnt >& centers = ceCurve._curvaCenters;
4340         vector< gp_XYZ > &   newNormals = ceCurve._normals;
4341         for ( size_t iC1 = 0; iC1 < centers.size(); ++iC1 )
4342         {
4343           isOK = false;
4344           for ( size_t iE2 = 0; iE2 < centerCurves.size() && !isOK; ++iE2 )
4345           {
4346             if ( iE1 != iE2 )
4347               isOK = centerCurves[ iE2 ].FindNewNormal( centers[ iC1 ], newNormals[ iC1 ]);
4348           }
4349           if ( isOK && !ceCurve._adjFace.IsNull() )
4350           {
4351             // compute new _LayerEdge::_cosin
4352             const SMDS_MeshNode* node = ceCurve._ledges[ iC1 ]->_nodes[0];
4353             inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
4354             if ( isOK )
4355             {
4356               double angle = inFaceDir.Angle( newNormals[ iC1 ] ); // [0,PI]
4357               ceCurve._ledges[ iC1 ]->_cosin = Cos( angle );
4358               avgCosin += ceCurve._ledges[ iC1 ]->_cosin;
4359               nbCosin++;
4360             }
4361           }
4362         }
4363       }
4364       // set new normals to _LayerEdge's of NOT degenerated central curves
4365       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
4366       {
4367         if ( centerCurves[ iE ]._isDegenerated )
4368           continue;
4369         for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
4370           centerCurves[ iE ]._ledges[ iLE ]->_normal = centerCurves[ iE ]._normals[ iLE ];
4371       }
4372       // set new normals to _LayerEdge's of     degenerated central curves
4373       for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
4374       {
4375         if ( !centerCurves[ iE ]._isDegenerated ||
4376              centerCurves[ iE ]._ledges.size() < 3 )
4377           continue;
4378         // new normal is an average of new normals at VERTEXes that
4379         // was computed on non-degenerated _CentralCurveOnEdge's
4380         gp_XYZ newNorm = ( centerCurves[ iE ]._ledges.front()->_normal +
4381                            centerCurves[ iE ]._ledges.back ()->_normal );
4382         double sz = newNorm.Modulus();
4383         if ( sz < 1e-200 )
4384           continue;
4385         newNorm /= sz;
4386         double newCosin = ( 0.5 * centerCurves[ iE ]._ledges.front()->_cosin +
4387                             0.5 * centerCurves[ iE ]._ledges.back ()->_cosin );
4388         for ( size_t iLE = 1, nb = centerCurves[ iE ]._ledges.size() - 1; iLE < nb; ++iLE )
4389         {
4390           centerCurves[ iE ]._ledges[ iLE ]->_normal = newNorm;
4391           centerCurves[ iE ]._ledges[ iLE ]->_cosin  = newCosin;
4392         }
4393       }
4394
4395       // Find new normals for _LayerEdge's based on FACE
4396
4397       if ( nbCosin > 0 )
4398         avgCosin /= nbCosin;
4399       const TGeomID faceID = meshDS->ShapeToIndex( convFace._face );
4400       map< TGeomID, int >::iterator id2end = convFace._subIdToEdgeEnd.find( faceID );
4401       if ( id2end != convFace._subIdToEdgeEnd.end() )
4402       {
4403         int iE = 0;
4404         gp_XYZ newNorm;
4405         data.GetEdgesOnShape( id2end->second, iBeg, iEnd );
4406         for ( ; iBeg < iEnd; ++iBeg )
4407         {
4408           _LayerEdge* ledge = data._edges[ iBeg ];
4409           if ( !convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
4410             continue;
4411           for ( size_t i = 0; i < centerCurves.size(); ++i, ++iE )
4412           {
4413             iE = iE % centerCurves.size();
4414             if ( centerCurves[ iE ]._isDegenerated )
4415               continue;
4416             newNorm.SetCoord( 0,0,0 );
4417             if ( centerCurves[ iE ].FindNewNormal( center, newNorm ))
4418             {
4419               ledge->_normal = newNorm;
4420               ledge->_cosin  = avgCosin;
4421               break;
4422             }
4423           }
4424         }
4425       }
4426
4427     } // not a quasi-spherical FACE
4428
4429     // Update _LayerEdge's data according to a new normal
4430
4431     dumpFunction(SMESH_Comment("updateNormalsOfConvexFaces")<<data._index
4432                  <<"_F"<<meshDS->ShapeToIndex( convFace._face ));
4433
4434     id2end = convFace._subIdToEdgeEnd.begin();
4435     for ( ; id2end != convFace._subIdToEdgeEnd.end(); ++id2end )
4436     {
4437       data.GetEdgesOnShape( id2end->second, iBeg, iEnd );
4438       for ( ; iBeg < iEnd; ++iBeg )
4439       {
4440         _LayerEdge* & ledge = data._edges[ iBeg ];
4441         double len = ledge->_len;
4442         ledge->InvalidateStep( stepNb + 1, /*restoreLength=*/true );
4443         ledge->SetCosin( ledge->_cosin );
4444         ledge->SetNewLength( len, helper );
4445       }
4446
4447     } // loop on sub-shapes of convFace._face
4448
4449     // Find FACEs adjacent to convFace._face that got necessity to smooth
4450     // as a result of normals modification
4451
4452     set< TGeomID > adjFacesToSmooth;
4453     for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
4454     {
4455       if ( centerCurves[ iE ]._adjFace.IsNull() ||
4456            centerCurves[ iE ]._adjFaceToSmooth )
4457         continue;
4458       for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
4459       {
4460         if ( centerCurves[ iE ]._ledges[ iLE ]->_cosin > theMinSmoothCosin )
4461         {
4462           adjFacesToSmooth.insert( meshDS->ShapeToIndex( centerCurves[ iE ]._adjFace ));
4463           break;
4464         }
4465       }
4466     }
4467     data.AddShapesToSmooth( adjFacesToSmooth );
4468
4469     dumpFunctionEnd();
4470
4471
4472   } // loop on data._convexFaces
4473
4474   return true;
4475 }
4476
4477 //================================================================================
4478 /*!
4479  * \brief Finds a center of curvature of a surface at a _LayerEdge
4480  */
4481 //================================================================================
4482
4483 bool _ConvexFace::GetCenterOfCurvature( _LayerEdge*         ledge,
4484                                         BRepLProp_SLProps&  surfProp,
4485                                         SMESH_MesherHelper& helper,
4486                                         gp_Pnt &            center ) const
4487 {
4488   gp_XY uv = helper.GetNodeUV( _face, ledge->_nodes[0] );
4489   surfProp.SetParameters( uv.X(), uv.Y() );
4490   if ( !surfProp.IsCurvatureDefined() )
4491     return false;
4492
4493   const double oriFactor = ( _face.Orientation() == TopAbs_REVERSED ? +1. : -1. );
4494   double surfCurvatureMax = surfProp.MaxCurvature() * oriFactor;
4495   double surfCurvatureMin = surfProp.MinCurvature() * oriFactor;
4496   if ( surfCurvatureMin > surfCurvatureMax )
4497     center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMin * oriFactor );
4498   else
4499     center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMax * oriFactor );
4500
4501   return true;
4502 }
4503
4504 //================================================================================
4505 /*!
4506  * \brief Check that prisms are not distorted
4507  */
4508 //================================================================================
4509
4510 bool _ConvexFace::CheckPrisms() const
4511 {
4512   for ( size_t i = 0; i < _simplexTestEdges.size(); ++i )
4513   {
4514     const _LayerEdge* edge = _simplexTestEdges[i];
4515     SMESH_TNodeXYZ tgtXYZ( edge->_nodes.back() );
4516     for ( size_t j = 0; j < edge->_simplices.size(); ++j )
4517       if ( !edge->_simplices[j].IsForward( edge->_nodes[0], &tgtXYZ ))
4518       {
4519         debugMsg( "Bad simplex of _simplexTestEdges ("
4520                   << " "<< edge->_nodes[0]->GetID()<< " "<< tgtXYZ._node->GetID()
4521                   << " "<< edge->_simplices[j]._nPrev->GetID()
4522                   << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
4523         return false;
4524       }
4525   }
4526   return true;
4527 }
4528
4529 //================================================================================
4530 /*!
4531  * \brief Try to compute a new normal by interpolating normals of _LayerEdge's
4532  *        stored in this _CentralCurveOnEdge.
4533  *  \param [in] center - curvature center of a point of another _CentralCurveOnEdge.
4534  *  \param [in,out] newNormal - current normal at this point, to be redefined
4535  *  \return bool - true if succeeded.
4536  */
4537 //================================================================================
4538
4539 bool _CentralCurveOnEdge::FindNewNormal( const gp_Pnt& center, gp_XYZ& newNormal )
4540 {
4541   if ( this->_isDegenerated )
4542     return false;
4543
4544   // find two centers the given one lies between
4545
4546   for ( size_t i = 0, nb = _curvaCenters.size()-1;  i < nb;  ++i )
4547   {
4548     double sl2 = 1.001 * _segLength2[ i ];
4549
4550     double d1 = center.SquareDistance( _curvaCenters[ i ]);
4551     if ( d1 > sl2 )
4552       continue;
4553     
4554     double d2 = center.SquareDistance( _curvaCenters[ i+1 ]);
4555     if ( d2 > sl2 || d2 + d1 < 1e-100 )
4556       continue;
4557
4558     d1 = Sqrt( d1 );
4559     d2 = Sqrt( d2 );
4560     double r = d1 / ( d1 + d2 );
4561     gp_XYZ norm = (( 1. - r ) * _ledges[ i   ]->_normal +
4562                    (      r ) * _ledges[ i+1 ]->_normal );
4563     norm.Normalize();
4564
4565     newNormal += norm;
4566     double sz = newNormal.Modulus();
4567     if ( sz < 1e-200 )
4568       break;
4569     newNormal /= sz;
4570     return true;
4571   }
4572   return false;
4573 }
4574
4575 //================================================================================
4576 /*!
4577  * \brief Set shape members
4578  */
4579 //================================================================================
4580
4581 void _CentralCurveOnEdge::SetShapes( const TopoDS_Edge&  edge,
4582                                      const _ConvexFace&  convFace,
4583                                      const _SolidData&   data,
4584                                      SMESH_MesherHelper& helper)
4585 {
4586   _edge = edge;
4587
4588   PShapeIteratorPtr fIt = helper.GetAncestors( edge, *helper.GetMesh(), TopAbs_FACE );
4589   while ( const TopoDS_Shape* F = fIt->next())
4590     if ( !convFace._face.IsSame( *F ))
4591     {
4592       _adjFace = TopoDS::Face( *F );
4593       _adjFaceToSmooth = false;
4594       // _adjFace already in a smoothing queue ?
4595       size_t end;
4596       TGeomID adjFaceID = helper.GetMeshDS()->ShapeToIndex( *F );
4597       if ( data.GetShapeEdges( adjFaceID, end ))
4598         _adjFaceToSmooth = ( end < data._nbShapesToSmooth );
4599       break;
4600     }
4601 }
4602
4603 //================================================================================
4604 /*!
4605  * \brief Looks for intersection of it's last segment with faces
4606  *  \param distance - returns shortest distance from the last node to intersection
4607  */
4608 //================================================================================
4609
4610 bool _LayerEdge::FindIntersection( SMESH_ElementSearcher&   searcher,
4611                                    double &                 distance,
4612                                    const double&            epsilon,
4613                                    const SMDS_MeshElement** face)
4614 {
4615   vector< const SMDS_MeshElement* > suspectFaces;
4616   double segLen;
4617   gp_Ax1 lastSegment = LastSegment(segLen);
4618   searcher.GetElementsNearLine( lastSegment, SMDSAbs_Face, suspectFaces );
4619
4620   bool segmentIntersected = false;
4621   distance = Precision::Infinite();
4622   int iFace = -1; // intersected face
4623   for ( size_t j = 0 ; j < suspectFaces.size() /*&& !segmentIntersected*/; ++j )
4624   {
4625     const SMDS_MeshElement* face = suspectFaces[j];
4626     if ( face->GetNodeIndex( _nodes.back() ) >= 0 ||
4627          face->GetNodeIndex( _nodes[0]     ) >= 0 )
4628       continue; // face sharing _LayerEdge node
4629     const int nbNodes = face->NbCornerNodes();
4630     bool intFound = false;
4631     double dist;
4632     SMDS_MeshElement::iterator nIt = face->begin_nodes();
4633     if ( nbNodes == 3 )
4634     {
4635       intFound = SegTriaInter( lastSegment, *nIt++, *nIt++, *nIt++, dist, epsilon );
4636     }
4637     else
4638     {
4639       const SMDS_MeshNode* tria[3];
4640       tria[0] = *nIt++;
4641       tria[1] = *nIt++;
4642       for ( int n2 = 2; n2 < nbNodes && !intFound; ++n2 )
4643       {
4644         tria[2] = *nIt++;
4645         intFound = SegTriaInter(lastSegment, tria[0], tria[1], tria[2], dist, epsilon );
4646         tria[1] = tria[2];
4647       }
4648     }
4649     if ( intFound )
4650     {
4651       if ( dist < segLen*(1.01) && dist > -(_len*_lenFactor-segLen) )
4652         segmentIntersected = true;
4653       if ( distance > dist )
4654         distance = dist, iFace = j;
4655     }
4656   }
4657   if ( iFace != -1 && face ) *face = suspectFaces[iFace];
4658
4659   if ( segmentIntersected )
4660   {
4661 #ifdef __myDEBUG
4662     SMDS_MeshElement::iterator nIt = suspectFaces[iFace]->begin_nodes();
4663     gp_XYZ intP( lastSegment.Location().XYZ() + lastSegment.Direction().XYZ() * distance );
4664     cout << "nodes: tgt " << _nodes.back()->GetID() << " src " << _nodes[0]->GetID()
4665          << ", intersection with face ("
4666          << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()
4667          << ") at point (" << intP.X() << ", " << intP.Y() << ", " << intP.Z()
4668          << ") distance = " << distance - segLen<< endl;
4669 #endif
4670   }
4671
4672   distance -= segLen;
4673
4674   return segmentIntersected;
4675 }
4676
4677 //================================================================================
4678 /*!
4679  * \brief Returns size and direction of the last segment
4680  */
4681 //================================================================================
4682
4683 gp_Ax1 _LayerEdge::LastSegment(double& segLen) const
4684 {
4685   // find two non-coincident positions
4686   gp_XYZ orig = _pos.back();
4687   gp_XYZ dir;
4688   int iPrev = _pos.size() - 2;
4689   while ( iPrev >= 0 )
4690   {
4691     dir = orig - _pos[iPrev];
4692     if ( dir.SquareModulus() > 1e-100 )
4693       break;
4694     else
4695       iPrev--;
4696   }
4697
4698   // make gp_Ax1
4699   gp_Ax1 segDir;
4700   if ( iPrev < 0 )
4701   {
4702     segDir.SetLocation( SMESH_TNodeXYZ( _nodes[0] ));
4703     segDir.SetDirection( _normal );
4704     segLen = 0;
4705   }
4706   else
4707   {
4708     gp_Pnt pPrev = _pos[ iPrev ];
4709     if ( !_sWOL.IsNull() )
4710     {
4711       TopLoc_Location loc;
4712       if ( _sWOL.ShapeType() == TopAbs_EDGE )
4713       {
4714         double f,l;
4715         Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( _sWOL ), loc, f,l);
4716         pPrev = curve->Value( pPrev.X() ).Transformed( loc );
4717       }
4718       else
4719       {
4720         Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face(_sWOL), loc );
4721         pPrev = surface->Value( pPrev.X(), pPrev.Y() ).Transformed( loc );
4722       }
4723       dir = SMESH_TNodeXYZ( _nodes.back() ) - pPrev.XYZ();
4724     }
4725     segDir.SetLocation( pPrev );
4726     segDir.SetDirection( dir );
4727     segLen = dir.Modulus();
4728   }
4729
4730   return segDir;
4731 }
4732
4733 //================================================================================
4734 /*!
4735  * \brief Return the last position of the target node on a FACE. 
4736  *  \param [in] F - the FACE this _LayerEdge is inflated along
4737  *  \return gp_XY - result UV
4738  */
4739 //================================================================================
4740
4741 gp_XY _LayerEdge::LastUV( const TopoDS_Face& F ) const
4742 {
4743   if ( F.IsSame( _sWOL )) // F is my FACE
4744     return gp_XY( _pos.back().X(), _pos.back().Y() );
4745
4746   if ( _sWOL.IsNull() || _sWOL.ShapeType() != TopAbs_EDGE ) // wrong call
4747     return gp_XY( 1e100, 1e100 );
4748
4749   // _sWOL is EDGE of F; _pos.back().X() is the last U on the EDGE
4750   double f, l, u = _pos.back().X();
4751   Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface( TopoDS::Edge(_sWOL), F, f,l);
4752   if ( !C2d.IsNull() && f <= u && u <= l )
4753     return C2d->Value( u ).XY();
4754
4755   return gp_XY( 1e100, 1e100 );
4756 }
4757
4758 //================================================================================
4759 /*!
4760  * \brief Test intersection of the last segment with a given triangle
4761  *   using Moller-Trumbore algorithm
4762  * Intersection is detected if distance to intersection is less than _LayerEdge._len
4763  */
4764 //================================================================================
4765
4766 bool _LayerEdge::SegTriaInter( const gp_Ax1&        lastSegment,
4767                                const SMDS_MeshNode* n0,
4768                                const SMDS_MeshNode* n1,
4769                                const SMDS_MeshNode* n2,
4770                                double&              t,
4771                                const double&        EPSILON) const
4772 {
4773   //const double EPSILON = 1e-6;
4774
4775   gp_XYZ orig = lastSegment.Location().XYZ();
4776   gp_XYZ dir  = lastSegment.Direction().XYZ();
4777
4778   SMESH_TNodeXYZ vert0( n0 );
4779   SMESH_TNodeXYZ vert1( n1 );
4780   SMESH_TNodeXYZ vert2( n2 );
4781
4782   /* calculate distance from vert0 to ray origin */
4783   gp_XYZ tvec = orig - vert0;
4784
4785   //if ( tvec * dir > EPSILON )
4786     // intersected face is at back side of the temporary face this _LayerEdge belongs to
4787     //return false;
4788
4789   gp_XYZ edge1 = vert1 - vert0;
4790   gp_XYZ edge2 = vert2 - vert0;
4791
4792   /* begin calculating determinant - also used to calculate U parameter */
4793   gp_XYZ pvec = dir ^ edge2;
4794
4795   /* if determinant is near zero, ray lies in plane of triangle */
4796   double det = edge1 * pvec;
4797
4798   if (det > -EPSILON && det < EPSILON)
4799     return false;
4800   double inv_det = 1.0 / det;
4801
4802   /* calculate U parameter and test bounds */
4803   double u = ( tvec * pvec ) * inv_det;
4804   //if (u < 0.0 || u > 1.0)
4805   if (u < -EPSILON || u > 1.0 + EPSILON)
4806     return false;
4807
4808   /* prepare to test V parameter */
4809   gp_XYZ qvec = tvec ^ edge1;
4810
4811   /* calculate V parameter and test bounds */
4812   double v = (dir * qvec) * inv_det;
4813   //if ( v < 0.0 || u + v > 1.0 )
4814   if ( v < -EPSILON || u + v > 1.0 + EPSILON)
4815     return false;
4816
4817   /* calculate t, ray intersects triangle */
4818   t = (edge2 * qvec) * inv_det;
4819
4820   //return true;
4821   return t > 0.;
4822 }
4823
4824 //================================================================================
4825 /*!
4826  * \brief Perform smooth of _LayerEdge's based on EDGE's
4827  *  \retval bool - true if node has been moved
4828  */
4829 //================================================================================
4830
4831 bool _LayerEdge::SmoothOnEdge(Handle(Geom_Surface)& surface,
4832                               const TopoDS_Face&    F,
4833                               SMESH_MesherHelper&   helper)
4834 {
4835   ASSERT( IsOnEdge() );
4836
4837   SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _nodes.back() );
4838   SMESH_TNodeXYZ oldPos( tgtNode );
4839   double dist01, distNewOld;
4840   
4841   SMESH_TNodeXYZ p0( _2neibors->tgtNode(0));
4842   SMESH_TNodeXYZ p1( _2neibors->tgtNode(1));
4843   dist01 = p0.Distance( _2neibors->tgtNode(1) );
4844
4845   gp_Pnt newPos = p0 * _2neibors->_wgt[0] + p1 * _2neibors->_wgt[1];
4846   double lenDelta = 0;
4847   if ( _curvature )
4848   {
4849     //lenDelta = _curvature->lenDelta( _len );
4850     lenDelta = _curvature->lenDeltaByDist( dist01 );
4851     newPos.ChangeCoord() += _normal * lenDelta;
4852   }
4853
4854   distNewOld = newPos.Distance( oldPos );
4855
4856   if ( F.IsNull() )
4857   {
4858     if ( _2neibors->_plnNorm )
4859     {
4860       // put newPos on the plane defined by source node and _plnNorm
4861       gp_XYZ new2src = SMESH_TNodeXYZ( _nodes[0] ) - newPos.XYZ();
4862       double new2srcProj = (*_2neibors->_plnNorm) * new2src;
4863       newPos.ChangeCoord() += (*_2neibors->_plnNorm) * new2srcProj;
4864     }
4865     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
4866     _pos.back() = newPos.XYZ();
4867   }
4868   else
4869   {
4870     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
4871     gp_XY uv( Precision::Infinite(), 0 );
4872     helper.CheckNodeUV( F, tgtNode, uv, 1e-10, /*force=*/true );
4873     _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
4874
4875     newPos = surface->Value( uv.X(), uv.Y() );
4876     tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
4877   }
4878
4879   if ( _curvature && lenDelta < 0 )
4880   {
4881     gp_Pnt prevPos( _pos[ _pos.size()-2 ]);
4882     _len -= prevPos.Distance( oldPos );
4883     _len += prevPos.Distance( newPos );
4884   }
4885   bool moved = distNewOld > dist01/50;
4886   //if ( moved )
4887   dumpMove( tgtNode ); // debug
4888
4889   return moved;
4890 }
4891
4892 //================================================================================
4893 /*!
4894  * \brief Perform laplacian smooth in 3D of nodes inflated from FACE
4895  *  \retval bool - true if _tgtNode has been moved
4896  */
4897 //================================================================================
4898
4899 bool _LayerEdge::Smooth(int& badNb)
4900 {
4901   if ( _simplices.size() < 2 )
4902     return false; // _LayerEdge inflated along EDGE or FACE
4903
4904   // compute new position for the last _pos
4905   gp_XYZ newPos (0,0,0);
4906   for ( size_t i = 0; i < _simplices.size(); ++i )
4907     newPos += SMESH_TNodeXYZ( _simplices[i]._nPrev );
4908   newPos /= _simplices.size();
4909
4910   const gp_XYZ& curPos ( _pos.back() );
4911   const gp_Pnt  prevPos( _pos[ _pos.size()-2 ]);
4912   if ( _curvature )
4913   {
4914     double delta  = _curvature->lenDelta( _len );
4915     if ( delta > 0 )
4916       newPos += _normal * delta;
4917     else
4918     {
4919       double segLen = _normal * ( newPos - prevPos.XYZ() );
4920       if ( segLen + delta > 0 )
4921         newPos += _normal * delta;
4922     }
4923     // double segLenChange = _normal * ( curPos - newPos );
4924     // newPos += 0.5 * _normal * segLenChange;
4925   }
4926
4927   // count quality metrics (orientation) of tetras around _tgtNode
4928   int nbOkBefore = 0;
4929   for ( size_t i = 0; i < _simplices.size(); ++i )
4930     nbOkBefore += _simplices[i].IsForward( _nodes[0], &curPos );
4931
4932   int nbOkAfter = 0;
4933   for ( size_t i = 0; i < _simplices.size(); ++i )
4934     nbOkAfter += _simplices[i].IsForward( _nodes[0], &newPos );
4935
4936   if ( nbOkAfter < nbOkBefore )
4937     return false;
4938
4939   SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
4940
4941   _len -= prevPos.Distance(SMESH_TNodeXYZ( n ));
4942   _len += prevPos.Distance(newPos);
4943
4944   n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
4945   _pos.back() = newPos;
4946
4947   badNb += _simplices.size() - nbOkAfter;
4948
4949   dumpMove( n );
4950
4951   return true;
4952 }
4953
4954 //================================================================================
4955 /*!
4956  * \brief Add a new segment to _LayerEdge during inflation
4957  */
4958 //================================================================================
4959
4960 void _LayerEdge::SetNewLength( double len, SMESH_MesherHelper& helper )
4961 {
4962   if ( _len - len > -1e-6 )
4963   {
4964     //_pos.push_back( _pos.back() );
4965     return;
4966   }
4967
4968   SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
4969   SMESH_TNodeXYZ oldXYZ( n );
4970   gp_XYZ nXYZ = oldXYZ + _normal * ( len - _len ) * _lenFactor;
4971   n->setXYZ( nXYZ.X(), nXYZ.Y(), nXYZ.Z() );
4972
4973   _pos.push_back( nXYZ );
4974   _len = len;
4975   if ( !_sWOL.IsNull() )
4976   {
4977     double distXYZ[4];
4978     if ( _sWOL.ShapeType() == TopAbs_EDGE )
4979     {
4980       double u = Precision::Infinite(); // to force projection w/o distance check
4981       helper.CheckNodeU( TopoDS::Edge( _sWOL ), n, u, 1e-10, /*force=*/true, distXYZ );
4982       _pos.back().SetCoord( u, 0, 0 );
4983       if ( _nodes.size() > 1 )
4984       {
4985         SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( n->GetPosition() );
4986         pos->SetUParameter( u );
4987       }
4988     }
4989     else //  TopAbs_FACE
4990     {
4991       gp_XY uv( Precision::Infinite(), 0 );
4992       helper.CheckNodeUV( TopoDS::Face( _sWOL ), n, uv, 1e-10, /*force=*/true, distXYZ );
4993       _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
4994       if ( _nodes.size() > 1 )
4995       {
4996         SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( n->GetPosition() );
4997         pos->SetUParameter( uv.X() );
4998         pos->SetVParameter( uv.Y() );
4999       }
5000     }
5001     n->setXYZ( distXYZ[1], distXYZ[2], distXYZ[3]);
5002   }
5003   dumpMove( n ); //debug
5004 }
5005
5006 //================================================================================
5007 /*!
5008  * \brief Remove last inflation step
5009  */
5010 //================================================================================
5011
5012 void _LayerEdge::InvalidateStep( int curStep, bool restoreLength )
5013 {
5014   if ( _pos.size() > curStep )
5015   {
5016     if ( restoreLength )
5017       _len -= ( _pos[ curStep-1 ] - _pos.back() ).Modulus();
5018
5019     _pos.resize( curStep );
5020     gp_Pnt nXYZ = _pos.back();
5021     SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
5022     if ( !_sWOL.IsNull() )
5023     {
5024       TopLoc_Location loc;
5025       if ( _sWOL.ShapeType() == TopAbs_EDGE )
5026       {
5027         SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( n->GetPosition() );
5028         pos->SetUParameter( nXYZ.X() );
5029         double f,l;
5030         Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( _sWOL ), loc, f,l);
5031         nXYZ = curve->Value( nXYZ.X() ).Transformed( loc );
5032       }
5033       else
5034       {
5035         SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( n->GetPosition() );
5036         pos->SetUParameter( nXYZ.X() );
5037         pos->SetVParameter( nXYZ.Y() );
5038         Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face(_sWOL), loc );
5039         nXYZ = surface->Value( nXYZ.X(), nXYZ.Y() ).Transformed( loc );
5040       }
5041     }
5042     n->setXYZ( nXYZ.X(), nXYZ.Y(), nXYZ.Z() );
5043     dumpMove( n );
5044   }
5045 }
5046
5047 //================================================================================
5048 /*!
5049  * \brief Create layers of prisms
5050  */
5051 //================================================================================
5052
5053 bool _ViscousBuilder::refine(_SolidData& data)
5054 {
5055   SMESH_MesherHelper helper( *_mesh );
5056   helper.SetSubShape( data._solid );
5057   helper.SetElementsOnShape(false);
5058
5059   Handle(Geom_Curve) curve;
5060   Handle(Geom_Surface) surface;
5061   TopoDS_Edge geomEdge;
5062   TopoDS_Face geomFace;
5063   TopoDS_Shape prevSWOL;
5064   TopLoc_Location loc;
5065   double f,l, u;
5066   gp_XY uv;
5067   bool isOnEdge;
5068   TGeomID prevBaseId = -1;
5069   TNode2Edge* n2eMap = 0;
5070   TNode2Edge::iterator n2e;
5071
5072   // Create intermediate nodes on each _LayerEdge
5073
5074   int iS = 0, iEnd = data._endEdgeOnShape[ iS ];
5075
5076   for ( size_t i = 0; i < data._edges.size(); ++i )
5077   {
5078     _LayerEdge& edge = *data._edges[i];
5079
5080     if ( edge._nodes.size() < 2 )
5081       continue; // on _noShrinkShapes
5082
5083     // get parameters of layers for the edge
5084     if ( i == iEnd )
5085       iEnd = data._endEdgeOnShape[ ++iS ];
5086     const AverageHyp& hyp = data._hypOnShape[ iS ];
5087
5088     // get accumulated length of segments
5089     vector< double > segLen( edge._pos.size() );
5090     segLen[0] = 0.0;
5091     for ( size_t j = 1; j < edge._pos.size(); ++j )
5092       segLen[j] = segLen[j-1] + (edge._pos[j-1] - edge._pos[j] ).Modulus();
5093
5094     // allocate memory for new nodes if it is not yet refined
5095     const SMDS_MeshNode* tgtNode = edge._nodes.back();
5096     if ( edge._nodes.size() == 2 )
5097     {
5098       edge._nodes.resize( hyp.GetNumberLayers() + 1, 0 );
5099       edge._nodes[1] = 0;
5100       edge._nodes.back() = tgtNode;
5101     }
5102     // get data of a shrink shape
5103     if ( !edge._sWOL.IsNull() && edge._sWOL != prevSWOL )
5104     {
5105       isOnEdge = ( edge._sWOL.ShapeType() == TopAbs_EDGE );
5106       if ( isOnEdge )
5107       {
5108         geomEdge = TopoDS::Edge( edge._sWOL );
5109         curve    = BRep_Tool::Curve( geomEdge, loc, f,l);
5110       }
5111       else
5112       {
5113         geomFace = TopoDS::Face( edge._sWOL );
5114         surface  = BRep_Tool::Surface( geomFace, loc );
5115       }
5116       prevSWOL = edge._sWOL;
5117     }
5118     // restore shapePos of the last node by already treated _LayerEdge of another _SolidData
5119     const TGeomID baseShapeId = edge._nodes[0]->getshapeId();
5120     if ( baseShapeId != prevBaseId )
5121     {
5122       map< TGeomID, TNode2Edge* >::iterator s2ne = data._s2neMap.find( baseShapeId );
5123       n2eMap = ( s2ne == data._s2neMap.end() ) ? 0 : n2eMap = s2ne->second;
5124       prevBaseId = baseShapeId;
5125     }
5126     _LayerEdge* edgeOnSameNode = 0;
5127     if ( n2eMap && (( n2e = n2eMap->find( edge._nodes[0] )) != n2eMap->end() ))
5128     {
5129       edgeOnSameNode = n2e->second;
5130       const gp_XYZ& otherTgtPos = edgeOnSameNode->_pos.back();
5131       SMDS_PositionPtr  lastPos = tgtNode->GetPosition();
5132       if ( isOnEdge )
5133       {
5134         SMDS_EdgePosition* epos = static_cast<SMDS_EdgePosition*>( lastPos );
5135         epos->SetUParameter( otherTgtPos.X() );
5136       }
5137       else
5138       {
5139         SMDS_FacePosition* fpos = static_cast<SMDS_FacePosition*>( lastPos );
5140         fpos->SetUParameter( otherTgtPos.X() );
5141         fpos->SetVParameter( otherTgtPos.Y() );
5142       }
5143     }
5144     // calculate height of the first layer
5145     double h0;
5146     const double T = segLen.back(); //data._hyp.GetTotalThickness();
5147     const double f = hyp.GetStretchFactor();
5148     const int    N = hyp.GetNumberLayers();
5149     const double fPowN = pow( f, N );
5150     if ( fPowN - 1 <= numeric_limits<double>::min() )
5151       h0 = T / N;
5152     else
5153       h0 = T * ( f - 1 )/( fPowN - 1 );
5154
5155     const double zeroLen = std::numeric_limits<double>::min();
5156
5157     // create intermediate nodes
5158     double hSum = 0, hi = h0/f;
5159     size_t iSeg = 1;
5160     for ( size_t iStep = 1; iStep < edge._nodes.size(); ++iStep )
5161     {
5162       // compute an intermediate position
5163       hi *= f;
5164       hSum += hi;
5165       while ( hSum > segLen[iSeg] && iSeg < segLen.size()-1)
5166         ++iSeg;
5167       int iPrevSeg = iSeg-1;
5168       while ( fabs( segLen[iPrevSeg] - segLen[iSeg]) <= zeroLen && iPrevSeg > 0 )
5169         --iPrevSeg;
5170       double r = ( segLen[iSeg] - hSum ) / ( segLen[iSeg] - segLen[iPrevSeg] );
5171       gp_Pnt pos = r * edge._pos[iPrevSeg] + (1-r) * edge._pos[iSeg];
5172
5173       SMDS_MeshNode*& node = const_cast< SMDS_MeshNode*& >( edge._nodes[ iStep ]);
5174       if ( !edge._sWOL.IsNull() )
5175       {
5176         // compute XYZ by parameters <pos>
5177         if ( isOnEdge )
5178         {
5179           u = pos.X();
5180           if ( !node )
5181             pos = curve->Value( u ).Transformed(loc);
5182         }
5183         else
5184         {
5185           uv.SetCoord( pos.X(), pos.Y() );
5186           if ( !node )
5187             pos = surface->Value( pos.X(), pos.Y() ).Transformed(loc);
5188         }
5189       }
5190       // create or update the node
5191       if ( !node )
5192       {
5193         node = helper.AddNode( pos.X(), pos.Y(), pos.Z());
5194         if ( !edge._sWOL.IsNull() )
5195         {
5196           if ( isOnEdge )
5197             getMeshDS()->SetNodeOnEdge( node, geomEdge, u );
5198           else
5199             getMeshDS()->SetNodeOnFace( node, geomFace, uv.X(), uv.Y() );
5200         }
5201         else
5202         {
5203           getMeshDS()->SetNodeInVolume( node, helper.GetSubShapeID() );
5204         }
5205       }
5206       else
5207       {
5208         if ( !edge._sWOL.IsNull() )
5209         {
5210           // make average pos from new and current parameters
5211           if ( isOnEdge )
5212           {
5213             u = 0.5 * ( u + helper.GetNodeU( geomEdge, node ));
5214             pos = curve->Value( u ).Transformed(loc);
5215
5216             SMDS_EdgePosition* epos = static_cast<SMDS_EdgePosition*>( node->GetPosition() );
5217             epos->SetUParameter( u );
5218           }
5219           else
5220           {
5221             uv = 0.5 * ( uv + helper.GetNodeUV( geomFace, node ));
5222             pos = surface->Value( uv.X(), uv.Y()).Transformed(loc);
5223
5224             SMDS_FacePosition* fpos = static_cast<SMDS_FacePosition*>( node->GetPosition() );
5225             fpos->SetUParameter( uv.X() );
5226             fpos->SetVParameter( uv.Y() );
5227           }
5228         }
5229         node->setXYZ( pos.X(), pos.Y(), pos.Z() );
5230       }
5231     } // loop on edge._nodes
5232
5233     if ( !edge._sWOL.IsNull() ) // prepare for shrink()
5234     {
5235       if ( isOnEdge )
5236         edge._pos.back().SetCoord( u, 0,0);
5237       else
5238         edge._pos.back().SetCoord( uv.X(), uv.Y() ,0);
5239
5240       if ( edgeOnSameNode )
5241         edgeOnSameNode->_pos.back() = edge._pos.back();
5242     }
5243
5244   } // loop on data._edges to create nodes
5245
5246   if ( !getMeshDS()->IsEmbeddedMode() )
5247     // Log node movement
5248     for ( size_t i = 0; i < data._edges.size(); ++i )
5249     {
5250       _LayerEdge& edge = *data._edges[i];
5251       SMESH_TNodeXYZ p ( edge._nodes.back() );
5252       getMeshDS()->MoveNode( p._node, p.X(), p.Y(), p.Z() );
5253     }
5254
5255   // Create volumes
5256
5257   helper.SetElementsOnShape(true);
5258
5259   vector< vector<const SMDS_MeshNode*>* > nnVec;
5260   set< vector<const SMDS_MeshNode*>* >    nnSet;
5261   set< int > degenEdgeInd;
5262   vector<const SMDS_MeshElement*> degenVols;
5263
5264   TopExp_Explorer exp( data._solid, TopAbs_FACE );
5265   for ( ; exp.More(); exp.Next() )
5266   {
5267     const TGeomID faceID = getMeshDS()->ShapeToIndex( exp.Current() );
5268     if ( data._ignoreFaceIds.count( faceID ))
5269       continue;
5270     const bool isReversedFace = data._reversedFaceIds.count( faceID );
5271     SMESHDS_SubMesh*    fSubM = getMeshDS()->MeshElements( exp.Current() );
5272     SMDS_ElemIteratorPtr  fIt = fSubM->GetElements();
5273     while ( fIt->more() )
5274     {
5275       const SMDS_MeshElement* face = fIt->next();
5276       const int            nbNodes = face->NbCornerNodes();
5277       nnVec.resize( nbNodes );
5278       nnSet.clear();
5279       degenEdgeInd.clear();
5280       int nbZ = 0;
5281       SMDS_NodeIteratorPtr nIt = face->nodeIterator();
5282       for ( int iN = 0; iN < nbNodes; ++iN )
5283       {
5284         const SMDS_MeshNode* n = nIt->next();
5285         const int i = isReversedFace ? nbNodes-1-iN : iN;
5286         nnVec[ i ] = & data._n2eMap[ n ]->_nodes;
5287         if ( nnVec[ i ]->size() < 2 )
5288           degenEdgeInd.insert( iN );
5289         else
5290           nbZ = nnVec[ i ]->size();
5291
5292         if ( helper.HasDegeneratedEdges() )
5293           nnSet.insert( nnVec[ i ]);
5294       }
5295       if ( nbZ == 0 )
5296         continue;
5297       if ( 0 < nnSet.size() && nnSet.size() < 3 )
5298         continue;
5299
5300       switch ( nbNodes )
5301       {
5302       case 3:
5303         switch ( degenEdgeInd.size() )
5304         {
5305         case 0: // PENTA
5306         {
5307           for ( int iZ = 1; iZ < nbZ; ++iZ )
5308             helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1], (*nnVec[2])[iZ-1],
5309                               (*nnVec[0])[iZ],   (*nnVec[1])[iZ],   (*nnVec[2])[iZ]);
5310           break;
5311         }
5312         case 1: // PYRAM
5313         {
5314           int i2 = *degenEdgeInd.begin();
5315           int i0 = helper.WrapIndex( i2 - 1, nbNodes );
5316           int i1 = helper.WrapIndex( i2 + 1, nbNodes );
5317           for ( int iZ = 1; iZ < nbZ; ++iZ )
5318             helper.AddVolume( (*nnVec[i0])[iZ-1], (*nnVec[i1])[iZ-1],
5319                               (*nnVec[i1])[iZ],   (*nnVec[i0])[iZ],   (*nnVec[i2])[0]);
5320           break;
5321         }
5322         case 2: // TETRA
5323         {
5324           int i3 = !degenEdgeInd.count(0) ? 0 : !degenEdgeInd.count(1) ? 1 : 2;
5325           for ( int iZ = 1; iZ < nbZ; ++iZ )
5326             helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1], (*nnVec[2])[iZ-1],
5327                               (*nnVec[i3])[iZ]);
5328           break;
5329         }
5330         }
5331         break;
5332
5333       case 4:
5334         switch ( degenEdgeInd.size() )
5335         {
5336         case 0: // HEX
5337         {
5338           for ( int iZ = 1; iZ < nbZ; ++iZ )
5339             helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1],
5340                               (*nnVec[2])[iZ-1], (*nnVec[3])[iZ-1],
5341                               (*nnVec[0])[iZ],   (*nnVec[1])[iZ],
5342                               (*nnVec[2])[iZ],   (*nnVec[3])[iZ]);
5343           break;
5344         }
5345         case 2: // PENTA?
5346         {
5347           int i2 = *degenEdgeInd.begin();
5348           int i3 = *degenEdgeInd.rbegin();
5349           bool ok = ( i3 - i2 == 1 );
5350           if ( i2 == 0 && i3 == 3 ) { i2 = 3; i3 = 0; ok = true; }
5351           int i0 = helper.WrapIndex( i3 + 1, nbNodes );
5352           int i1 = helper.WrapIndex( i0 + 1, nbNodes );
5353           for ( int iZ = 1; iZ < nbZ; ++iZ )
5354           {
5355             const SMDS_MeshElement* vol =
5356               helper.AddVolume( (*nnVec[i3])[0], (*nnVec[i0])[iZ], (*nnVec[i0])[iZ-1],
5357                                 (*nnVec[i2])[0], (*nnVec[i1])[iZ], (*nnVec[i1])[iZ-1]);
5358             if ( !ok && vol )
5359               degenVols.push_back( vol );
5360           }
5361           break;
5362         }
5363         case 3: // degen HEX
5364         {
5365           const SMDS_MeshNode* nn[8];
5366           for ( int iZ = 1; iZ < nbZ; ++iZ )
5367           {
5368             const SMDS_MeshElement* vol =
5369               helper.AddVolume( nnVec[0]->size() > 1 ? (*nnVec[0])[iZ-1] : (*nnVec[0])[0],
5370                                 nnVec[1]->size() > 1 ? (*nnVec[1])[iZ-1] : (*nnVec[1])[0],
5371                                 nnVec[2]->size() > 1 ? (*nnVec[2])[iZ-1] : (*nnVec[2])[0],
5372                                 nnVec[3]->size() > 1 ? (*nnVec[3])[iZ-1] : (*nnVec[3])[0],
5373                                 nnVec[0]->size() > 1 ? (*nnVec[0])[iZ]   : (*nnVec[0])[0],
5374                                 nnVec[1]->size() > 1 ? (*nnVec[1])[iZ]   : (*nnVec[1])[0],
5375                                 nnVec[2]->size() > 1 ? (*nnVec[2])[iZ]   : (*nnVec[2])[0],
5376                                 nnVec[3]->size() > 1 ? (*nnVec[3])[iZ]   : (*nnVec[3])[0]);
5377             degenVols.push_back( vol );
5378           }
5379         }
5380         break;
5381         }
5382         break;
5383
5384       default:
5385         return error("Not supported type of element", data._index);
5386
5387       } // switch ( nbNodes )
5388     } // while ( fIt->more() )
5389   } // loop on FACEs
5390
5391   if ( !degenVols.empty() )
5392   {
5393     SMESH_ComputeErrorPtr& err = _mesh->GetSubMesh( data._solid )->GetComputeError();
5394     if ( !err || err->IsOK() )
5395     {
5396       err.reset( new SMESH_ComputeError( COMPERR_WARNING,
5397                                          "Degenerated volumes created" ));
5398       err->myBadElements.insert( err->myBadElements.end(),
5399                                  degenVols.begin(),degenVols.end() );
5400     }
5401   }
5402
5403   return true;
5404 }
5405
5406 //================================================================================
5407 /*!
5408  * \brief Shrink 2D mesh on faces to let space for inflated layers
5409  */
5410 //================================================================================
5411
5412 bool _ViscousBuilder::shrink()
5413 {
5414   // make map of (ids of FACEs to shrink mesh on) to (_SolidData containing _LayerEdge's
5415   // inflated along FACE or EDGE)
5416   map< TGeomID, _SolidData* > f2sdMap;
5417   for ( size_t i = 0 ; i < _sdVec.size(); ++i )
5418   {
5419     _SolidData& data = _sdVec[i];
5420     TopTools_MapOfShape FFMap;
5421     map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin();
5422     for (; s2s != data._shrinkShape2Shape.end(); ++s2s )
5423       if ( s2s->second.ShapeType() == TopAbs_FACE )
5424       {
5425         f2sdMap.insert( make_pair( getMeshDS()->ShapeToIndex( s2s->second ), &data ));
5426
5427         if ( FFMap.Add( (*s2s).second ))
5428           // Put mesh faces on the shrinked FACE to the proxy sub-mesh to avoid
5429           // usage of mesh faces made in addBoundaryElements() by the 3D algo or
5430           // by StdMeshers_QuadToTriaAdaptor
5431           if ( SMESHDS_SubMesh* smDS = getMeshDS()->MeshElements( s2s->second ))
5432           {
5433             SMESH_ProxyMesh::SubMesh* proxySub =
5434               data._proxyMesh->getFaceSubM( TopoDS::Face( s2s->second ), /*create=*/true);
5435             SMDS_ElemIteratorPtr fIt = smDS->GetElements();
5436             while ( fIt->more() )
5437               proxySub->AddElement( fIt->next() );
5438             // as a result 3D algo will use elements from proxySub and not from smDS
5439           }
5440       }
5441   }
5442
5443   SMESH_MesherHelper helper( *_mesh );
5444   helper.ToFixNodeParameters( true );
5445
5446   // EDGE's to shrink
5447   map< TGeomID, _Shrinker1D > e2shrMap;
5448   vector< _LayerEdge* > lEdges;
5449
5450   // loop on FACES to srink mesh on
5451   map< TGeomID, _SolidData* >::iterator f2sd = f2sdMap.begin();
5452   for ( ; f2sd != f2sdMap.end(); ++f2sd )
5453   {
5454     _SolidData&      data = *f2sd->second;
5455     const TopoDS_Face&  F = TopoDS::Face( getMeshDS()->IndexToShape( f2sd->first ));
5456     SMESH_subMesh*     sm = _mesh->GetSubMesh( F );
5457     SMESHDS_SubMesh* smDS = sm->GetSubMeshDS();
5458
5459     Handle(Geom_Surface) surface = BRep_Tool::Surface(F);
5460
5461     helper.SetSubShape(F);
5462
5463     // ===========================
5464     // Prepare data for shrinking
5465     // ===========================
5466
5467     // Collect nodes to smooth, as src nodes are not yet replaced by tgt ones
5468     // and thus all nodes on a FACE connected to 2d elements are to be smoothed
5469     vector < const SMDS_MeshNode* > smoothNodes;
5470     {
5471       SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
5472       while ( nIt->more() )
5473       {
5474         const SMDS_MeshNode* n = nIt->next();
5475         if ( n->NbInverseElements( SMDSAbs_Face ) > 0 )
5476           smoothNodes.push_back( n );
5477       }
5478     }
5479     // Find out face orientation
5480     double refSign = 1;
5481     const set<TGeomID> ignoreShapes;
5482     bool isOkUV;
5483     if ( !smoothNodes.empty() )
5484     {
5485       vector<_Simplex> simplices;
5486       getSimplices( smoothNodes[0], simplices, ignoreShapes );
5487       helper.GetNodeUV( F, simplices[0]._nPrev, 0, &isOkUV ); // fix UV of silpmex nodes
5488       helper.GetNodeUV( F, simplices[0]._nNext, 0, &isOkUV );
5489       gp_XY uv = helper.GetNodeUV( F, smoothNodes[0], 0, &isOkUV );
5490       if ( !simplices[0].IsForward(uv, smoothNodes[0], F, helper,refSign) )
5491         refSign = -1;
5492     }
5493
5494     // Find _LayerEdge's inflated along F
5495     lEdges.clear();
5496     {
5497       set< TGeomID > subIDs;
5498       SMESH_subMeshIteratorPtr subIt = sm->getDependsOnIterator(/*includeSelf=*/false);
5499       while ( subIt->more() )
5500         subIDs.insert( subIt->next()->GetId() );
5501
5502       int iBeg, iEnd = 0;
5503       for ( int iS = 0; iS < data._endEdgeOnShape.size() && !subIDs.empty(); ++iS )
5504       {
5505         iBeg = iEnd;
5506         iEnd = data._endEdgeOnShape[ iS ];
5507         TGeomID shapeID = data._edges[ iBeg ]->_nodes[0]->getshapeId();
5508         set< TGeomID >::iterator idIt = subIDs.find( shapeID );
5509         if ( idIt == subIDs.end() ||
5510              data._edges[ iBeg ]->_sWOL.IsNull() ) continue;
5511         subIDs.erase( idIt );
5512
5513         if ( !data._noShrinkShapes.count( shapeID ))
5514           for ( ; iBeg < iEnd; ++iBeg )
5515           {
5516             lEdges.push_back( data._edges[ iBeg ] );
5517             prepareEdgeToShrink( *data._edges[ iBeg ], F, helper, smDS );
5518           }
5519       }
5520     }
5521
5522     dumpFunction(SMESH_Comment("beforeShrinkFace")<<f2sd->first); // debug
5523     SMDS_ElemIteratorPtr fIt = smDS->GetElements();
5524     while ( fIt->more() )
5525       if ( const SMDS_MeshElement* f = fIt->next() )
5526         dumpChangeNodes( f );
5527
5528     // Replace source nodes by target nodes in mesh faces to shrink
5529     dumpFunction(SMESH_Comment("replNodesOnFace")<<f2sd->first); // debug
5530     const SMDS_MeshNode* nodes[20];
5531     for ( size_t i = 0; i < lEdges.size(); ++i )
5532     {
5533       _LayerEdge& edge = *lEdges[i];
5534       const SMDS_MeshNode* srcNode = edge._nodes[0];
5535       const SMDS_MeshNode* tgtNode = edge._nodes.back();
5536       SMDS_ElemIteratorPtr fIt = srcNode->GetInverseElementIterator(SMDSAbs_Face);
5537       while ( fIt->more() )
5538       {
5539         const SMDS_MeshElement* f = fIt->next();
5540         if ( !smDS->Contains( f ))
5541           continue;
5542         SMDS_NodeIteratorPtr nIt = f->nodeIterator();
5543         for ( int iN = 0; nIt->more(); ++iN )
5544         {
5545           const SMDS_MeshNode* n = nIt->next();
5546           nodes[iN] = ( n == srcNode ? tgtNode : n );
5547         }
5548         helper.GetMeshDS()->ChangeElementNodes( f, nodes, f->NbNodes() );
5549         dumpChangeNodes( f );
5550       }
5551     }
5552
5553     // find out if a FACE is concave
5554     const bool isConcaveFace = isConcave( F, helper );
5555
5556     // Create _SmoothNode's on face F
5557     vector< _SmoothNode > nodesToSmooth( smoothNodes.size() );
5558     {
5559       dumpFunction(SMESH_Comment("fixUVOnFace")<<f2sd->first); // debug
5560       const bool sortSimplices = isConcaveFace;
5561       for ( size_t i = 0; i < smoothNodes.size(); ++i )
5562       {
5563         const SMDS_MeshNode* n = smoothNodes[i];
5564         nodesToSmooth[ i ]._node = n;
5565         // src nodes must be replaced by tgt nodes to have tgt nodes in _simplices
5566         getSimplices( n, nodesToSmooth[ i ]._simplices, ignoreShapes, NULL, sortSimplices );
5567         // fix up incorrect uv of nodes on the FACE
5568         helper.GetNodeUV( F, n, 0, &isOkUV);
5569         dumpMove( n );
5570       }
5571     }
5572     //if ( nodesToSmooth.empty() ) continue;
5573
5574     // Find EDGE's to shrink and set simpices to LayerEdge's
5575     set< _Shrinker1D* > eShri1D;
5576     {
5577       for ( size_t i = 0; i < lEdges.size(); ++i )
5578       {
5579         _LayerEdge* edge = lEdges[i];
5580         if ( edge->_sWOL.ShapeType() == TopAbs_EDGE )
5581         {
5582           TGeomID edgeIndex = getMeshDS()->ShapeToIndex( edge->_sWOL );
5583           _Shrinker1D& srinker = e2shrMap[ edgeIndex ];
5584           eShri1D.insert( & srinker );
5585           srinker.AddEdge( edge, helper );
5586           VISCOUS_3D::ToClearSubWithMain( _mesh->GetSubMesh( edge->_sWOL ), data._solid );
5587           // restore params of nodes on EGDE if the EDGE has been already
5588           // srinked while srinking another FACE
5589           srinker.RestoreParams();
5590         }
5591         getSimplices( /*tgtNode=*/edge->_nodes.back(), edge->_simplices, ignoreShapes );
5592       }
5593     }
5594
5595     bool toFixTria = false; // to improve quality of trias by diagonal swap
5596     if ( isConcaveFace )
5597     {
5598       const bool hasTria = _mesh->NbTriangles(), hasQuad = _mesh->NbQuadrangles();
5599       if ( hasTria != hasQuad ) {
5600         toFixTria = hasTria;
5601       }
5602       else {
5603         set<int> nbNodesSet;
5604         SMDS_ElemIteratorPtr fIt = smDS->GetElements();
5605         while ( fIt->more() && nbNodesSet.size() < 2 )
5606           nbNodesSet.insert( fIt->next()->NbCornerNodes() );
5607         toFixTria = ( *nbNodesSet.begin() == 3 );
5608       }
5609     }
5610
5611     // ==================
5612     // Perform shrinking
5613     // ==================
5614
5615     bool shrinked = true;
5616     int badNb, shriStep=0, smooStep=0;
5617     _SmoothNode::SmoothType smoothType
5618       = isConcaveFace ? _SmoothNode::ANGULAR : _SmoothNode::LAPLACIAN;
5619     while ( shrinked )
5620     {
5621       shriStep++;
5622       // Move boundary nodes (actually just set new UV)
5623       // -----------------------------------------------
5624       dumpFunction(SMESH_Comment("moveBoundaryOnF")<<f2sd->first<<"_st"<<shriStep ); // debug
5625       shrinked = false;
5626       for ( size_t i = 0; i < lEdges.size(); ++i )
5627       {
5628         shrinked |= lEdges[i]->SetNewLength2d( surface,F,helper );
5629       }
5630       dumpFunctionEnd();
5631
5632       // Move nodes on EDGE's
5633       // (XYZ is set as soon as a needed length reached in SetNewLength2d())
5634       set< _Shrinker1D* >::iterator shr = eShri1D.begin();
5635       for ( ; shr != eShri1D.end(); ++shr )
5636         (*shr)->Compute( /*set3D=*/false, helper );
5637
5638       // Smoothing in 2D
5639       // -----------------
5640       int nbNoImpSteps = 0;
5641       bool       moved = true;
5642       badNb = 1;
5643       while (( nbNoImpSteps < 5 && badNb > 0) && moved)
5644       {
5645         dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
5646
5647         int oldBadNb = badNb;
5648         badNb = 0;
5649         moved = false;
5650         for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
5651         {
5652           moved |= nodesToSmooth[i].Smooth( badNb,surface,helper,refSign,
5653                                             smoothType, /*set3D=*/isConcaveFace);
5654         }
5655         if ( badNb < oldBadNb )
5656           nbNoImpSteps = 0;
5657         else
5658           nbNoImpSteps++;
5659
5660         dumpFunctionEnd();
5661       }
5662       if ( badNb > 0 )
5663         return error(SMESH_Comment("Can't shrink 2D mesh on face ") << f2sd->first );
5664       if ( shriStep > 200 )
5665         return error(SMESH_Comment("Infinite loop at shrinking 2D mesh on face ") << f2sd->first );
5666
5667       // Fix narrow triangles by swapping diagonals
5668       // ---------------------------------------
5669       if ( toFixTria )
5670       {
5671         set<const SMDS_MeshNode*> usedNodes;
5672         fixBadFaces( F, helper, /*is2D=*/true, shriStep, & usedNodes); // swap diagonals
5673
5674         // update working data
5675         set<const SMDS_MeshNode*>::iterator n;
5676         for ( size_t i = 0; i < nodesToSmooth.size() && !usedNodes.empty(); ++i )
5677         {
5678           n = usedNodes.find( nodesToSmooth[ i ]._node );
5679           if ( n != usedNodes.end())
5680           {
5681             getSimplices( nodesToSmooth[ i ]._node,
5682                           nodesToSmooth[ i ]._simplices,
5683                           ignoreShapes, NULL,
5684                           /*sortSimplices=*/ smoothType == _SmoothNode::ANGULAR );
5685             usedNodes.erase( n );
5686           }
5687         }
5688         for ( size_t i = 0; i < lEdges.size() && !usedNodes.empty(); ++i )
5689         {
5690           n = usedNodes.find( /*tgtNode=*/ lEdges[i]->_nodes.back() );
5691           if ( n != usedNodes.end())
5692           {
5693             getSimplices( lEdges[i]->_nodes.back(),
5694                           lEdges[i]->_simplices,
5695                           ignoreShapes );
5696             usedNodes.erase( n );
5697           }
5698         }
5699       }
5700       // TODO: check effect of this additional smooth
5701       // additional laplacian smooth to increase allowed shrink step
5702       // for ( int st = 1; st; --st )
5703       // {
5704       //   dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
5705       //   for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
5706       //   {
5707       //     nodesToSmooth[i].Smooth( badNb,surface,helper,refSign,
5708       //                              _SmoothNode::LAPLACIAN,/*set3D=*/false);
5709       //   }
5710       // }
5711     } // while ( shrinked )
5712
5713     // No wrongly shaped faces remain; final smooth. Set node XYZ.
5714     bool isStructuredFixed = false;
5715     if ( SMESH_2D_Algo* algo = dynamic_cast<SMESH_2D_Algo*>( sm->GetAlgo() ))
5716       isStructuredFixed = algo->FixInternalNodes( *data._proxyMesh, F );
5717     if ( !isStructuredFixed )
5718     {
5719       if ( isConcaveFace ) // fix narrow faces by swapping diagonals
5720         fixBadFaces( F, helper, /*is2D=*/false, ++shriStep );
5721
5722       for ( int st = 3; st; --st )
5723       {
5724         switch( st ) {
5725         case 1: smoothType = _SmoothNode::LAPLACIAN; break;
5726         case 2: smoothType = _SmoothNode::LAPLACIAN; break;
5727         case 3: smoothType = _SmoothNode::ANGULAR; break;
5728         }
5729         dumpFunction(SMESH_Comment("shrinkFace")<<f2sd->first<<"_st"<<++smooStep); // debug
5730         for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
5731         {
5732           nodesToSmooth[i].Smooth( badNb,surface,helper,refSign,
5733                                    smoothType,/*set3D=*/st==1 );
5734         }
5735         dumpFunctionEnd();
5736       }
5737     }
5738     // Set an event listener to clear FACE sub-mesh together with SOLID sub-mesh
5739     VISCOUS_3D::ToClearSubWithMain( sm, data._solid );
5740
5741     if ( !getMeshDS()->IsEmbeddedMode() )
5742       // Log node movement
5743       for ( size_t i = 0; i < nodesToSmooth.size(); ++i )
5744       {
5745         SMESH_TNodeXYZ p ( nodesToSmooth[i]._node );
5746         getMeshDS()->MoveNode( nodesToSmooth[i]._node, p.X(), p.Y(), p.Z() );
5747       }
5748
5749   } // loop on FACES to srink mesh on
5750
5751
5752   // Replace source nodes by target nodes in shrinked mesh edges
5753
5754   map< int, _Shrinker1D >::iterator e2shr = e2shrMap.begin();
5755   for ( ; e2shr != e2shrMap.end(); ++e2shr )
5756     e2shr->second.SwapSrcTgtNodes( getMeshDS() );
5757
5758   return true;
5759 }
5760
5761 //================================================================================
5762 /*!
5763  * \brief Computes 2d shrink direction and finds nodes limiting shrinking
5764  */
5765 //================================================================================
5766
5767 bool _ViscousBuilder::prepareEdgeToShrink( _LayerEdge&            edge,
5768                                            const TopoDS_Face&     F,
5769                                            SMESH_MesherHelper&    helper,
5770                                            const SMESHDS_SubMesh* faceSubMesh)
5771 {
5772   const SMDS_MeshNode* srcNode = edge._nodes[0];
5773   const SMDS_MeshNode* tgtNode = edge._nodes.back();
5774
5775   if ( edge._sWOL.ShapeType() == TopAbs_FACE )
5776   {
5777     gp_XY srcUV( edge._pos[0].X(), edge._pos[0].Y() );//helper.GetNodeUV( F, srcNode );
5778     gp_XY tgtUV = edge.LastUV( F );                   //helper.GetNodeUV( F, tgtNode );
5779     gp_Vec2d uvDir( srcUV, tgtUV );
5780     double uvLen = uvDir.Magnitude();
5781     uvDir /= uvLen;
5782     edge._normal.SetCoord( uvDir.X(),uvDir.Y(), 0 );
5783     edge._len = uvLen;
5784
5785     edge._pos.resize(1);
5786     edge._pos[0].SetCoord( tgtUV.X(), tgtUV.Y(), 0 );
5787
5788     // set UV of source node to target node
5789     SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( tgtNode->GetPosition() );
5790     pos->SetUParameter( srcUV.X() );
5791     pos->SetVParameter( srcUV.Y() );
5792   }
5793   else // _sWOL is TopAbs_EDGE
5794   {
5795     const TopoDS_Edge&    E = TopoDS::Edge( edge._sWOL );
5796     SMESHDS_SubMesh* edgeSM = getMeshDS()->MeshElements( E );
5797     if ( !edgeSM || edgeSM->NbElements() == 0 )
5798       return error(SMESH_Comment("Not meshed EDGE ") << getMeshDS()->ShapeToIndex( E ));
5799
5800     const SMDS_MeshNode* n2 = 0;
5801     SMDS_ElemIteratorPtr eIt = srcNode->GetInverseElementIterator(SMDSAbs_Edge);
5802     while ( eIt->more() && !n2 )
5803     {
5804       const SMDS_MeshElement* e = eIt->next();
5805       if ( !edgeSM->Contains(e)) continue;
5806       n2 = e->GetNode( 0 );
5807       if ( n2 == srcNode ) n2 = e->GetNode( 1 );
5808     }
5809     if ( !n2 )
5810       return error(SMESH_Comment("Wrongly meshed EDGE ") << getMeshDS()->ShapeToIndex( E ));
5811
5812     double uSrc = helper.GetNodeU( E, srcNode, n2 );
5813     double uTgt = helper.GetNodeU( E, tgtNode, srcNode );
5814     double u2   = helper.GetNodeU( E, n2, srcNode );
5815
5816     edge._pos.clear();
5817
5818     if ( fabs( uSrc-uTgt ) < 0.99 * fabs( uSrc-u2 ))
5819     {
5820       // tgtNode is located so that it does not make faces with wrong orientation
5821       return true;
5822     }
5823     edge._pos.resize(1);
5824     edge._pos[0].SetCoord( U_TGT, uTgt );
5825     edge._pos[0].SetCoord( U_SRC, uSrc );
5826     edge._pos[0].SetCoord( LEN_TGT, fabs( uSrc-uTgt ));
5827
5828     edge._simplices.resize( 1 );
5829     edge._simplices[0]._nPrev = n2;
5830
5831     // set U of source node to the target node
5832     SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( tgtNode->GetPosition() );
5833     pos->SetUParameter( uSrc );
5834   }
5835   return true;
5836 }
5837
5838 //================================================================================
5839 /*!
5840  * \brief Restore position of a sole node of a _LayerEdge based on _noShrinkShapes
5841  */
5842 //================================================================================
5843
5844 void _ViscousBuilder::restoreNoShrink( _LayerEdge& edge ) const
5845 {
5846   if ( edge._nodes.size() == 1 )
5847   {
5848     edge._pos.clear();
5849     edge._len = 0;
5850
5851     const SMDS_MeshNode* srcNode = edge._nodes[0];
5852     TopoDS_Shape S = SMESH_MesherHelper::GetSubShapeByNode( srcNode, getMeshDS() );
5853     if ( S.IsNull() ) return;
5854
5855     gp_Pnt p;
5856
5857     switch ( S.ShapeType() )
5858     {
5859     case TopAbs_EDGE:
5860     {
5861       double f,l;
5862       TopLoc_Location loc;
5863       Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( S ), loc, f, l );
5864       if ( curve.IsNull() ) return;
5865       SMDS_EdgePosition* ePos = static_cast<SMDS_EdgePosition*>( srcNode->GetPosition() );
5866       p = curve->Value( ePos->GetUParameter() );
5867       break;
5868     }
5869     case TopAbs_VERTEX:
5870     {
5871       p = BRep_Tool::Pnt( TopoDS::Vertex( S ));
5872       break;
5873     }
5874     default: return;
5875     }
5876     getMeshDS()->MoveNode( srcNode, p.X(), p.Y(), p.Z() );
5877     dumpMove( srcNode );
5878   }
5879 }
5880
5881 //================================================================================
5882 /*!
5883  * \brief Try to fix triangles with high aspect ratio by swaping diagonals
5884  */
5885 //================================================================================
5886
5887 void _ViscousBuilder::fixBadFaces(const TopoDS_Face&          F,
5888                                   SMESH_MesherHelper&         helper,
5889                                   const bool                  is2D,
5890                                   const int                   step,
5891                                   set<const SMDS_MeshNode*> * involvedNodes)
5892 {
5893   SMESH::Controls::AspectRatio qualifier;
5894   SMESH::Controls::TSequenceOfXYZ points(3), points1(3), points2(3);
5895   const double maxAspectRatio = is2D ? 4. : 2;
5896   _NodeCoordHelper xyz( F, helper, is2D );
5897
5898   // find bad triangles
5899
5900   vector< const SMDS_MeshElement* > badTrias;
5901   vector< double >                  badAspects;
5902   SMESHDS_SubMesh*      sm = helper.GetMeshDS()->MeshElements( F );
5903   SMDS_ElemIteratorPtr fIt = sm->GetElements();
5904   while ( fIt->more() )
5905   {
5906     const SMDS_MeshElement * f = fIt->next();
5907     if ( f->NbCornerNodes() != 3 ) continue;
5908     for ( int iP = 0; iP < 3; ++iP ) points(iP+1) = xyz( f->GetNode(iP));
5909     double aspect = qualifier.GetValue( points );
5910     if ( aspect > maxAspectRatio )
5911     {
5912       badTrias.push_back( f );
5913       badAspects.push_back( aspect );
5914     }
5915   }
5916   if ( step == 1 )
5917   {
5918     dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<<helper.GetSubShapeID());
5919     SMDS_ElemIteratorPtr fIt = sm->GetElements();
5920     while ( fIt->more() )
5921     {
5922       const SMDS_MeshElement * f = fIt->next();
5923       if ( f->NbCornerNodes() == 3 )
5924         dumpChangeNodes( f );
5925     }
5926     dumpFunctionEnd();
5927   }
5928   if ( badTrias.empty() )
5929     return;
5930
5931   // find couples of faces to swap diagonal
5932
5933   typedef pair < const SMDS_MeshElement* , const SMDS_MeshElement* > T2Trias;
5934   vector< T2Trias > triaCouples; 
5935
5936   TIDSortedElemSet involvedFaces, emptySet;
5937   for ( size_t iTia = 0; iTia < badTrias.size(); ++iTia )
5938   {
5939     T2Trias trias    [3];
5940     double  aspRatio [3];
5941     int i1, i2, i3;
5942
5943     if ( !involvedFaces.insert( badTrias[iTia] ).second )
5944       continue;
5945     for ( int iP = 0; iP < 3; ++iP )
5946       points(iP+1) = xyz( badTrias[iTia]->GetNode(iP));
5947
5948     // find triangles adjacent to badTrias[iTia] with better aspect ratio after diag-swaping
5949     int bestCouple = -1;
5950     for ( int iSide = 0; iSide < 3; ++iSide )
5951     {
5952       const SMDS_MeshNode* n1 = badTrias[iTia]->GetNode( iSide );
5953       const SMDS_MeshNode* n2 = badTrias[iTia]->GetNode(( iSide+1 ) % 3 );
5954       trias [iSide].first  = badTrias[iTia];
5955       trias [iSide].second = SMESH_MeshAlgos::FindFaceInSet( n1, n2, emptySet, involvedFaces,
5956                                                              & i1, & i2 );
5957       if (( ! trias[iSide].second ) ||
5958           ( trias[iSide].second->NbCornerNodes() != 3 ) ||
5959           ( ! sm->Contains( trias[iSide].second )))
5960         continue;
5961
5962       // aspect ratio of an adjacent tria
5963       for ( int iP = 0; iP < 3; ++iP )
5964         points2(iP+1) = xyz( trias[iSide].second->GetNode(iP));
5965       double aspectInit = qualifier.GetValue( points2 );
5966
5967       // arrange nodes as after diag-swaping
5968       if ( helper.WrapIndex( i1+1, 3 ) == i2 )
5969         i3 = helper.WrapIndex( i1-1, 3 );
5970       else
5971         i3 = helper.WrapIndex( i1+1, 3 );
5972       points1 = points;
5973       points1( 1+ iSide ) = points2( 1+ i3 );
5974       points2( 1+ i2    ) = points1( 1+ ( iSide+2 ) % 3 );
5975
5976       // aspect ratio after diag-swaping
5977       aspRatio[ iSide ] = qualifier.GetValue( points1 ) + qualifier.GetValue( points2 );
5978       if ( aspRatio[ iSide ] > aspectInit + badAspects[ iTia ] )
5979         continue;
5980
5981       // prevent inversion of a triangle
5982       gp_Vec norm1 = gp_Vec( points1(1), points1(3) ) ^ gp_Vec( points1(1), points1(2) );
5983       gp_Vec norm2 = gp_Vec( points2(1), points2(3) ) ^ gp_Vec( points2(1), points2(2) );
5984       if ( norm1 * norm2 < 0. && norm1.Angle( norm2 ) > 70./180.*M_PI )
5985         continue;
5986
5987       if ( bestCouple < 0 || aspRatio[ bestCouple ] > aspRatio[ iSide ] )
5988         bestCouple = iSide;
5989     }
5990
5991     if ( bestCouple >= 0 )
5992     {
5993       triaCouples.push_back( trias[bestCouple] );
5994       involvedFaces.insert ( trias[bestCouple].second );
5995     }
5996     else
5997     {
5998       involvedFaces.erase( badTrias[iTia] );
5999     }
6000   }
6001   if ( triaCouples.empty() )
6002     return;
6003
6004   // swap diagonals
6005
6006   SMESH_MeshEditor editor( helper.GetMesh() );
6007   dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<<helper.GetSubShapeID()<<"_"<<step);
6008   for ( size_t i = 0; i < triaCouples.size(); ++i )
6009   {
6010     dumpChangeNodes( triaCouples[i].first );
6011     dumpChangeNodes( triaCouples[i].second );
6012     editor.InverseDiag( triaCouples[i].first, triaCouples[i].second );
6013   }
6014
6015   if ( involvedNodes )
6016     for ( size_t i = 0; i < triaCouples.size(); ++i )
6017     {
6018       involvedNodes->insert( triaCouples[i].first->begin_nodes(),
6019                              triaCouples[i].first->end_nodes() );
6020       involvedNodes->insert( triaCouples[i].second->begin_nodes(),
6021                              triaCouples[i].second->end_nodes() );
6022     }
6023
6024   // just for debug dump resulting triangles
6025   dumpFunction(SMESH_Comment("swapDiagonals_F")<<helper.GetSubShapeID()<<"_"<<step);
6026   for ( size_t i = 0; i < triaCouples.size(); ++i )
6027   {
6028     dumpChangeNodes( triaCouples[i].first );
6029     dumpChangeNodes( triaCouples[i].second );
6030   }
6031 }
6032
6033 //================================================================================
6034 /*!
6035  * \brief Move target node to it's final position on the FACE during shrinking
6036  */
6037 //================================================================================
6038
6039 bool _LayerEdge::SetNewLength2d( Handle(Geom_Surface)& surface,
6040                                  const TopoDS_Face&    F,
6041                                  SMESH_MesherHelper&   helper )
6042 {
6043   if ( _pos.empty() )
6044     return false; // already at the target position
6045
6046   SMDS_MeshNode* tgtNode = const_cast< SMDS_MeshNode*& >( _nodes.back() );
6047
6048   if ( _sWOL.ShapeType() == TopAbs_FACE )
6049   {
6050     gp_XY    curUV = helper.GetNodeUV( F, tgtNode );
6051     gp_Pnt2d tgtUV( _pos[0].X(), _pos[0].Y() );
6052     gp_Vec2d uvDir( _normal.X(), _normal.Y() );
6053     const double uvLen = tgtUV.Distance( curUV );
6054     const double kSafe = Max( 0.5, 1. - 0.1 * _simplices.size() );
6055
6056     // Select shrinking step such that not to make faces with wrong orientation.
6057     double stepSize = 1e100;
6058     for ( size_t i = 0; i < _simplices.size(); ++i )
6059     {
6060       // find intersection of 2 lines: curUV-tgtUV and that connecting simplex nodes
6061       gp_XY uvN1 = helper.GetNodeUV( F, _simplices[i]._nPrev );
6062       gp_XY uvN2 = helper.GetNodeUV( F, _simplices[i]._nNext );
6063       gp_XY dirN = uvN2 - uvN1;
6064       double det = uvDir.Crossed( dirN );
6065       if ( Abs( det )  < std::numeric_limits<double>::min() ) continue;
6066       gp_XY dirN2Cur = curUV - uvN1;
6067       double step = dirN.Crossed( dirN2Cur ) / det;
6068       if ( step > 0 )
6069         stepSize = Min( step, stepSize );
6070     }
6071     gp_Pnt2d newUV;
6072     if ( uvLen <= stepSize )
6073     {
6074       newUV = tgtUV;
6075       _pos.clear();
6076     }
6077     else if ( stepSize > 0 )
6078     {
6079       newUV = curUV + uvDir.XY() * stepSize * kSafe;
6080     }
6081     else
6082     {
6083       return true;
6084     }
6085     SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( tgtNode->GetPosition() );
6086     pos->SetUParameter( newUV.X() );
6087     pos->SetVParameter( newUV.Y() );
6088
6089 #ifdef __myDEBUG
6090     gp_Pnt p = surface->Value( newUV.X(), newUV.Y() );
6091     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
6092     dumpMove( tgtNode );
6093 #endif
6094   }
6095   else // _sWOL is TopAbs_EDGE
6096   {
6097     const TopoDS_Edge&      E = TopoDS::Edge( _sWOL );
6098     const SMDS_MeshNode*   n2 = _simplices[0]._nPrev;
6099     SMDS_EdgePosition* tgtPos = static_cast<SMDS_EdgePosition*>( tgtNode->GetPosition() );
6100
6101     const double u2     = helper.GetNodeU( E, n2, tgtNode );
6102     const double uSrc   = _pos[0].Coord( U_SRC );
6103     const double lenTgt = _pos[0].Coord( LEN_TGT );
6104
6105     double newU = _pos[0].Coord( U_TGT );
6106     if ( lenTgt < 0.99 * fabs( uSrc-u2 )) // n2 got out of src-tgt range
6107     {
6108       _pos.clear();
6109     }
6110     else
6111     {
6112       newU = 0.1 * tgtPos->GetUParameter() + 0.9 * u2;
6113     }
6114     tgtPos->SetUParameter( newU );
6115 #ifdef __myDEBUG
6116     gp_XY newUV = helper.GetNodeUV( F, tgtNode, _nodes[0]);
6117     gp_Pnt p = surface->Value( newUV.X(), newUV.Y() );
6118     tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
6119     dumpMove( tgtNode );
6120 #endif
6121   }
6122   return true;
6123 }
6124
6125 //================================================================================
6126 /*!
6127  * \brief Perform smooth on the FACE
6128  *  \retval bool - true if the node has been moved
6129  */
6130 //================================================================================
6131
6132 bool _SmoothNode::Smooth(int&                  badNb,
6133                          Handle(Geom_Surface)& surface,
6134                          SMESH_MesherHelper&   helper,
6135                          const double          refSign,
6136                          SmoothType            how,
6137                          bool                  set3D)
6138 {
6139   const TopoDS_Face& face = TopoDS::Face( helper.GetSubShape() );
6140
6141   // get uv of surrounding nodes
6142   vector<gp_XY> uv( _simplices.size() );
6143   for ( size_t i = 0; i < _simplices.size(); ++i )
6144     uv[i] = helper.GetNodeUV( face, _simplices[i]._nPrev, _node );
6145
6146   // compute new UV for the node
6147   gp_XY newPos (0,0);
6148   if ( how == TFI && _simplices.size() == 4 )
6149   {
6150     gp_XY corners[4];
6151     for ( size_t i = 0; i < _simplices.size(); ++i )
6152       if ( _simplices[i]._nOpp )
6153         corners[i] = helper.GetNodeUV( face, _simplices[i]._nOpp, _node );
6154       else
6155         throw SALOME_Exception(LOCALIZED("TFI smoothing: _Simplex::_nOpp not set!"));
6156
6157     newPos = helper.calcTFI ( 0.5, 0.5,
6158                               corners[0], corners[1], corners[2], corners[3],
6159                               uv[1], uv[2], uv[3], uv[0] );
6160   }
6161   else if ( how == ANGULAR )
6162   {
6163     newPos = computeAngularPos( uv, helper.GetNodeUV( face, _node ), refSign );
6164   }
6165   else if ( how == CENTROIDAL && _simplices.size() > 3 )
6166   {
6167     // average centers of diagonals wieghted with their reciprocal lengths
6168     if ( _simplices.size() == 4 )
6169     {
6170       double w1 = 1. / ( uv[2]-uv[0] ).SquareModulus();
6171       double w2 = 1. / ( uv[3]-uv[1] ).SquareModulus();
6172       newPos = ( w1 * ( uv[2]+uv[0] ) + w2 * ( uv[3]+uv[1] )) / ( w1+w2 ) / 2;
6173     }
6174     else
6175     {
6176       double sumWeight = 0;
6177       int nb = _simplices.size() == 4 ? 2 : _simplices.size();
6178       for ( int i = 0; i < nb; ++i )
6179       {
6180         int iFrom = i + 2;
6181         int iTo   = i + _simplices.size() - 1;
6182         for ( int j = iFrom; j < iTo; ++j )
6183         {
6184           int i2 = SMESH_MesherHelper::WrapIndex( j, _simplices.size() );
6185           double w = 1. / ( uv[i]-uv[i2] ).SquareModulus();
6186           sumWeight += w;
6187           newPos += w * ( uv[i]+uv[i2] );
6188         }
6189       }
6190       newPos /= 2 * sumWeight; // 2 is to get a middle between uv's
6191     }
6192   }
6193   else
6194   {
6195     // Laplacian smooth
6196     for ( size_t i = 0; i < _simplices.size(); ++i )
6197       newPos += uv[i];
6198     newPos /= _simplices.size();
6199   }
6200
6201   // count quality metrics (orientation) of triangles around the node
6202   int nbOkBefore = 0;
6203   gp_XY tgtUV = helper.GetNodeUV( face, _node );
6204   for ( size_t i = 0; i < _simplices.size(); ++i )
6205     nbOkBefore += _simplices[i].IsForward( tgtUV, _node, face, helper, refSign );
6206
6207   int nbOkAfter = 0;
6208   for ( size_t i = 0; i < _simplices.size(); ++i )
6209     nbOkAfter += _simplices[i].IsForward( newPos, _node, face, helper, refSign );
6210
6211   if ( nbOkAfter < nbOkBefore )
6212   {
6213     badNb += _simplices.size() - nbOkBefore;
6214     return false;
6215   }
6216
6217   SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( _node->GetPosition() );
6218   pos->SetUParameter( newPos.X() );
6219   pos->SetVParameter( newPos.Y() );
6220
6221 #ifdef __myDEBUG
6222   set3D = true;
6223 #endif
6224   if ( set3D )
6225   {
6226     gp_Pnt p = surface->Value( newPos.X(), newPos.Y() );
6227     const_cast< SMDS_MeshNode* >( _node )->setXYZ( p.X(), p.Y(), p.Z() );
6228     dumpMove( _node );
6229   }
6230
6231   badNb += _simplices.size() - nbOkAfter;
6232   return ( (tgtUV-newPos).SquareModulus() > 1e-10 );
6233 }
6234
6235 //================================================================================
6236 /*!
6237  * \brief Computes new UV using angle based smoothing technic
6238  */
6239 //================================================================================
6240
6241 gp_XY _SmoothNode::computeAngularPos(vector<gp_XY>& uv,
6242                                      const gp_XY&   uvToFix,
6243                                      const double   refSign)
6244 {
6245   uv.push_back( uv.front() );
6246
6247   vector< gp_XY >  edgeDir ( uv.size() );
6248   vector< double > edgeSize( uv.size() );
6249   for ( size_t i = 1; i < edgeDir.size(); ++i )
6250   {
6251     edgeDir [i-1] = uv[i] - uv[i-1];
6252     edgeSize[i-1] = edgeDir[i-1].Modulus();
6253     if ( edgeSize[i-1] < numeric_limits<double>::min() )
6254       edgeDir[i-1].SetX( 100 );
6255     else
6256       edgeDir[i-1] /= edgeSize[i-1] * refSign;
6257   }
6258   edgeDir.back()  = edgeDir.front();
6259   edgeSize.back() = edgeSize.front();
6260
6261   gp_XY  newPos(0,0);
6262   int    nbEdges = 0;
6263   double sumSize = 0;
6264   for ( size_t i = 1; i < edgeDir.size(); ++i )
6265   {
6266     if ( edgeDir[i-1].X() > 1. ) continue;
6267     int i1 = i-1;
6268     while ( edgeDir[i].X() > 1. && ++i < edgeDir.size() );
6269     if ( i == edgeDir.size() ) break;
6270     gp_XY p = uv[i];
6271     gp_XY norm1( -edgeDir[i1].Y(), edgeDir[i1].X() );
6272     gp_XY norm2( -edgeDir[i].Y(),  edgeDir[i].X() );
6273     gp_XY bisec = norm1 + norm2;
6274     double bisecSize = bisec.Modulus();
6275     if ( bisecSize < numeric_limits<double>::min() )
6276     {
6277       bisec = -edgeDir[i1] + edgeDir[i];
6278       bisecSize = bisec.Modulus();
6279     }
6280     bisec /= bisecSize;
6281
6282     gp_XY  dirToN  = uvToFix - p;
6283     double distToN = dirToN.Modulus();
6284     if ( bisec * dirToN < 0 )
6285       distToN = -distToN;
6286
6287     newPos += ( p + bisec * distToN ) * ( edgeSize[i1] + edgeSize[i] );
6288     ++nbEdges;
6289     sumSize += edgeSize[i1] + edgeSize[i];
6290   }
6291   newPos /= /*nbEdges * */sumSize;
6292   return newPos;
6293 }
6294
6295 //================================================================================
6296 /*!
6297  * \brief Delete _SolidData
6298  */
6299 //================================================================================
6300
6301 _SolidData::~_SolidData()
6302 {
6303   for ( size_t i = 0; i < _edges.size(); ++i )
6304   {
6305     if ( _edges[i] && _edges[i]->_2neibors )
6306       delete _edges[i]->_2neibors;
6307     delete _edges[i];
6308   }
6309   _edges.clear();
6310 }
6311 //================================================================================
6312 /*!
6313  * \brief Add a _LayerEdge inflated along the EDGE
6314  */
6315 //================================================================================
6316
6317 void _Shrinker1D::AddEdge( const _LayerEdge* e, SMESH_MesherHelper& helper )
6318 {
6319   // init
6320   if ( _nodes.empty() )
6321   {
6322     _edges[0] = _edges[1] = 0;
6323     _done = false;
6324   }
6325   // check _LayerEdge
6326   if ( e == _edges[0] || e == _edges[1] )
6327     return;
6328   if ( e->_sWOL.IsNull() || e->_sWOL.ShapeType() != TopAbs_EDGE )
6329     throw SALOME_Exception(LOCALIZED("Wrong _LayerEdge is added"));
6330   if ( _edges[0] && _edges[0]->_sWOL != e->_sWOL )
6331     throw SALOME_Exception(LOCALIZED("Wrong _LayerEdge is added"));
6332
6333   // store _LayerEdge
6334   const TopoDS_Edge& E = TopoDS::Edge( e->_sWOL );
6335   double f,l;
6336   BRep_Tool::Range( E, f,l );
6337   double u = helper.GetNodeU( E, e->_nodes[0], e->_nodes.back());
6338   _edges[ u < 0.5*(f+l) ? 0 : 1 ] = e;
6339
6340   // Update _nodes
6341
6342   const SMDS_MeshNode* tgtNode0 = _edges[0] ? _edges[0]->_nodes.back() : 0;
6343   const SMDS_MeshNode* tgtNode1 = _edges[1] ? _edges[1]->_nodes.back() : 0;
6344
6345   if ( _nodes.empty() )
6346   {
6347     SMESHDS_SubMesh * eSubMesh = helper.GetMeshDS()->MeshElements( E );
6348     if ( !eSubMesh || eSubMesh->NbNodes() < 1 )
6349       return;
6350     TopLoc_Location loc;
6351     Handle(Geom_Curve) C = BRep_Tool::Curve(E, loc, f,l);
6352     GeomAdaptor_Curve aCurve(C, f,l);
6353     const double totLen = GCPnts_AbscissaPoint::Length(aCurve, f, l);
6354
6355     int nbExpectNodes = eSubMesh->NbNodes();
6356     _initU  .reserve( nbExpectNodes );
6357     _normPar.reserve( nbExpectNodes );
6358     _nodes  .reserve( nbExpectNodes );
6359     SMDS_NodeIteratorPtr nIt = eSubMesh->GetNodes();
6360     while ( nIt->more() )
6361     {
6362       const SMDS_MeshNode* node = nIt->next();
6363       if ( node->NbInverseElements(SMDSAbs_Edge) == 0 ||
6364            node == tgtNode0 || node == tgtNode1 )
6365         continue; // refinement nodes
6366       _nodes.push_back( node );
6367       _initU.push_back( helper.GetNodeU( E, node ));
6368       double len = GCPnts_AbscissaPoint::Length(aCurve, f, _initU.back());
6369       _normPar.push_back(  len / totLen );
6370     }
6371   }
6372   else
6373   {
6374     // remove target node of the _LayerEdge from _nodes
6375     int nbFound = 0;
6376     for ( size_t i = 0; i < _nodes.size(); ++i )
6377       if ( !_nodes[i] || _nodes[i] == tgtNode0 || _nodes[i] == tgtNode1 )
6378         _nodes[i] = 0, nbFound++;
6379     if ( nbFound == _nodes.size() )
6380       _nodes.clear();
6381   }
6382 }
6383
6384 //================================================================================
6385 /*!
6386  * \brief Move nodes on EDGE from ends where _LayerEdge's are inflated
6387  */
6388 //================================================================================
6389
6390 void _Shrinker1D::Compute(bool set3D, SMESH_MesherHelper& helper)
6391 {
6392   if ( _done || _nodes.empty())
6393     return;
6394   const _LayerEdge* e = _edges[0];
6395   if ( !e ) e = _edges[1];
6396   if ( !e ) return;
6397
6398   _done =  (( !_edges[0] || _edges[0]->_pos.empty() ) &&
6399             ( !_edges[1] || _edges[1]->_pos.empty() ));
6400
6401   const TopoDS_Edge& E = TopoDS::Edge( e->_sWOL );
6402   double f,l;
6403   if ( set3D || _done )
6404   {
6405     Handle(Geom_Curve) C = BRep_Tool::Curve(E, f,l);
6406     GeomAdaptor_Curve aCurve(C, f,l);
6407
6408     if ( _edges[0] )
6409       f = helper.GetNodeU( E, _edges[0]->_nodes.back(), _nodes[0] );
6410     if ( _edges[1] )
6411       l = helper.GetNodeU( E, _edges[1]->_nodes.back(), _nodes.back() );
6412     double totLen = GCPnts_AbscissaPoint::Length( aCurve, f, l );
6413
6414     for ( size_t i = 0; i < _nodes.size(); ++i )
6415     {
6416       if ( !_nodes[i] ) continue;
6417       double len = totLen * _normPar[i];
6418       GCPnts_AbscissaPoint discret( aCurve, len, f );
6419       if ( !discret.IsDone() )
6420         return throw SALOME_Exception(LOCALIZED("GCPnts_AbscissaPoint failed"));
6421       double u = discret.Parameter();
6422       SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( _nodes[i]->GetPosition() );
6423       pos->SetUParameter( u );
6424       gp_Pnt p = C->Value( u );
6425       const_cast< SMDS_MeshNode*>( _nodes[i] )->setXYZ( p.X(), p.Y(), p.Z() );
6426     }
6427   }
6428   else
6429   {
6430     BRep_Tool::Range( E, f,l );
6431     if ( _edges[0] )
6432       f = helper.GetNodeU( E, _edges[0]->_nodes.back(), _nodes[0] );
6433     if ( _edges[1] )
6434       l = helper.GetNodeU( E, _edges[1]->_nodes.back(), _nodes.back() );
6435     
6436     for ( size_t i = 0; i < _nodes.size(); ++i )
6437     {
6438       if ( !_nodes[i] ) continue;
6439       double u = f * ( 1-_normPar[i] ) + l * _normPar[i];
6440       SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( _nodes[i]->GetPosition() );
6441       pos->SetUParameter( u );
6442     }
6443   }
6444 }
6445
6446 //================================================================================
6447 /*!
6448  * \brief Restore initial parameters of nodes on EDGE
6449  */
6450 //================================================================================
6451
6452 void _Shrinker1D::RestoreParams()
6453 {
6454   if ( _done )
6455     for ( size_t i = 0; i < _nodes.size(); ++i )
6456     {
6457       if ( !_nodes[i] ) continue;
6458       SMDS_EdgePosition* pos = static_cast<SMDS_EdgePosition*>( _nodes[i]->GetPosition() );
6459       pos->SetUParameter( _initU[i] );
6460     }
6461   _done = false;
6462 }
6463
6464 //================================================================================
6465 /*!
6466  * \brief Replace source nodes by target nodes in shrinked mesh edges
6467  */
6468 //================================================================================
6469
6470 void _Shrinker1D::SwapSrcTgtNodes( SMESHDS_Mesh* mesh )
6471 {
6472   const SMDS_MeshNode* nodes[3];
6473   for ( int i = 0; i < 2; ++i )
6474   {
6475     if ( !_edges[i] ) continue;
6476
6477     SMESHDS_SubMesh * eSubMesh = mesh->MeshElements( _edges[i]->_sWOL );
6478     if ( !eSubMesh ) return;
6479     const SMDS_MeshNode* srcNode = _edges[i]->_nodes[0];
6480     const SMDS_MeshNode* tgtNode = _edges[i]->_nodes.back();
6481     SMDS_ElemIteratorPtr eIt = srcNode->GetInverseElementIterator(SMDSAbs_Edge);
6482     while ( eIt->more() )
6483     {
6484       const SMDS_MeshElement* e = eIt->next();
6485       if ( !eSubMesh->Contains( e ))
6486           continue;
6487       SMDS_ElemIteratorPtr nIt = e->nodesIterator();
6488       for ( int iN = 0; iN < e->NbNodes(); ++iN )
6489       {
6490         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
6491         nodes[iN] = ( n == srcNode ? tgtNode : n );
6492       }
6493       mesh->ChangeElementNodes( e, nodes, e->NbNodes() );
6494     }
6495   }
6496 }
6497
6498 //================================================================================
6499 /*!
6500  * \brief Creates 2D and 1D elements on boundaries of new prisms
6501  */
6502 //================================================================================
6503
6504 bool _ViscousBuilder::addBoundaryElements()
6505 {
6506   SMESH_MesherHelper helper( *_mesh );
6507
6508   vector< const SMDS_MeshNode* > faceNodes;
6509
6510   for ( size_t i = 0; i < _sdVec.size(); ++i )
6511   {
6512     _SolidData& data = _sdVec[i];
6513     TopTools_IndexedMapOfShape geomEdges;
6514     TopExp::MapShapes( data._solid, TopAbs_EDGE, geomEdges );
6515     for ( int iE = 1; iE <= geomEdges.Extent(); ++iE )
6516     {
6517       const TopoDS_Edge& E = TopoDS::Edge( geomEdges(iE));
6518       if ( data._noShrinkShapes.count( getMeshDS()->ShapeToIndex( E )))
6519         continue;
6520
6521       // Get _LayerEdge's based on E
6522
6523       map< double, const SMDS_MeshNode* > u2nodes;
6524       if ( !SMESH_Algo::GetSortedNodesOnEdge( getMeshDS(), E, /*ignoreMedium=*/false, u2nodes))
6525         continue;
6526
6527       vector< _LayerEdge* > ledges; ledges.reserve( u2nodes.size() );
6528       TNode2Edge & n2eMap = data._n2eMap;
6529       map< double, const SMDS_MeshNode* >::iterator u2n = u2nodes.begin();
6530       {
6531         //check if 2D elements are needed on E
6532         TNode2Edge::iterator n2e = n2eMap.find( u2n->second );
6533         if ( n2e == n2eMap.end() ) continue; // no layers on vertex
6534         ledges.push_back( n2e->second );
6535         u2n++;
6536         if (( n2e = n2eMap.find( u2n->second )) == n2eMap.end() )
6537           continue; // no layers on E
6538         ledges.push_back( n2eMap[ u2n->second ]);
6539
6540         const SMDS_MeshNode* tgtN0 = ledges[0]->_nodes.back();
6541         const SMDS_MeshNode* tgtN1 = ledges[1]->_nodes.back();
6542         int nbSharedPyram = 0;
6543         SMDS_ElemIteratorPtr vIt = tgtN0->GetInverseElementIterator(SMDSAbs_Volume);
6544         while ( vIt->more() )
6545         {
6546           const SMDS_MeshElement* v = vIt->next();
6547           nbSharedPyram += int( v->GetNodeIndex( tgtN1 ) >= 0 );
6548         }
6549         if ( nbSharedPyram > 1 )
6550           continue; // not free border of the pyramid
6551
6552         faceNodes.clear();
6553         faceNodes.push_back( ledges[0]->_nodes[0] );
6554         faceNodes.push_back( ledges[1]->_nodes[0] );
6555         if ( ledges[0]->_nodes.size() > 1 ) faceNodes.push_back( ledges[0]->_nodes[1] );
6556         if ( ledges[1]->_nodes.size() > 1 ) faceNodes.push_back( ledges[1]->_nodes[1] );
6557
6558         if ( getMeshDS()->FindElement( faceNodes, SMDSAbs_Face, /*noMedium=*/true))
6559           continue; // faces already created
6560       }
6561       for ( ++u2n; u2n != u2nodes.end(); ++u2n )
6562         ledges.push_back( n2eMap[ u2n->second ]);
6563
6564       // Find out orientation and type of face to create
6565
6566       bool reverse = false, isOnFace;
6567       
6568       map< TGeomID, TopoDS_Shape >::iterator e2f =
6569         data._shrinkShape2Shape.find( getMeshDS()->ShapeToIndex( E ));
6570       TopoDS_Shape F;
6571       if (( isOnFace = ( e2f != data._shrinkShape2Shape.end() )))
6572       {
6573         F = e2f->second.Oriented( TopAbs_FORWARD );
6574         reverse = ( helper.GetSubShapeOri( F, E ) == TopAbs_REVERSED );
6575         if ( helper.GetSubShapeOri( data._solid, F ) == TopAbs_REVERSED )
6576           reverse = !reverse, F.Reverse();
6577         if ( helper.IsReversedSubMesh( TopoDS::Face(F) ))
6578           reverse = !reverse;
6579       }
6580       else
6581       {
6582         // find FACE with layers sharing E
6583         PShapeIteratorPtr fIt = helper.GetAncestors( E, *_mesh, TopAbs_FACE );
6584         while ( fIt->more() && F.IsNull() )
6585         {
6586           const TopoDS_Shape* pF = fIt->next();
6587           if ( helper.IsSubShape( *pF, data._solid) &&
6588                !data._ignoreFaceIds.count( e2f->first ))
6589             F = *pF;
6590         }
6591       }
6592       // Find the sub-mesh to add new faces
6593       SMESHDS_SubMesh* sm = 0;
6594       if ( isOnFace )
6595         sm = getMeshDS()->MeshElements( F );
6596       else
6597         sm = data._proxyMesh->getFaceSubM( TopoDS::Face(F), /*create=*/true );
6598       if ( !sm )
6599         return error("error in addBoundaryElements()", data._index);
6600
6601       // Make faces
6602       const int dj1 = reverse ? 0 : 1;
6603       const int dj2 = reverse ? 1 : 0;
6604       for ( size_t j = 1; j < ledges.size(); ++j )
6605       {
6606         vector< const SMDS_MeshNode*>&  nn1 = ledges[j-dj1]->_nodes;
6607         vector< const SMDS_MeshNode*>&  nn2 = ledges[j-dj2]->_nodes;
6608         if ( nn1.size() == nn2.size() )
6609         {
6610           if ( isOnFace )
6611             for ( size_t z = 1; z < nn1.size(); ++z )
6612               sm->AddElement( getMeshDS()->AddFace( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
6613           else
6614             for ( size_t z = 1; z < nn1.size(); ++z )
6615               sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[z-1], nn2[z], nn1[z] ));
6616         }
6617         else if ( nn1.size() == 1 )
6618         {
6619           if ( isOnFace )
6620             for ( size_t z = 1; z < nn2.size(); ++z )
6621               sm->AddElement( getMeshDS()->AddFace( nn1[0], nn2[z-1], nn2[z] ));
6622           else
6623             for ( size_t z = 1; z < nn2.size(); ++z )
6624               sm->AddElement( new SMDS_FaceOfNodes( nn1[0], nn2[z-1], nn2[z] ));
6625         }
6626         else
6627         {
6628           if ( isOnFace )
6629             for ( size_t z = 1; z < nn1.size(); ++z )
6630               sm->AddElement( getMeshDS()->AddFace( nn1[z-1], nn2[0], nn1[z] ));
6631           else
6632             for ( size_t z = 1; z < nn1.size(); ++z )
6633               sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[0], nn2[z] ));
6634         }
6635       }
6636
6637       // Make edges
6638       for ( int isFirst = 0; isFirst < 2; ++isFirst )
6639       {
6640         _LayerEdge* edge = isFirst ? ledges.front() : ledges.back();
6641         if ( !edge->_sWOL.IsNull() && edge->_sWOL.ShapeType() == TopAbs_EDGE )
6642         {
6643           vector< const SMDS_MeshNode*>&  nn = edge->_nodes;
6644           if ( nn.size() < 2 || nn[1]->GetInverseElementIterator( SMDSAbs_Edge )->more() )
6645             continue;
6646           helper.SetSubShape( edge->_sWOL );
6647           helper.SetElementsOnShape( true );
6648           for ( size_t z = 1; z < nn.size(); ++z )
6649             helper.AddEdge( nn[z-1], nn[z] );
6650         }
6651       }
6652     }
6653   }
6654
6655   return true;
6656 }